Merge "Remove unnecessary code for new BugreportManager"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9d3f4f9..560459b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,8 +6,10 @@
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/idlcli/
                include/input/
+               include/powermanager/
                libs/binder/fuzzer/
                libs/binder/ndk/
+               libs/binder/tests/fuzzers/
                libs/binderthreadstate/
                libs/graphicsenv/
                libs/gui/
@@ -18,6 +20,7 @@
                opengl/libs/
                services/bufferhub/
                services/inputflinger/
+               services/powermanager/
                services/surfaceflinger/
                services/vibratorservice/
                services/vr/
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8173c89..307e21c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -53,11 +53,22 @@
         },
         {
           "include-filter": "*RelativeZTest.*"
+        },
+        {
+          "include-filter": "*RefreshRateOverlayTest.*"
         }
       ]
     },
     {
       "name": "libsurfaceflinger_unittest"
+    },
+    {
+      "name": "CtsGraphicsTestCases",
+      "options": [
+        {
+          "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+        }
+      ]
     }
   ]
 }
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 7026ca8..491c629 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -23,4 +23,6 @@
     METADATA_WINDOW_TYPE = 2,
     METADATA_TASK_ID = 3,
     METADATA_MOUSE_CURSOR = 4,
+    METADATA_ACCESSIBILITY_ID = 5,
+    METADATA_OWNER_PID = 6,
 }
diff --git a/build/phone-hdpi-512-dalvik-heap.mk b/build/phone-hdpi-512-dalvik-heap.mk
index 102c3f1..f9a12ef 100644
--- a/build/phone-hdpi-512-dalvik-heap.mk
+++ b/build/phone-hdpi-512-dalvik-heap.mk
@@ -17,10 +17,10 @@
 # Provides overrides to configure the Dalvik heap for a standard high density
 # phone with around 512MB total RAM.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=5m \
-    dalvik.vm.heapgrowthlimit=48m \
-    dalvik.vm.heapsize=128m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=5m \
+    dalvik.vm.heapgrowthlimit?=48m \
+    dalvik.vm.heapsize?=128m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=2m
diff --git a/build/phone-hdpi-dalvik-heap.mk b/build/phone-hdpi-dalvik-heap.mk
index cc0ac90..71cce74 100644
--- a/build/phone-hdpi-dalvik-heap.mk
+++ b/build/phone-hdpi-dalvik-heap.mk
@@ -16,9 +16,9 @@
 
 # Provides overrides to configure the Dalvik heap for a standard high density phone.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=5m \
-    dalvik.vm.heapsize=32m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=5m \
+    dalvik.vm.heapsize?=32m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=2m
diff --git a/build/phone-xhdpi-1024-dalvik-heap.mk b/build/phone-xhdpi-1024-dalvik-heap.mk
index 221227d..a522a7d 100644
--- a/build/phone-xhdpi-1024-dalvik-heap.mk
+++ b/build/phone-xhdpi-1024-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a xhdpi phone
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=8m \
-    dalvik.vm.heapgrowthlimit=96m \
-    dalvik.vm.heapsize=256m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=8m \
+    dalvik.vm.heapgrowthlimit?=96m \
+    dalvik.vm.heapsize?=256m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=8m
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 7ccfc13..f38d2f2 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -17,10 +17,10 @@
 # Provides overrides to configure the Dalvik heap for a 2GB phone
 # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=8m \
-    dalvik.vm.heapgrowthlimit=192m \
-    dalvik.vm.heapsize=512m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=8m \
+    dalvik.vm.heapgrowthlimit?=192m \
+    dalvik.vm.heapsize?=512m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=8m
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
index 2b84841..a6ff5a8 100644
--- a/build/phone-xhdpi-4096-dalvik-heap.mk
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a 4GB phone
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=8m \
-    dalvik.vm.heapgrowthlimit=192m \
-    dalvik.vm.heapsize=512m \
-    dalvik.vm.heaptargetutilization=0.6 \
-    dalvik.vm.heapminfree=8m \
-    dalvik.vm.heapmaxfree=16m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=8m \
+    dalvik.vm.heapgrowthlimit?=192m \
+    dalvik.vm.heapsize?=512m \
+    dalvik.vm.heaptargetutilization?=0.6 \
+    dalvik.vm.heapminfree?=8m \
+    dalvik.vm.heapmaxfree?=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
index 2bacc4a..f08830b 100644
--- a/build/phone-xhdpi-6144-dalvik-heap.mk
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a 6GB phone
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=16m \
-    dalvik.vm.heapgrowthlimit=256m \
-    dalvik.vm.heapsize=512m \
-    dalvik.vm.heaptargetutilization=0.5 \
-    dalvik.vm.heapminfree=8m \
-    dalvik.vm.heapmaxfree=32m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=16m \
+    dalvik.vm.heapgrowthlimit?=256m \
+    dalvik.vm.heapsize?=512m \
+    dalvik.vm.heaptargetutilization?=0.5 \
+    dalvik.vm.heapminfree?=8m \
+    dalvik.vm.heapmaxfree?=32m
diff --git a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
index 1721fcc..48c6ea6 100644
--- a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
+++ b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a standard tablet device.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=16m \
-    dalvik.vm.heapgrowthlimit=192m \
-    dalvik.vm.heapsize=512m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=16m \
+    dalvik.vm.heapgrowthlimit?=192m \
+    dalvik.vm.heapsize?=512m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-7in-hdpi-1024-dalvik-heap.mk b/build/tablet-7in-hdpi-1024-dalvik-heap.mk
index 7fd34b5..d0027dc 100644
--- a/build/tablet-7in-hdpi-1024-dalvik-heap.mk
+++ b/build/tablet-7in-hdpi-1024-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a standard tablet device.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=8m \
-    dalvik.vm.heapgrowthlimit=80m \
-    dalvik.vm.heapsize=384m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=8m \
+    dalvik.vm.heapgrowthlimit?=80m \
+    dalvik.vm.heapsize?=384m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
index e0f20c1..7c06b4b 100644
--- a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
+++ b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a 320dpi 7" tablet device.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=16m \
-    dalvik.vm.heapgrowthlimit=192m \
-    dalvik.vm.heapsize=512m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=16m \
+    dalvik.vm.heapgrowthlimit?=192m \
+    dalvik.vm.heapsize?=512m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-dalvik-heap.mk b/build/tablet-dalvik-heap.mk
index f577fb8..1688665 100644
--- a/build/tablet-dalvik-heap.mk
+++ b/build/tablet-dalvik-heap.mk
@@ -16,10 +16,10 @@
 
 # Provides overrides to configure the Dalvik heap for a standard tablet device.
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    dalvik.vm.heapstartsize=5m \
-    dalvik.vm.heapgrowthlimit=48m \
-    dalvik.vm.heapsize=256m \
-    dalvik.vm.heaptargetutilization=0.75 \
-    dalvik.vm.heapminfree=512k \
-    dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+    dalvik.vm.heapstartsize?=5m \
+    dalvik.vm.heapgrowthlimit?=48m \
+    dalvik.vm.heapsize?=256m \
+    dalvik.vm.heaptargetutilization?=0.75 \
+    dalvik.vm.heapminfree?=512k \
+    dalvik.vm.heapmaxfree?=2m
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 28fdaa4..3184843 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,7 +99,9 @@
 
 /* Tracing categories */
 static const TracingCategory k_categories[] = {
-    { "gfx",        "Graphics",                 ATRACE_TAG_GRAPHICS, { } },
+    { "gfx",        "Graphics",                 ATRACE_TAG_GRAPHICS, {
+        { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
+    } },
     { "input",      "Input",                    ATRACE_TAG_INPUT, { } },
     { "view",       "View System",              ATRACE_TAG_VIEW, { } },
     { "webview",    "WebView",                  ATRACE_TAG_WEBVIEW, { } },
@@ -124,6 +126,7 @@
     { "aidl",       "AIDL calls",               ATRACE_TAG_AIDL, { } },
     { "nnapi",      "NNAPI",                    ATRACE_TAG_NNAPI, { } },
     { "rro",        "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
+    { "sysprop",    "System Property",          ATRACE_TAG_SYSPROP, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { k_pdxServiceCategory, "PDX services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
@@ -172,6 +175,8 @@
         { OPT,      "events/clk/clk_enable/enable" },
         { OPT,      "events/power/cpu_frequency_limits/enable" },
         { OPT,      "events/power/suspend_resume/enable" },
+        { OPT,      "events/cpuhp/cpuhp_enter/enable" },
+        { OPT,      "events/cpuhp/cpuhp_exit/enable" },
     } },
     { "membus",     "Memory Bus Utilization", 0, {
         { REQ,      "events/memory_bus/enable" },
@@ -238,6 +243,11 @@
         { OPT,      "events/kmem/ion_heap_grow/enable" },
         { OPT,      "events/kmem/ion_heap_shrink/enable" },
         { OPT,      "events/ion/ion_stat/enable" },
+        { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
+    } },
+    { "thermal",  "Thermal event", 0, {
+        { REQ,      "events/thermal/thermal_temperature/enable" },
+        { OPT,      "events/thermal/cdev_update/enable" },
     } },
 };
 
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 9b2f4a8..d95d04a 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -47,6 +47,10 @@
     chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
     chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_enter/enable
+    chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
@@ -125,6 +129,36 @@
     chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
     chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
     chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable
+    chmod 0666 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_entry/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_entry/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/softirq_entry/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/softirq_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_raise/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/softirq_raise/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_entry/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/tasklet_entry/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/tasklet_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_entry/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_entry/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable
+    chmod 0666 /sys/kernel/tracing/events/ipi/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_entry/enable
+    chmod 0666 /sys/kernel/tracing/events/ipi/ipi_entry/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/ipi/ipi_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_raise/enable
+    chmod 0666 /sys/kernel/tracing/events/ipi/ipi_raise/enable
 
     # disk
     chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
@@ -164,6 +198,12 @@
     chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
     chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
 
+    # thermal
+    chmod 0666 /sys/kernel/debug/tracing/events/thermal/thermal_temperature/enable
+    chmod 0666 /sys/kernel/tracing/events/thermal/thermal_temperature/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable
+    chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable
+
 # Tracing disabled by default
     write /sys/kernel/debug/tracing/tracing_on 0
     write /sys/kernel/tracing/tracing_on 0
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index 840ae47..e5c52d8 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -27,12 +27,20 @@
 // dumpstate, then connect to the dumpstate local client to read the
 // output. All of the dumpstate output is written to stdout, including
 // any errors encountered while reading/writing the output.
-int main() {
-
+int main(int argc, char* /*argv*/[]) {
   fprintf(stderr, "=============================================================================\n");
-  fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
+  fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
+  fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
+  fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
+  fprintf(stderr, "WARNING: On the device use: bugreportz\n");
+  fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
   fprintf(stderr, "=============================================================================\n\n\n");
 
+  if (argc != 1) {
+    fprintf(stderr, "usage: bugreport\n");
+    exit(1);
+  }
+
   // Start the dumpstate service.
   property_set("ctl.start", "dumpstate");
 
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 40346be..1d48e08 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -30,7 +30,7 @@
 
 static void show_usage() {
     fprintf(stderr,
-            "usage: bugreportz [-h | -v]\n"
+            "usage: bugreportz [-hpv]\n"
             "  -h: to display this help message\n"
             "  -p: display progress\n"
             "  -v: to display the version\n"
@@ -64,6 +64,12 @@
         }
     }
 
+    // We don't support any non-option arguments.
+    if (optind != argc) {
+        show_usage();
+        return EXIT_FAILURE;
+    }
+
     // TODO: code below was copy-and-pasted from bugreport.cpp (except by the
     // timeout value);
     // should be reused instead.
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index acca11a..80d14ac 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,34 +105,27 @@
     name: "dumpstate",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.cpp",
+        "TaskQueue.cpp",
         "dumpstate.cpp",
         "main.cpp",
     ],
     required: [
         "atrace",
-        "df",
-        "getprop",
         "ip",
         "iptables",
-        "ip6tables",
-        "kill",
         "librank",
         "logcat",
         "lpdump",
         "lpdumpd",
-        "lsmod",
-        "lsof",
-        "netstat",
-        "printenv",
         "procrank",
         "screencap",
         "showmap",
         "ss",
         "storaged",
-        "top",
-        "uptime",
+        "toolbox",
+        "toybox",
         "vdc",
-        "vril-dump",
     ],
     init_rc: ["dumpstate.rc"],
 }
@@ -141,6 +134,8 @@
     name: "dumpstate_test",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.cpp",
+        "TaskQueue.cpp",
         "dumpstate.cpp",
         "tests/dumpstate_test.cpp",
     ],
@@ -157,10 +152,14 @@
     name: "dumpstate_smoke_test",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.cpp",
+        "TaskQueue.cpp",
         "dumpstate.cpp",
         "tests/dumpstate_smoke_test.cpp",
     ],
     static_libs: ["libgmock"],
+    test_config: "dumpstate_smoke_test.xml",
+    test_suites: ["device-tests"],
 }
 
 
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
new file mode 100644
index 0000000..e15ac3f
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpPool.h"
+
+#include <array>
+#include <thread>
+
+#include <log/log.h>
+
+#include "dumpstate.h"
+#include "DumpstateInternal.h"
+#include "DumpstateUtil.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+        log_duration_(true) {
+    assert(!tmp_root.empty());
+    deleteTempFiles(tmp_root_);
+}
+
+DumpPool::~DumpPool() {
+    shutdown();
+}
+
+void DumpPool::start(int thread_counts) {
+    assert(thread_counts > 0);
+    assert(threads_.empty());
+    if (thread_counts > MAX_THREAD_COUNT) {
+        thread_counts = MAX_THREAD_COUNT;
+    }
+    MYLOGI("Start thread pool:%d", thread_counts);
+    shutdown_ = false;
+    for (int i = 0; i < thread_counts; i++) {
+        threads_.emplace_back(std::thread([=]() {
+            setThreadName(pthread_self(), i + 1);
+            loop();
+        }));
+    }
+}
+
+void DumpPool::shutdown() {
+    std::unique_lock lock(lock_);
+    if (shutdown_ || threads_.empty()) {
+        return;
+    }
+    while (!tasks_.empty()) tasks_.pop();
+    futures_map_.clear();
+
+    shutdown_ = true;
+    condition_variable_.notify_all();
+    lock.unlock();
+
+    for (auto& thread : threads_) {
+        thread.join();
+    }
+    threads_.clear();
+    deleteTempFiles(tmp_root_);
+    MYLOGI("shutdown thread pool");
+}
+
+void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
+        int out_fd) {
+    DurationReporter duration_reporter("Wait for " + task_name, true);
+    auto iterator = futures_map_.find(task_name);
+    if (iterator == futures_map_.end()) {
+        MYLOGW("Task %s does not exist", task_name.c_str());
+        return;
+    }
+    Future future = iterator->second;
+    futures_map_.erase(iterator);
+
+    std::string result = future.get();
+    if (result.empty()) {
+        return;
+    }
+    DumpFileToFd(out_fd, title, result);
+    if (unlink(result.c_str())) {
+        MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+    }
+}
+
+void DumpPool::deleteTempFiles() {
+    deleteTempFiles(tmp_root_);
+}
+
+void DumpPool::setLogDuration(bool log_duration) {
+    log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+        const std::string& duration_title, int out_fd) {
+    DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+            /*verbose =*/false, out_fd);
+    std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+        const std::string& duration_title, int out_fd) {
+    DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+            /*verbose =*/false, out_fd);
+    std::invoke(dump_func, out_fd);
+}
+
+std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
+    auto tmp_file_ptr = std::make_unique<TmpFile>();
+    std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
+    snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
+             tmp_root_.c_str());
+    tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
+            mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
+    if (tmp_file_ptr->fd.get() == -1) {
+        MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
+        tmp_file_ptr = nullptr;
+        return tmp_file_ptr;
+    }
+    return tmp_file_ptr;
+}
+
+void DumpPool::deleteTempFiles(const std::string& folder) {
+    std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+            &closedir);
+    if (!dir_ptr) {
+        MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
+        return;
+    }
+    int dir_fd = dirfd(dir_ptr.get());
+    if (dir_fd < 0) {
+        MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
+               strerror(errno));
+        return;
+    }
+
+    struct dirent* de;
+    while ((de = readdir(dir_ptr.get()))) {
+        if (de->d_type != DT_REG) {
+            continue;
+        }
+        std::string file_name(de->d_name);
+        if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
+            continue;
+        }
+        if (unlinkat(dir_fd, file_name.c_str(), 0)) {
+            MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
+                   strerror(errno));
+        }
+    }
+}
+
+void DumpPool::setThreadName(const pthread_t thread, int id) {
+    std::array<char, 15> name;
+    snprintf(name.data(), name.size(), "dumpstate_%d", id);
+    pthread_setname_np(thread, name.data());
+}
+
+void DumpPool::loop() {
+    std::unique_lock lock(lock_);
+    while (!shutdown_) {
+        if (tasks_.empty()) {
+            condition_variable_.wait(lock);
+            continue;
+        } else {
+            std::packaged_task<std::string()> task = std::move(tasks_.front());
+            tasks_.pop();
+            lock.unlock();
+            std::invoke(task);
+            lock.lock();
+        }
+    }
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
new file mode 100644
index 0000000..0c3c2cc
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+
+#include <future>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpPoolTest;
+
+/*
+ * A thread pool with the fixed number of threads to execute multiple dump tasks
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
+ *
+ * void DumpFoo(int out_fd) {
+ *     dprintf(out_fd, "Dump result to out_fd ...");
+ * }
+ * ...
+ * DumpPool pool(tmp_root);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * ...
+ * pool.waitForTask("TaskName");
+ *
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ */
+class DumpPool {
+  friend class android::os::dumpstate::DumpPoolTest;
+
+  public:
+    /*
+     * Creates a thread pool.
+     *
+     * |tmp_root| A path to a temporary folder for threads to create temporary
+     * files.
+     */
+    explicit DumpPool(const std::string& tmp_root);
+    ~DumpPool();
+
+    /*
+     * Starts the threads in the pool.
+     *
+     * |thread_counts| the number of threads to start.
+     */
+    void start(int thread_counts = MAX_THREAD_COUNT);
+
+    /*
+     * Requests to shutdown the pool and waits until all threads exit the loop.
+     */
+    void shutdown();
+
+    /*
+     * Adds a task into the queue of the thread pool.
+     *
+     * |task_name| The name of the task. It's also the title of the
+     * DurationReporter log.
+     * |f| Callable function to execute the task.
+     * |args| A list of arguments.
+     *
+     * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
+     */
+    template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+            Args&&... args) {
+        std::function<void(void)> func = std::bind(std::forward<F>(f),
+                std::forward<Args>(args)...);
+        futures_map_[task_name] = post(task_name, func);
+        if (threads_.empty()) {
+            start();
+        }
+    }
+
+    /*
+     * Adds a task into the queue of the thread pool. The task takes a file
+     * descriptor as a parameter to redirect dump results to a temporary file.
+     *
+     * |task_name| The name of the task. It's also the title of the
+     * DurationReporter log.
+     * |f| Callable function to execute the task.
+     * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+     * argument needs to be included here.
+     */
+    template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+            Args&&... args) {
+        std::function<void(int)> func = std::bind(std::forward<F>(f),
+                std::forward<Args>(args)...);
+        futures_map_[task_name] = post(task_name, func);
+        if (threads_.empty()) {
+            start();
+        }
+    }
+
+    /*
+     * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+     */
+    void waitForTask(const std::string& task_name) {
+        waitForTask(task_name, "", STDOUT_FILENO);
+    }
+
+    /*
+     * Waits until the task is finished. Dumps the task results to the specified
+     * out_fd.
+     *
+     * |task_name| The name of the task.
+     * |title| Dump title string to the out_fd, an empty string for nothing.
+     * |out_fd| The target file to dump the result from the task.
+     */
+    void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+
+    /*
+     * Deletes temporary files created by DumpPool.
+     */
+    void deleteTempFiles();
+
+    static const std::string PREFIX_TMPFILE_NAME;
+
+  private:
+    using Task = std::packaged_task<std::string()>;
+    using Future = std::shared_future<std::string>;
+
+    template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+    template<class T> Future post(const std::string& task_name, T dump_func) {
+        Task packaged_task([=]() {
+            std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
+            if (!tmp_file_ptr) {
+                return std::string("");
+            }
+            invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+            fsync(tmp_file_ptr->fd.get());
+            return std::string(tmp_file_ptr->path);
+        });
+        std::unique_lock lock(lock_);
+        auto future = packaged_task.get_future().share();
+        tasks_.push(std::move(packaged_task));
+        condition_variable_.notify_one();
+        return future;
+    }
+
+    typedef struct {
+      android::base::unique_fd fd;
+      char path[1024];
+    } TmpFile;
+
+    std::unique_ptr<TmpFile> createTempFile();
+    void deleteTempFiles(const std::string& folder);
+    void setThreadName(const pthread_t thread, int id);
+    void loop();
+
+    /*
+     * For test purpose only. Enables or disables logging duration of the task.
+     *
+     * |log_duration| if true, DurationReporter is initiated to log duration of
+     * the task.
+     */
+    void setLogDuration(bool log_duration);
+
+  private:
+    static const int MAX_THREAD_COUNT = 4;
+
+    /* A path to a temporary folder for threads to create temporary files. */
+    std::string tmp_root_;
+    bool shutdown_;
+    bool log_duration_; // For test purpose only, the default value is true.
+    std::mutex lock_;  // A lock for the tasks_.
+    std::condition_variable condition_variable_;
+
+    std::vector<std::thread> threads_;
+    std::queue<Task> tasks_;
+    std::map<std::string, Future> futures_map_;
+
+    DISALLOW_COPY_AND_ASSIGN(DumpPool);
+};
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index bbc724c..3091f6b 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -69,7 +69,7 @@
 
     static const std::vector<std::string> group_names{
         "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats",
-            "readproc", "bluetooth", "wakelock"};
+            "readproc", "bluetooth", "wakelock", "nfc"};
     std::vector<gid_t> groups(group_names.size(), 0);
     for (size_t i = 0; i < group_names.size(); ++i) {
         grp = getgrnam(group_names[i].c_str());
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
 std::string PropertiesHelper::build_type_ = "";
 int PropertiesHelper::dry_run_ = -1;
 int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
 
 bool PropertiesHelper::IsUserBuild() {
     if (build_type_.empty()) {
@@ -202,6 +203,14 @@
     return unroot_ == 1;
 }
 
+bool PropertiesHelper::IsParallelRun() {
+    if (parallel_run_ == -1) {
+        parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+                /* default_value = */true) ? 1 : 0;
+    }
+    return parallel_run_ == 1;
+}
+
 int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
     if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
      */
     static bool IsUnroot();
 
+    /*
+     * Whether or not the parallel run is enabled. Setting the system property
+     * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+     * true by default.
+     */
+    static bool IsParallelRun();
+
   private:
     static std::string build_type_;
     static int dry_run_;
     static int unroot_;
+    static int parallel_run_;
 };
 
 /*
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 083944f..839a2c3 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -1,7 +1,28 @@
 {
   "presubmit": [
     {
+      "name": "BugreportManagerTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.LargeTest"
+        }
+      ]
+    },
+    {
+      "name": "dumpstate_smoke_test"
+    },
+    {
       "name": "dumpstate_test"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "BugreportManagerTestCases"
+    }
+  ],
+  "imports": [
+    {
+      "path": "frameworks/base/packages/Shell"
+    }
   ]
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/TaskQueue.cpp b/cmds/dumpstate/TaskQueue.cpp
new file mode 100644
index 0000000..8550aec
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TaskQueue.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+TaskQueue::~TaskQueue() {
+    run(/* do_cancel = */true);
+}
+
+void TaskQueue::run(bool do_cancel) {
+    std::unique_lock lock(lock_);
+    while (!tasks_.empty()) {
+        auto task = tasks_.front();
+        tasks_.pop();
+        lock.unlock();
+        std::invoke(task, do_cancel);
+        lock.lock();
+    }
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/TaskQueue.h b/cmds/dumpstate/TaskQueue.h
new file mode 100644
index 0000000..b7e72f1
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+#define FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+
+#include <mutex>
+#include <queue>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * A task queue for dumpstate to collect tasks such as adding file to the zip
+ * which are needed to run in a single thread. The task is a callable function
+ * included a cancel task boolean parameter. The TaskQueue could
+ * cancel the task in the destructor if the task has never been called.
+ */
+class TaskQueue {
+  public:
+    TaskQueue() = default;
+    ~TaskQueue();
+
+    /*
+     * Adds a task into the queue.
+     *
+     * |f| Callable function to execute the task. The function must include a
+     *     boolean parameter for TaskQueue to notify whether the task is
+     *     cancelled or not.
+     * |args| A list of arguments.
+     */
+    template<class F, class... Args> void add(F&& f, Args&&... args) {
+        auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+        std::unique_lock lock(lock_);
+        tasks_.emplace([=](bool cancelled) {
+            std::invoke(func, cancelled);
+        });
+    }
+
+    /*
+     * Invokes all tasks in the task queue.
+     *
+     * |do_cancel| true to cancel all tasks in the queue.
+     */
+    void run(bool do_cancel);
+
+  private:
+    using Task = std::function<void(bool)>;
+
+    std::mutex lock_;
+    std::queue<Task> tasks_;
+
+    DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 93f8b68..f1e6fa9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -78,6 +78,7 @@
 #include <hardware_legacy/power.h>
 #include <hidl/ServiceManagement.h>
 #include <log/log.h>
+#include <log/log_read.h>
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -94,6 +95,7 @@
 using ::android::hardware::dumpstate::V1_1::toString;
 using ::std::literals::chrono_literals::operator""ms;
 using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
 
 // TODO: remove once moved to namespace
 using android::defaultServiceManager;
@@ -112,7 +114,9 @@
 using android::os::IDumpstateListener;
 using android::os::dumpstate::CommandOptions;
 using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
 using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::TaskQueue;
 
 // Keep in sync with
 // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -125,8 +129,8 @@
 static Dumpstate& ds = Dumpstate::GetInstance();
 static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                       const CommandOptions& options = CommandOptions::DEFAULT,
-                      bool verbose_duration = false) {
-    return ds.RunCommand(title, full_command, options, verbose_duration);
+                      bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
+    return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
 }
 
 // Reasonable value for max stats.
@@ -195,8 +199,36 @@
     func_ptr(__VA_ARGS__);                                  \
     RETURN_IF_USER_DENIED_CONSENT();
 
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...)      \
+    {                                                            \
+        DurationReporter duration_reporter_in_macro(log_title);  \
+        func_ptr(__VA_ARGS__);                                   \
+    }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+    RETURN_IF_USER_DENIED_CONSENT();                                           \
+    RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__);               \
+    RETURN_IF_USER_DENIED_CONSENT();
+
+#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
+    RETURN_IF_USER_DENIED_CONSENT();                      \
+    pool_ptr->waitForTask(task_name);                     \
+    RETURN_IF_USER_DENIED_CONSENT();
+
 static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
 
+// Names of parallel tasks, they are used for the DumpPool to identify the dump
+// task and the log title of the duration report.
+static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
+static const std::string DUMP_HALS_TASK = "DUMP HALS";
+static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
+static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
+
 namespace android {
 namespace os {
 namespace {
@@ -209,6 +241,10 @@
     return fd;
 }
 
+static int OpenForWrite(std::string path) {
+    return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
 
 static int OpenForRead(std::string path) {
     return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
@@ -274,14 +310,39 @@
     return version_code;
 }
 
+static bool PathExists(const std::string& path) {
+  struct stat sb;
+  return stat(path.c_str(), &sb) == 0;
+}
+
+static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) {
+    if (input_file == output_file) {
+        MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n",
+               output_file.c_str());
+        return false;
+    }
+    else if (PathExists(output_file)) {
+        MYLOGD("Cannot overwrite an existing file (%s)\n", output_file.c_str());
+        return false;
+    }
+
+    MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str());
+    android::base::unique_fd out_fd(OpenForWrite(output_file));
+    return CopyFileToFd(input_file, out_fd.get());
+}
+
 }  // namespace
 }  // namespace os
 }  // namespace android
 
 static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
                        const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
-                       long dumpsysTimeoutMs = 0) {
-    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs);
+                       long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {
+    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+                       int out_fd) {
+    return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd);
 }
 static int DumpFile(const std::string& title, const std::string& path) {
     return ds.DumpFile(title, path);
@@ -636,14 +697,24 @@
 
 static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
 
-/* timeout in ms to read a list of buffers */
+// Returns the actual readable size of the given buffer or -1 on error.
+static long logcat_buffer_readable_size(const std::string& buffer) {
+    std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+        android_logger_list_alloc(0, 0, 0), &android_logger_list_free};
+    auto logger = android_logger_open(logger_list.get(), android_name_to_log_id(buffer.c_str()));
+
+    return android_logger_get_log_readable_size(logger);
+}
+
+// Returns timeout in ms to read a list of buffers.
 static unsigned long logcat_timeout(const std::vector<std::string>& buffers) {
     unsigned long timeout_ms = 0;
     for (const auto& buffer : buffers) {
-        log_id_t id = android_name_to_log_id(buffer.c_str());
-        unsigned long property_size = __android_logger_get_buffer_size(id);
-        /* Engineering margin is ten-fold our guess */
-        timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf;
+        long readable_size = logcat_buffer_readable_size(buffer);
+        if (readable_size > 0) {
+            // Engineering margin is ten-fold our guess.
+            timeout_ms += 10 * (readable_size + worst_write_perf) / worst_write_perf;
+        }
     }
     return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
 }
@@ -726,8 +797,9 @@
     RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
                    CommandOptions::WithTimeout(1).Always().Build());
     printf("Bugreport format version: %s\n", version_.c_str());
-    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
-           PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+    printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+           id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+           options_->args.c_str(), options_->bugreport_mode.c_str());
     printf("\n");
 }
 
@@ -957,7 +1029,6 @@
         MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
         return;
     }
-    DurationReporter duration_reporter("INCIDENT REPORT");
     const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
     auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
                 O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
@@ -972,9 +1043,11 @@
         // Use a different name from "incident.proto"
         // /proto/incident.proto is reserved for incident service dump
         // i.e. metadata for debugging.
-        ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
+        ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt,
+                path);
+    } else {
+        unlink(path.c_str());
     }
-    unlink(path.c_str());
 }
 
 static void DumpVisibleWindowViews() {
@@ -1269,15 +1342,21 @@
                            /* timeout= */ 90s, /* service_timeout= */ 10s);
 }
 
-static void DumpHals() {
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpHals(int out_fd = STDOUT_FILENO) {
     if (!ds.IsZipping()) {
-        RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
-                   CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+        RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
+                   CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(),
+                   false, out_fd);
         return;
     }
-    DurationReporter duration_reporter("DUMP HALS");
-    RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
-               CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+    RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
+               CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+               false, out_fd);
 
     using android::hidl::manager::V1_0::IServiceManager;
     using android::hardware::defaultServiceManager;
@@ -1299,6 +1378,7 @@
                             }, '_');
             const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
 
+            bool empty = false;
             {
                 auto fd = android::base::unique_fd(
                     TEMP_FAILURE_RETRY(open(path.c_str(),
@@ -1313,13 +1393,14 @@
                         {"lshal", "debug", "-E", interface},
                         CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
 
-                bool empty = 0 == lseek(fd, 0, SEEK_END);
-                if (!empty) {
-                    ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
-                }
+                empty = 0 == lseek(fd, 0, SEEK_END);
             }
-
-            unlink(path.c_str());
+            if (!empty) {
+                ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
+                        path);
+            } else {
+                unlink(path.c_str());
+            }
         }
     });
 
@@ -1367,13 +1448,11 @@
     printf("\n");
 }
 
-static void DumpstateArcOnly() {
+static void DumpstateLimitedOnly() {
     // Trimmed-down version of dumpstate to only include a whitelisted
     // set of logs (system log, event log, and system server / system app
-    // crashes, and ARC networking logs). See b/136273873 and b/138459828
-    // for context. New sections must be first approved by Chrome OS Privacy
-    // and then added to server side cros monitoring PII scrubber before adding
-    // them here. See cl/312126645 for an example.
+    // crashes, and networking logs). See b/136273873 and b/138459828
+    // for context.
     DurationReporter duration_reporter("DUMPSTATE");
     unsigned long timeout_ms;
     // calculate timeout
@@ -1391,11 +1470,6 @@
     printf("== Networking Service\n");
     printf("========================================================\n");
 
-    // ARC networking service implements dumpsys by reusing the 'wifi' service name.
-    // The top-level handler is implemented in handleDump() in
-    // vendor/google_arc/libs/arc-services/src/com/android/server/arc/net/ArcNetworkService.java.
-    // It outputs a subset of Android system server state relevant for debugging ARC
-    // connectivity issues, in a PII-free manner. See b/147270970.
     RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
                CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
 
@@ -1414,6 +1488,73 @@
     printf("========================================================\n");
 }
 
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpCheckins(int out_fd = STDOUT_FILENO) {
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Checkins\n");
+    dprintf(out_fd, "========================================================\n");
+
+    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
+    RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
+    RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
+    RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
+    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
+    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd);
+}
+
+/*
+ * Runs dumpsys on activity service to dump all application activities, services
+ * and providers in the device.
+ *
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpAppInfos(int out_fd = STDOUT_FILENO) {
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Running Application Activities\n");
+    dprintf(out_fd, "========================================================\n");
+
+    // The following dumpsys internally collects output from running apps, so it can take a long
+    // time. So let's extend the timeout.
+
+    const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
+
+    RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Running Application Services (platform)\n");
+    dprintf(out_fd, "========================================================\n");
+
+    RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
+            DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Running Application Services (non-platform)\n");
+    dprintf(out_fd, "========================================================\n");
+
+    RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+            DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Running Application Providers (platform)\n");
+    dprintf(out_fd, "========================================================\n");
+
+    RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
+            DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Running Application Providers (non-platform)\n");
+    dprintf(out_fd, "========================================================\n");
+
+    RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
+            DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+}
+
 // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
 // via the consent they are shown. Ignores other errors that occur while running various
 // commands. The consent checking is currently done around long running tasks, which happen to
@@ -1421,6 +1562,19 @@
 static Dumpstate::RunStatus dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
+    // Enqueue slow functions into the thread pool, if the parallel run is enabled.
+    if (ds.dump_pool_) {
+        // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
+        // drop root user. Restarts it with two threads for the parallel run.
+        ds.dump_pool_->start(/* thread_counts = */2);
+
+        ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+        ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+        ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+        ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+        ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1);
+    }
+
     // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
     // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
     // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
@@ -1452,7 +1606,11 @@
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
                                          CommandOptions::AS_ROOT);
 
-    DumpHals();
+    if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
+    }
 
     RunCommand("PRINTENV", {"printenv"});
     RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1463,8 +1621,7 @@
         RunCommand("LSMOD", {"lsmod"});
     }
 
-    if (__android_logger_property_get_bool(
-            "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) {
+    if (android::base::GetBoolProperty("ro.logd.kernel", false)) {
         DoKernelLogcat();
     } else {
         do_dmesg();
@@ -1479,6 +1636,8 @@
 
     /* Dump Bluetooth HCI logs */
     ds.AddDir("/data/misc/bluetooth/logs", true);
+    /* Dump Nfc NCI logs */
+    ds.AddDir("/data/misc/nfc/logs", true);
 
     if (ds.options_->do_screenshot && !ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
@@ -1534,7 +1693,11 @@
 
     ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
 
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
+    if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+    }
 
     /* Migrate the ril_dumpstate to a device specific dumpstate? */
     int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1556,57 +1719,17 @@
 
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
 
-    printf("========================================================\n");
-    printf("== Checkins\n");
-    printf("========================================================\n");
+    if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
+    }
 
-    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
-
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
-
-    RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
-    RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
-    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
-    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
-
-    printf("========================================================\n");
-    printf("== Running Application Activities\n");
-    printf("========================================================\n");
-
-    // The following dumpsys internally collects output from running apps, so it can take a long
-    // time. So let's extend the timeout.
-
-    const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
-
-    RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS);
-
-    printf("========================================================\n");
-    printf("== Running Application Services (platform)\n");
-    printf("========================================================\n");
-
-    RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
-            DUMPSYS_COMPONENTS_OPTIONS);
-
-    printf("========================================================\n");
-    printf("== Running Application Services (non-platform)\n");
-    printf("========================================================\n");
-
-    RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
-            DUMPSYS_COMPONENTS_OPTIONS);
-
-    printf("========================================================\n");
-    printf("== Running Application Providers (platform)\n");
-    printf("========================================================\n");
-
-    RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
-            DUMPSYS_COMPONENTS_OPTIONS);
-
-    printf("========================================================\n");
-    printf("== Running Application Providers (non-platform)\n");
-    printf("========================================================\n");
-
-    RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
-            DUMPSYS_COMPONENTS_OPTIONS);
+    if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
+    }
 
     printf("========================================================\n");
     printf("== Dropbox crashes\n");
@@ -1631,7 +1754,12 @@
     // Add linker configuration directory
     ds.AddDir(LINKERCONFIG_DIR, true);
 
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
+    if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
+                DumpIncidentReport);
+    }
 
     return Dumpstate::RunStatus::OK;
 }
@@ -1652,7 +1780,18 @@
     time_t logcat_ts = time(nullptr);
 
     /* collect stack traces from Dalvik and native processes (needs root) */
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+    if (dump_pool_) {
+        RETURN_IF_USER_DENIED_CONSENT();
+        // One thread is enough since we only need to enqueue DumpTraces here.
+        dump_pool_->start(/* thread_counts = */1);
+
+        // DumpTraces takes long time, post it to the another thread in the
+        // pool, if pool is available
+        dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+                &dump_traces_path);
+    }
 
     /* Run some operations that require root. */
     ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1695,6 +1834,15 @@
     DumpFile("PSI memory", "/proc/pressure/memory");
     DumpFile("PSI io", "/proc/pressure/io");
 
+    if (dump_pool_) {
+        RETURN_IF_USER_DENIED_CONSENT();
+        dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+        // Current running thread in the pool is the root user also. Shutdown
+        // the pool and restart later to ensure all threads in the pool could
+        // drop the root user.
+        dump_pool_->shutdown();
+    }
     if (!DropRootUser()) {
         return Dumpstate::RunStatus::ERROR;
     }
@@ -1706,31 +1854,39 @@
     return status;
 }
 
+// Common states for telephony and wifi which are needed to be collected before
+// dumpstate drop the root user.
+static void DumpstateRadioAsRoot() {
+    DumpIpTablesAsRoot();
+    ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
 // This method collects common dumpsys for telephony and wifi. Typically, wifi
 // reports are fine to include all information, but telephony reports on user
 // builds need to strip some content (see DumpstateTelephonyOnly).
 static void DumpstateRadioCommon(bool include_sensitive_info = true) {
-    DumpIpTablesAsRoot();
-
-    ds.AddDir(LOGPERSIST_DATA_DIR, false);
-
-    if (!DropRootUser()) {
-        return;
-    }
-
     // We need to be picky about some stuff for telephony reports on user builds.
     if (!include_sensitive_info) {
         // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
         DoRadioLogcat();
     } else {
+        // DumpHals takes long time, post it to the another thread in the pool,
+        // if pool is available.
+        if (ds.dump_pool_) {
+            ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+        }
         // Contains various system properties and process startup info.
         do_dmesg();
         // Logs other than the radio buffer may contain package/component names and potential PII.
         DoLogcat();
         // Too broad for connectivity problems.
         DoKmsg();
-        // Contains unrelated hardware info (camera, NFC, biometrics, ...).
-        DumpHals();
+        // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
+        if (ds.dump_pool_) {
+            ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+        } else {
+            RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
+        }
     }
 
     DumpPacketStats();
@@ -1754,6 +1910,21 @@
 
     const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
 
+    DumpstateRadioAsRoot();
+    if (!DropRootUser()) {
+        return;
+    }
+
+    // Starts thread pool after the root user is dropped, and two additional threads
+    // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+    if (ds.dump_pool_) {
+        ds.dump_pool_->start(/*thread_counts =*/2);
+
+        // DumpstateBoard takes long time, post it to the another thread in the pool,
+        // if pool is available.
+        ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+    }
+
     DumpstateRadioCommon(include_sensitive_info);
 
     if (include_sensitive_info) {
@@ -1830,12 +2001,29 @@
     printf("========================================================\n");
     printf("== dumpstate: done (id %d)\n", ds.id_);
     printf("========================================================\n");
+
+    if (ds.dump_pool_) {
+        ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+    } else {
+        RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+    }
 }
 
 // This method collects dumpsys for wifi debugging only
 static void DumpstateWifiOnly() {
     DurationReporter duration_reporter("DUMPSTATE");
 
+    DumpstateRadioAsRoot();
+    if (!DropRootUser()) {
+        return;
+    }
+
+    // Starts thread pool after the root user is dropped. Only one additional
+    // thread is needed for DumpHals in the DumpstateRadioCommon.
+    if (ds.dump_pool_) {
+        ds.dump_pool_->start(/*thread_counts =*/1);
+    }
+
     DumpstateRadioCommon();
 
     printf("========================================================\n");
@@ -1853,8 +2041,6 @@
 }
 
 Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
-    DurationReporter duration_reporter("DUMP TRACES");
-
     const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
     const size_t buf_size = temp_file_pattern.length() + 1;
     std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -1962,11 +2148,10 @@
     return RunStatus::OK;
 }
 
-void Dumpstate::DumpstateBoard() {
-    DurationReporter duration_reporter("dumpstate_board()");
-    printf("========================================================\n");
-    printf("== Board\n");
-    printf("========================================================\n");
+void Dumpstate::DumpstateBoard(int out_fd) {
+    dprintf(out_fd, "========================================================\n");
+    dprintf(out_fd, "== Board\n");
+    dprintf(out_fd, "========================================================\n");
 
     if (!IsZipping()) {
         MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
@@ -2092,18 +2277,20 @@
             MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
             continue;
         }
-        AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
-        printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
+        remover[i].Disable();
+        EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
+        dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
     }
 }
 
 static void ShowUsage() {
     fprintf(stderr,
-            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
-            "[-z] [-s] [-S] [-q] [-P] [-R] [-A] [-V version]\n"
+            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
+            "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
             "  -h: display this help message\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
+            "  -o: write to custom directory (only in limited mode)\n"
             "  -d: append date to filename\n"
             "  -p: capture screenshot to filename.png\n"
             "  -z: generate zipped file\n"
@@ -2113,7 +2300,7 @@
             "  -P: send broadcast when started and do progress updates\n"
             "  -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
             "  -w: start binder service and make it wait for a call to startBugreport\n"
-            "  -A: output limited information that is safe for submission in ARC++ bugreports\n"
+            "  -L: output limited information that is safe for submission in feedback reports\n"
             "  -v: prints the dumpstate header and exit\n");
 }
 
@@ -2122,6 +2309,11 @@
 }
 
 bool Dumpstate::FinishZipFile() {
+    // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file.
+    if (zip_entry_tasks_) {
+        zip_entry_tasks_->run(/* do_cancel = */false);
+    }
+
     std::string entry_name = base_name_ + "-" + name_ + ".txt";
     MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
            tmp_path_.c_str());
@@ -2274,6 +2466,13 @@
             do_text_file = false;
         }
     }
+
+    std::string final_path = ds.path_;
+    if (ds.options_->OutputToCustomFile()) {
+        final_path = ds.GetPath(ds.options_->out_dir, ".zip");
+        android::os::CopyFileToFile(ds.path_, final_path);
+    }
+
     if (ds.options_->use_control_socket) {
         if (do_text_file) {
             dprintf(ds.control_socket_fd_,
@@ -2281,7 +2480,7 @@
                     "for more details\n",
                     ds.log_path_.c_str());
         } else {
-            dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
+            dprintf(ds.control_socket_fd_, "OK:%s\n", final_path.c_str());
         }
     }
 }
@@ -2318,7 +2517,6 @@
             break;
         case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
             // Currently, the dumpstate binder is only used by Shell to update progress.
-            options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_screenshot = is_screenshot_requested;
             options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
@@ -2330,7 +2528,6 @@
             options->dumpstate_hal_mode = DumpstateMode::REMOTE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
-            options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_zip_file = true;
             options->do_screenshot = is_screenshot_requested;
@@ -2357,14 +2554,14 @@
 static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
     MYLOGI(
         "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
-        "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+        "is_remote_mode: %d show_header_only: %d telephony_only: %d "
         "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
-        "arc_only: %d args: %s\n",
+        "limited_only: %d args: %s\n",
         options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
         options.do_screenshot, options.is_remote_mode, options.show_header_only,
-        options.do_start_service, options.telephony_only, options.wifi_only,
+        options.telephony_only, options.wifi_only,
         options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
-        toString(options.dumpstate_hal_mode).c_str(), options.arc_only, options.args.c_str());
+        toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2386,11 +2583,12 @@
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
     RunStatus status = RunStatus::OK;
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
         switch (c) {
             // clang-format off
             case 'd': do_add_date = true;            break;
             case 'z': do_zip_file = true;            break;
+            case 'o': out_dir = optarg;              break;
             case 's': use_socket = true;             break;
             case 'S': use_control_socket = true;     break;
             case 'v': show_header_only = true;       break;
@@ -2398,7 +2596,7 @@
             case 'p': do_screenshot = true;          break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
-            case 'A': arc_only = true;               break;
+            case 'L': limited_only = true;           break;
             case 'V':                                break;  // compatibility no-op
             case 'w':
                 // This was already processed
@@ -2492,6 +2690,15 @@
     }
     tombstone_data_.clear();
     anr_data_.clear();
+
+    // Instead of shutdown the pool, we delete temporary files directly since
+    // shutdown blocking the call.
+    if (dump_pool_) {
+        dump_pool_->deleteTempFiles();
+    }
+    if (zip_entry_tasks_) {
+        zip_entry_tasks_->run(/*do_cancel =*/ true);
+    }
 }
 
 /*
@@ -2512,11 +2719,12 @@
  * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
  * gets added to the archive.
  *
- * Bugreports are first generated in a local directory and later copied to the caller's fd if
- * supplied.
+ * Bugreports are first generated in a local directory and later copied to the caller's fd
+ * or directory if supplied.
  */
 Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
                                             const std::string& calling_package) {
+    DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true);
     LogDumpOptions(*options_);
     if (!options_->ValidateOptions()) {
         MYLOGE("Invalid options specified\n");
@@ -2576,15 +2784,6 @@
 
     register_sig_handler();
 
-    // TODO(b/111441001): maybe skip if already started?
-    if (options_->do_start_service) {
-        MYLOGI("Starting 'dumpstate' service\n");
-        android::status_t ret;
-        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
-            MYLOGE("Unable to start DumpstateService: %d\n", ret);
-        }
-    }
-
     if (PropertiesHelper::IsDryRun()) {
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
@@ -2678,28 +2877,34 @@
     // Don't buffer stdout
     setvbuf(stdout, nullptr, _IONBF, 0);
 
+    // Enable the parallel run if the client requests to output to a file.
+    EnableParallelRunIfNeeded();
+    // Using scope guard to make sure the dump pool can be shut down correctly.
+    auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() {
+        ShutdownDumpPool();
+    });
+
     // NOTE: there should be no stdout output until now, otherwise it would break the header.
     // In particular, DurationReport objects should be created passing 'title, NULL', so their
     // duration is logged into MYLOG instead.
     PrintHeader();
 
-    // TODO(nandana) reduce code repetition in if branches
+    // TODO(b/158737089) reduce code repetition in if branches
     if (options_->telephony_only) {
         MaybeTakeEarlyScreenshot();
         onUiIntensiveBugreportDumpsFinished(calling_uid);
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateTelephonyOnly(calling_package);
-        DumpstateBoard();
     } else if (options_->wifi_only) {
         MaybeTakeEarlyScreenshot();
         onUiIntensiveBugreportDumpsFinished(calling_uid);
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
-    } else if (options_->arc_only) {
+    } else if (options_->limited_only) {
         MaybeTakeEarlyScreenshot();
         onUiIntensiveBugreportDumpsFinished(calling_uid);
         MaybeCheckUserConsent(calling_uid, calling_package);
-        DumpstateArcOnly();
+        DumpstateLimitedOnly();
     } else {
         // Invoke critical dumpsys first to preserve system state, before doing anything else.
         RunDumpsysCritical();
@@ -2842,6 +3047,45 @@
     android::os::UnlinkAndLogOnError(path_);
 }
 
+void Dumpstate::EnableParallelRunIfNeeded() {
+    // The thread pool needs to create temporary files to receive dump results.
+    // That's why we only enable it when the bugreport client chooses to output
+    // to a file.
+    if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+        return;
+    }
+    dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+    zip_entry_tasks_ = std::make_unique<TaskQueue>();
+}
+
+void Dumpstate::ShutdownDumpPool() {
+    if (dump_pool_) {
+        dump_pool_->shutdown();
+        dump_pool_ = nullptr;
+    }
+    if (zip_entry_tasks_) {
+        zip_entry_tasks_->run(/* do_cancel = */true);
+        zip_entry_tasks_ = nullptr;
+    }
+}
+
+void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+        const std::string& entry_path) {
+    auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) {
+        if (!task_cancelled) {
+            AddZipEntry(entry_name, entry_path);
+        }
+        android::os::UnlinkAndLogOnError(entry_path);
+    };
+    if (zip_entry_tasks_) {
+        // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled.
+        zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1);
+    } else {
+        // Invokes AddZipEntryAndCleanup immediately
+        std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false);
+    }
+}
+
 Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
     MYLOGD("User denied consent; deleting files and returning\n");
     CleanupTmpFiles();
@@ -2960,8 +3204,9 @@
     return singleton_;
 }
 
-DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose)
-    : title_(title), logcat_only_(logcat_only), verbose_(verbose) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose,
+        int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose),
+        duration_fd_(duration_fd) {
     if (!title_.empty()) {
         started_ = Nanotime();
     }
@@ -2975,7 +3220,8 @@
         }
         if (!logcat_only_) {
             // Use "Yoda grammar" to make it easier to grep|sort sections.
-            printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
+            dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
+                    elapsed, title_.c_str());
         }
     }
 }
@@ -3561,10 +3807,11 @@
 }
 
 int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
-                          const CommandOptions& options, bool verbose_duration) {
-    DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration);
+                          const CommandOptions& options, bool verbose_duration, int out_fd) {
+    DurationReporter duration_reporter(title, false /* logcat_only */,
+                                       verbose_duration, out_fd);
 
-    int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
+    int status = RunCommandToFd(out_fd, title, full_command, options);
 
     /* TODO: for now we're simplifying the progress calculation by using the
      * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -3576,11 +3823,11 @@
 }
 
 void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
-                           const CommandOptions& options, long dumpsysTimeoutMs) {
+                           const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) {
     long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs();
     std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)};
     dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
-    RunCommand(title, dumpsys, options);
+    RunCommand(title, dumpsys, options, false, out_fd);
 }
 
 int open_socket(const char *service) {
@@ -3703,12 +3950,16 @@
     fclose(fp);
 }
 
-// TODO: make this function thread safe if sections are generated in parallel.
 void Dumpstate::UpdateProgress(int32_t delta_sec) {
     if (progress_ == nullptr) {
         MYLOGE("UpdateProgress: progress_ not set\n");
         return;
     }
+    // This function updates progress related members of the dumpstate and reports
+    // progress percentage to the bugreport client. Since it could be called by
+    // different dump tasks at the same time if the parallel run is enabled, a
+    // mutex lock is necessary here to synchronize the call.
+    std::lock_guard<std::recursive_mutex> lock(mutex_);
 
     // Always update progess so stats can be tuned...
     progress_->Inc(delta_sec);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index be6bc5d..3b9b1b7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,8 @@
 #include <ziparchive/zip_writer.h>
 
 #include "DumpstateUtil.h"
+#include "DumpPool.h"
+#include "TaskQueue.h"
 
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
@@ -75,7 +77,7 @@
 class DurationReporter {
   public:
     explicit DurationReporter(const std::string& title, bool logcat_only = false,
-                              bool verbose = false);
+                              bool verbose = false, int duration_fd = STDOUT_FILENO);
 
     ~DurationReporter();
 
@@ -84,6 +86,7 @@
     bool logcat_only_;
     bool verbose_;
     uint64_t started_;
+    int duration_fd_;
 
     DISALLOW_COPY_AND_ASSIGN(DurationReporter);
 };
@@ -193,7 +196,7 @@
  * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
  */
 class Dumpstate {
-    friend class DumpstateTest;
+    friend class android::os::dumpstate::DumpstateTest;
 
   public:
     enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -227,11 +230,13 @@
      * |full_command| array containing the command (first entry) and its arguments.
      * Must contain at least one element.
      * |options| optional argument defining the command's behavior.
+     * |out_fd| A fd to support the DumpPool to output results to a temporary
+     * file. Using STDOUT_FILENO if it's not running in the parallel task.
      */
     int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
                    const android::os::dumpstate::CommandOptions& options =
                        android::os::dumpstate::CommandOptions::DEFAULT,
-                   bool verbose_duration = false);
+                   bool verbose_duration = false, int out_fd = STDOUT_FILENO);
 
     /*
      * Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -244,10 +249,12 @@
      * |options| optional argument defining the command's behavior.
      * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the
      * timeout from `options`)
+     * |out_fd| A fd to support the DumpPool to output results to a temporary
+     * file. Using STDOUT_FILENO if it's not running in the parallel task.
      */
     void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
                     const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
-                    long dumpsys_timeout_ms = 0);
+                    long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO);
 
     /*
      * Prints the contents of a file.
@@ -304,7 +311,12 @@
     // Returns OK in all other cases.
     RunStatus DumpTraces(const char** path);
 
-    void DumpstateBoard();
+    /*
+     * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+     * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+     * if it's not running in the parallel task.
+     */
+    void DumpstateBoard(int out_fd = STDOUT_FILENO);
 
     /*
      * Updates the overall progress of the bugreport generation by the given weight increment.
@@ -361,6 +373,18 @@
     bool CalledByApi() const;
 
     /*
+     * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled,
+     * otherwise invokes it immediately. The task adds file at path entry_path
+     * as a zip file entry with name entry_name. Unlinks entry_path when done.
+     *
+     * All enqueued tasks will be executed in the dumpstate's FinishZipFile method
+     * before the zip file is finished. Tasks will be cancelled in dumpstate's
+     * ShutdownDumpPool method if they have never been called.
+     */
+    void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+            const std::string& entry_path);
+
+    /*
      * Structure to hold options that determine the behavior of dumpstate.
      */
     struct DumpOptions {
@@ -374,11 +398,10 @@
         bool is_screenshot_copied = false;
         bool is_remote_mode = false;
         bool show_header_only = false;
-        bool do_start_service = false;
         bool telephony_only = false;
         bool wifi_only = false;
         // Trimmed-down version of dumpstate to only include whitelisted logs.
-        bool arc_only = false;
+        bool limited_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
         // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
@@ -386,10 +409,12 @@
         // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
         ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
             ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
-        // File descriptor to output zip file.
+        // File descriptor to output zip file. Takes precedence over out_dir.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
         android::base::unique_fd screenshot_fd;
+        // Custom output directory.
+        std::string out_dir;
         // Bugreport mode of the bugreport.
         std::string bugreport_mode;
         // Command-line arguments as string
@@ -415,6 +440,12 @@
             // specified, it is preferred. If not bugreport is written to /bugreports.
             return !use_socket;
         }
+
+        /* Returns if options specified require writing to custom file location */
+        bool OutputToCustomFile() {
+            // Custom location is only honored in limited mode.
+            return limited_only && !out_dir.empty() && bugreport_fd.get() == -1;
+        }
     };
 
     // TODO: initialize fields on constructor
@@ -482,6 +513,13 @@
     // List of open ANR dump files.
     std::vector<DumpData> anr_data_;
 
+    // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
+    std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+
+    // A task queue to collect adding zip entry tasks inside dump tasks if the
+    // parallel run is enabled.
+    std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_;
+
     // A callback to IncidentCompanion service, which checks user consent for sharing the
     // bugreport with the calling app. If the user has not responded yet to the dialog it will
     // be neither confirmed nor denied.
@@ -519,6 +557,10 @@
     // but leaves the log file alone.
     void CleanupTmpFiles();
 
+    // Create the thread pool to enable the parallel run function.
+    void EnableParallelRunIfNeeded();
+    void ShutdownDumpPool();
+
     RunStatus HandleUserConsentDenied();
 
     // Copies bugreport artifacts over to the caller's directories provided there is user consent or
@@ -530,6 +572,8 @@
 
     android::sp<ConsentCallback> consent_callback_;
 
+    std::recursive_mutex mutex_;
+
     DISALLOW_COPY_AND_ASSIGN(Dumpstate);
 };
 
diff --git a/libs/binder/ndk/tests/AndroidTest.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
similarity index 70%
rename from libs/binder/ndk/tests/AndroidTest.xml
rename to cmds/dumpstate/dumpstate_smoke_test.xml
index 89646f7..0aff200 100644
--- a/libs/binder/ndk/tests/AndroidTest.xml
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,20 +13,19 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs binderVendorDoubleLoadTest.">
+<configuration description="Config for dumpstate_smoke_test">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-native" />
 
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" />
+        <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/nativetest/vendor" />
-        <option name="module-name" value="binderVendorDoubleLoadTest" />
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="dumpstate_smoke_test" />
+        <option name="native-test-timeout" value="600000" />
     </test>
 </configuration>
-
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index f1342a5..ec89c0d 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -30,7 +30,7 @@
     bool do_wait = false;
     int c;
     // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
-    while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1 && !do_wait) {
+    while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1 && !do_wait) {
         switch (c) {
             case 'w':
                 do_wait = true;
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c2c31c0..70bdbcc 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -206,13 +206,12 @@
     static std::shared_ptr<std::vector<SectionInfo>> sections;
     static Dumpstate& ds;
     static std::chrono::milliseconds duration;
-    static void SetUpTestCase() {
+    static void GenerateBugreport() {
         // clang-format off
         char* argv[] = {
             (char*)"dumpstate",
             (char*)"-d",
-            (char*)"-z",
-            (char*)"-B"
+            (char*)"-z"
         };
         // clang-format on
         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
@@ -233,20 +232,20 @@
 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
 
 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+    GenerateBugreport();
     EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
 }
 
-TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
     struct stat st;
     EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
-    EXPECT_GE(st.st_size, 3000000 /* 3MB */);
-    EXPECT_LE(st.st_size, 30000000 /* 30MB */);
+    EXPECT_GE(st.st_size, 1000000 /* 1MB */);
 }
 
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
     EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
                              << duration.count() << " s.";
-    EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+    EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
                               << duration.count() << " s.";
 }
 
@@ -263,7 +262,8 @@
         CloseArchive(handle);
     }
 
-    void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+    void FileExists(const char* filename, uint32_t minsize,
+                    uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
         ZipEntry entry;
         GetEntry(handle, filename, &entry);
         EXPECT_GT(entry.uncompressed_length, minsize);
@@ -282,7 +282,7 @@
                     main_entry.uncompressed_length);
 
     // contains main entry file
-    FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
+    FileExists(bugreport_txt_name.c_str(), 1000000U);
 }
 
 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
@@ -298,8 +298,9 @@
 }
 
 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
-    FileExists("dumpstate_board.bin", 1000000U, 80000000U);
-    FileExists("dumpstate_board.txt", 100000U, 1000000U);
+    // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
+    // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+    // FileExists("dumpstate_board.txt", 100000U, 1000000U);
 }
 
 TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
@@ -397,7 +398,7 @@
     SectionExists("batterystats", /* bytes= */ 1000);
 }
 
-TEST_F(BugreportSectionTest, WifiSectionGenerated) {
+TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
     SectionExists("wifi", /* bytes= */ 100000);
 }
 
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index bc3da1f..fdeea24 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -21,6 +21,7 @@
 #include "DumpstateService.h"
 #include "android/os/BnDumpstate.h"
 #include "dumpstate.h"
+#include "DumpPool.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -46,6 +47,7 @@
 
 using ::android::hardware::dumpstate::V1_1::DumpstateMode;
 using ::testing::EndsWith;
+using ::testing::Eq;
 using ::testing::HasSubstr;
 using ::testing::IsEmpty;
 using ::testing::IsNull;
@@ -94,6 +96,10 @@
         PropertiesHelper::unroot_ = unroot;
     }
 
+    void SetParallelRun(bool parallel_run) const {
+        PropertiesHelper::parallel_run_ = parallel_run;
+    }
+
     bool IsStandalone() const {
         return calls_ == 1;
     }
@@ -171,6 +177,7 @@
 
     EXPECT_FALSE(options_.do_add_date);
     EXPECT_FALSE(options_.do_zip_file);
+    EXPECT_EQ("", options_.out_dir);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
     EXPECT_FALSE(options_.show_header_only);
@@ -178,7 +185,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -206,7 +213,7 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -232,7 +239,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -250,8 +257,7 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.do_start_service);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
@@ -259,7 +265,6 @@
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
-    EXPECT_TRUE(options_.do_start_service);
     EXPECT_TRUE(options_.do_screenshot);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
@@ -269,7 +274,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
@@ -286,7 +291,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
@@ -295,7 +300,6 @@
     EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
-    EXPECT_TRUE(options_.do_start_service);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
 
     // Other options retain default values
@@ -304,7 +308,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
@@ -322,7 +326,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
@@ -340,10 +344,10 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
-TEST_F(DumpOptionsTest, InitializeArcOnlyBugreport) {
+TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) {
     // clang-format off
     char* argv[] = {
         const_cast<char*>("dumpstatez"),
@@ -351,7 +355,8 @@
         const_cast<char*>("-d"),
         const_cast<char*>("-z"),
         const_cast<char*>("-q"),
-        const_cast<char*>("-A")
+        const_cast<char*>("-L"),
+        const_cast<char*>("-o abc")
     };
     // clang-format on
 
@@ -362,7 +367,8 @@
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.use_control_socket);
     EXPECT_FALSE(options_.do_vibrate);
-    EXPECT_TRUE(options_.arc_only);
+    EXPECT_TRUE(options_.limited_only);
+    EXPECT_EQ(" abc", std::string(options_.out_dir));
 
     // Other options retain default values
     EXPECT_FALSE(options_.show_header_only);
@@ -399,7 +405,7 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.wifi_only);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial1) {
@@ -429,7 +435,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -459,7 +465,7 @@
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
-    EXPECT_FALSE(options_.arc_only);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -538,6 +544,10 @@
         ds.options_.reset(new Dumpstate::DumpOptions());
     }
 
+    void TearDown() {
+        ds.ShutdownDumpPool();
+    }
+
     // Runs a command and capture `stdout` and `stderr`.
     int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                    const CommandOptions& options = CommandOptions::DEFAULT) {
@@ -565,6 +575,10 @@
         ds.progress_.reset(new Progress(initial_max, progress, 1.2));
     }
 
+    void EnableParallelRunIfNeeded() {
+        ds.EnableParallelRunIfNeeded();
+    }
+
     std::string GetProgressMessage(int progress, int max,
             int old_max = 0, bool update_progress = true) {
         EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
@@ -1003,6 +1017,30 @@
     ds.listener_.clear();
 }
 
+TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
+    ds.options_->use_socket = false;
+    SetParallelRun(true);
+    EnableParallelRunIfNeeded();
+    EXPECT_TRUE(ds.options_->OutputToFile());
+    EXPECT_TRUE(ds.zip_entry_tasks_);
+    EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+    ds.options_->use_socket = true;
+    EnableParallelRunIfNeeded();
+    EXPECT_FALSE(ds.options_->OutputToFile());
+    EXPECT_FALSE(ds.zip_entry_tasks_);
+    EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+    SetParallelRun(false);
+    EnableParallelRunIfNeeded();
+    EXPECT_FALSE(ds.zip_entry_tasks_);
+    EXPECT_FALSE(ds.dump_pool_);
+}
+
 class DumpstateServiceTest : public DumpstateBaseTest {
   public:
     DumpstateService dss;
@@ -1614,6 +1652,154 @@
     EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
 }
 
+class DumpPoolTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
+        DumpstateBaseTest::SetUp();
+        CreateOutputFile();
+    }
+
+    void CreateOutputFile() {
+        out_path_ = kTestDataPath + "out.txt";
+        out_fd_.reset(TEMP_FAILURE_RETRY(open(out_path_.c_str(),
+                O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+        ASSERT_GE(out_fd_.get(), 0) << "could not create FD for path "
+                << out_path_;
+    }
+
+    int getTempFileCounts(const std::string& folder) {
+        int count = 0;
+        std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+                &closedir);
+        if (!dir_ptr) {
+            return -1;
+        }
+        int dir_fd = dirfd(dir_ptr.get());
+        if (dir_fd < 0) {
+            return -1;
+        }
+
+        struct dirent* de;
+        while ((de = readdir(dir_ptr.get()))) {
+            if (de->d_type != DT_REG) {
+                continue;
+            }
+            std::string file_name(de->d_name);
+            if (file_name.find(DumpPool::PREFIX_TMPFILE_NAME) != 0) {
+                continue;
+            }
+            count++;
+        }
+        return count;
+    }
+
+    void setLogDuration(bool log_duration) {
+        dump_pool_->setLogDuration(log_duration);
+    }
+
+    std::unique_ptr<DumpPool> dump_pool_;
+    android::base::unique_fd out_fd_;
+    std::string out_path_;
+};
+
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
+    auto dump_func_1 = [](int out_fd) {
+        dprintf(out_fd, "A");
+    };
+    auto dump_func_2 = [](int out_fd) {
+        dprintf(out_fd, "B");
+        sleep(1);
+    };
+    auto dump_func_3 = [](int out_fd) {
+        dprintf(out_fd, "C");
+    };
+    setLogDuration(/* log_duration = */false);
+    dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+    dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+    dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+
+    dump_pool_->waitForTask("1", "", out_fd_.get());
+    dump_pool_->waitForTask("2", "", out_fd_.get());
+    dump_pool_->waitForTask("3", "", out_fd_.get());
+    dump_pool_->shutdown();
+
+    std::string result;
+    ReadFileToString(out_path_, &result);
+    EXPECT_THAT(result, StrEq("A\nB\nC\n"));
+    EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+    bool run_1 = false;
+    auto dump_func_1 = [&]() {
+        run_1 = true;
+    };
+
+    dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+    dump_pool_->waitForTask("1", "", out_fd_.get());
+    dump_pool_->shutdown();
+
+    std::string result;
+    ReadFileToString(out_path_, &result);
+    EXPECT_TRUE(run_1);
+    EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+    EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+class TaskQueueTest : public DumpstateBaseTest {
+public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+    }
+
+    TaskQueue task_queue_;
+};
+
+TEST_F(TaskQueueTest, runTask) {
+    bool is_task1_run = false;
+    bool is_task2_run = false;
+    auto task_1 = [&](bool task_cancelled) {
+        if (task_cancelled) {
+            return;
+        }
+        is_task1_run = true;
+    };
+    auto task_2 = [&](bool task_cancelled) {
+        if (task_cancelled) {
+            return;
+        }
+        is_task2_run = true;
+    };
+    task_queue_.add(task_1, std::placeholders::_1);
+    task_queue_.add(task_2, std::placeholders::_1);
+
+    task_queue_.run(/* do_cancel = */false);
+
+    EXPECT_TRUE(is_task1_run);
+    EXPECT_TRUE(is_task2_run);
+}
+
+TEST_F(TaskQueueTest, runTask_withCancelled) {
+    bool is_task1_cancelled = false;
+    bool is_task2_cancelled = false;
+    auto task_1 = [&](bool task_cancelled) {
+        is_task1_cancelled = task_cancelled;
+    };
+    auto task_2 = [&](bool task_cancelled) {
+        is_task2_cancelled = task_cancelled;
+    };
+    task_queue_.add(task_1, std::placeholders::_1);
+    task_queue_.add(task_2, std::placeholders::_1);
+
+    task_queue_.run(/* do_cancel = */true);
+
+    EXPECT_TRUE(is_task1_cancelled);
+    EXPECT_TRUE(is_task2_cancelled);
+}
+
+
 }  // namespace dumpstate
 }  // namespace os
 }  // namespace android
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 3467898..67a77f6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -55,6 +55,7 @@
     MOCK_METHOD1(listServices, Vector<String16>(int));
     MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
     MOCK_METHOD1(isDeclared, bool(const String16&));
+    MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
   protected:
     MOCK_METHOD0(onAsBinder, IBinder*());
 };
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 97c057f..eb9008b 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -37,7 +37,7 @@
                 {"-b", {"Block for duration of vibration."}},
                 {"<delay>", {"In milliseconds"}},
                 {"<primitive>", {"Primitive ID."}},
-                {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+                {"<scale>", {"0.0 (inclusive) - 1.0 (inclusive)."}},
                 {"...", {"May repeat multiple times."}},
         };
         return details;
@@ -72,7 +72,7 @@
                 return USAGE;
             }
             if (auto scale = args.pop<decltype(effect.scale)>();
-                scale && *scale > 0.0 && scale <= 1.0) {
+                scale && *scale >= 0.0 && scale <= 1.0) {
                 effect.scale = *scale;
                 std::cout << "Scale: " << effect.scale << std::endl;
             } else {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 8ff4dd8..96875d5 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -17,15 +17,15 @@
         "InstalldNativeService.cpp",
         "QuotaUtils.cpp",
         "dexopt.cpp",
+        "execv_helper.cpp",
         "globals.cpp",
+        "run_dex2oat.cpp",
+        "unique_file.cpp",
         "utils.cpp",
         "utils_default.cpp",
         "view_compiler.cpp",
         ":installd_aidl",
     ],
-    header_libs: [
-        "dex2oat_headers",
-    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -103,6 +103,28 @@
 }
 
 //
+// Unit tests
+//
+
+cc_test_host {
+    name: "run_dex2oat_test",
+    test_suites: ["general-tests"],
+    clang: true,
+    srcs: [
+        "run_dex2oat_test.cpp",
+        "run_dex2oat.cpp",
+        "unique_file.cpp",
+        "execv_helper.cpp",
+    ],
+    cflags: ["-Wall", "-Werror"],
+    shared_libs: [
+        "libbase",
+        "server_configurable_flags",
+    ],
+    test_config: "run_dex2oat_test.xml",
+}
+
+//
 // Executable
 //
 
@@ -163,8 +185,7 @@
 filegroup {
     name: "installd_aidl",
     srcs: [
-        "binder/android/os/IInstalld.aidl",
-        "binder/android/os/storage/CrateMetadata.aidl",
+        "binder/**/*.aidl",
     ],
     path: "binder",
 }
@@ -205,18 +226,18 @@
 
     srcs: [
         "dexopt.cpp",
+        "execv_helper.cpp",
         "globals.cpp",
         "otapreopt.cpp",
         "otapreopt_utils.cpp",
+        "run_dex2oat.cpp",
+        "unique_file.cpp",
         "utils.cpp",
         "utils_default.cpp",
         "view_compiler.cpp",
     ],
 
-    header_libs: ["dex2oat_headers"],
-
     static_libs: [
-        "libartimagevalues",
         "libdiskusage",
         "libotapreoptparameters",
     ],
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7014c8..b821578 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -420,33 +420,6 @@
     return true;
 }
 
-binder::Status InstalldNativeService::createAppDataBatched(
-        const std::optional<std::vector<std::optional<std::string>>>& uuids,
-        const std::optional<std::vector<std::optional<std::string>>>& packageNames,
-        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
-        const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
-        int64_t* _aidl_return) {
-    ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
-    ATRACE_BEGIN("createAppDataBatched");
-    binder::Status ret;
-    for (size_t i = 0; i < uuids->size(); i++) {
-        std::optional<std::string> packageName = packageNames->at(i);
-        if (!packageName) {
-            continue;
-        }
-        ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
-                seInfos[i], targetSdkVersions[i], _aidl_return);
-        if (!ret.isOk()) {
-            ATRACE_END();
-            return ret;
-        }
-    }
-    ATRACE_END();
-    return ok();
-}
-
 binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
         const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -528,6 +501,38 @@
     return ok();
 }
 
+
+binder::Status InstalldNativeService::createAppData(
+        const android::os::CreateAppDataArgs& args,
+        android::os::CreateAppDataResult* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    int64_t ceDataInode = -1;
+    auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
+                                args.seInfo, args.targetSdkVersion, &ceDataInode);
+    _aidl_return->ceDataInode = ceDataInode;
+    _aidl_return->exceptionCode = status.exceptionCode();
+    _aidl_return->exceptionMessage = status.exceptionMessage();
+    return ok();
+}
+
+binder::Status InstalldNativeService::createAppDataBatched(
+        const std::vector<android::os::CreateAppDataArgs>& args,
+        std::vector<android::os::CreateAppDataResult>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    std::vector<android::os::CreateAppDataResult> results;
+    for (auto arg : args) {
+        android::os::CreateAppDataResult result;
+        createAppData(arg, &result);
+        results.push_back(result);
+    }
+    *_aidl_return = results;
+    return ok();
+}
+
 binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
@@ -941,6 +946,33 @@
 
     auto scope_guard = android::base::make_scope_guard(deleter);
 
+    if (storageFlags & FLAG_STORAGE_DE) {
+        auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
+        auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
+        auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
+            snapshotId, package_name);
+
+        int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+        if (rc != 0) {
+            return error(rc, "Failed to create folder " + to);
+        }
+
+        rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+        if (rc != 0) {
+            return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+        }
+
+        // Check if we have data to copy.
+        if (access(from.c_str(), F_OK) == 0) {
+          rc = copy_directory_recursive(from.c_str(), to.c_str());
+        }
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from + " to " + to);
+            clear_de_on_exit = true;
+            return res;
+        }
+    }
+
     // The app may not have any data at all, in which case it's OK to skip here.
     auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
     if (access(from_ce.c_str(), F_OK) != 0) {
@@ -966,30 +998,6 @@
         LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
     }
 
-    if (storageFlags & FLAG_STORAGE_DE) {
-        auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
-        auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
-        auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
-            snapshotId, package_name);
-
-        int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
-        if (rc != 0) {
-            return error(rc, "Failed to create folder " + to);
-        }
-
-        rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
-        if (rc != 0) {
-            return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
-        }
-
-        rc = copy_directory_recursive(from.c_str(), to.c_str());
-        if (rc != 0) {
-            res = error(rc, "Failed copying " + from + " to " + to);
-            clear_de_on_exit = true;
-            return res;
-        }
-    }
-
     if (storageFlags & FLAG_STORAGE_CE) {
         auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
         auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,15 +44,18 @@
             int32_t userSerial, int32_t flags);
     binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
             int32_t flags);
-    binder::Status createAppDataBatched(
-            const std::optional<std::vector<std::optional<std::string>>>& uuids,
-            const std::optional<std::vector<std::optional<std::string>>>& packageNames,
-            int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
-            const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
-            int64_t* _aidl_return);
+
     binder::Status createAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+
+    binder::Status createAppData(
+            const android::os::CreateAppDataArgs& args,
+            android::os::CreateAppDataResult* _aidl_return);
+    binder::Status createAppDataBatched(
+            const std::vector<android::os::CreateAppDataArgs>& args,
+            std::vector<android::os::CreateAppDataResult>* _aidl_return);
+
     binder::Status restoreconAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             const std::string& seInfo);
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 9a21104..fc745d0 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -1,6 +1,5 @@
 set noparent
 
-agampe@google.com
 calin@google.com
 jsharkey@android.com
 maco@google.com
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index c6583a1..3f0fb6d 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -15,6 +15,9 @@
     {
       "name": "installd_utils_test"
     },
+    {
+      "name": "run_dex2oat_test"
+    },
     // AdoptableHostTest moves packages, part of which is handled by installd
     {
       "name": "AdoptableHostTest"
diff --git a/libs/ui/UiConfig.cpp b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
similarity index 65%
copy from libs/ui/UiConfig.cpp
copy to cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 0ac863d..96d7faa 100644
--- a/libs/ui/UiConfig.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include <ui/UiConfig.h>
+package android.os;
 
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
-    static const char* config =
-            " [libui]";
-    configStr.append(config);
+/** {@hide} */
+parcelable CreateAppDataArgs {
+    @nullable @utf8InCpp String uuid;
+    @utf8InCpp String packageName;
+    int userId;
+    int flags;
+    int appId;
+    @utf8InCpp String seInfo;
+    int targetSdkVersion;
 }
-
-
-}; // namespace android
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
similarity index 72%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to cmds/installd/binder/android/os/CreateAppDataResult.aidl
index 10cf17c..3b8fa6b 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,5 +14,11 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+package android.os;
+
+/** {@hide} */
+parcelable CreateAppDataResult {
+    long ceDataInode;
+    int exceptionCode;
+    @utf8InCpp String exceptionMessage;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index eeda6c5..2538e22 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -21,11 +21,9 @@
     void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
     void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
 
-    long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
-            int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
-    long createAppDataBatched(in @nullable @utf8InCpp String[] uuids,
-        in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds,
-        in @utf8InCpp String[] seInfos, in int[] targetSdkVersions);
+    android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
+    android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+
     void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, int flags, int appId, @utf8InCpp String seInfo);
     void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 82be007..f583c9b 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -39,7 +39,6 @@
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
-#include <dex2oat_return_codes.h>
 #include <log/log.h>               // TODO: Move everything to base/logging.
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
@@ -50,11 +49,15 @@
 
 #include "dexopt.h"
 #include "dexopt_return_codes.h"
+#include "execv_helper.h"
 #include "globals.h"
 #include "installd_deps.h"
 #include "otapreopt_utils.h"
+#include "run_dex2oat.h"
+#include "unique_file.h"
 #include "utils.h"
 
+using android::base::Basename;
 using android::base::EndsWith;
 using android::base::GetBoolProperty;
 using android::base::GetProperty;
@@ -67,16 +70,6 @@
 namespace android {
 namespace installd {
 
-// Should minidebug info be included in compiled artifacts? Even if this value is
-// "true," usage might still be conditional to other constraints, e.g., system
-// property overrides.
-static constexpr bool kEnableMinidebugInfo = true;
-
-static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
-static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
-static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
-static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
-
 
 // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
 struct FreeDelete {
@@ -186,93 +179,6 @@
     return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
 }
 
-static std::vector<std::string> SplitBySpaces(const std::string& str) {
-    if (str.empty()) {
-        return {};
-    }
-    return android::base::Split(str, " ");
-}
-
-static const char* get_location_from_path(const char* path) {
-    static constexpr char kLocationSeparator = '/';
-    const char *location = strrchr(path, kLocationSeparator);
-    if (location == nullptr) {
-        return path;
-    } else {
-        // Skip the separator character.
-        return location + 1;
-    }
-}
-
-// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
-// need to be performed between the fork and exec.
-class ExecVHelper {
-  public:
-    // Store a placeholder for the binary name.
-    ExecVHelper() : args_(1u, std::string()) {}
-
-    void PrepareArgs(const std::string& bin) {
-        CHECK(!args_.empty());
-        CHECK(args_[0].empty());
-        args_[0] = bin;
-        // Write char* into array.
-        for (const std::string& arg : args_) {
-            argv_.push_back(arg.c_str());
-        }
-        argv_.push_back(nullptr);  // Add null terminator.
-    }
-
-    [[ noreturn ]]
-    void Exec(int exit_code) {
-        execv(argv_[0], (char * const *)&argv_[0]);
-        PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
-        exit(exit_code);
-    }
-
-    // Add an arg if it's not empty.
-    void AddArg(const std::string& arg) {
-        if (!arg.empty()) {
-            args_.push_back(arg);
-        }
-    }
-
-    // Add a runtime arg if it's not empty.
-    void AddRuntimeArg(const std::string& arg) {
-        if (!arg.empty()) {
-            args_.push_back("--runtime-arg");
-            args_.push_back(arg);
-        }
-    }
-
-  protected:
-    // Holder arrays for backing arg storage.
-    std::vector<std::string> args_;
-
-    // Argument poiners.
-    std::vector<const char*> argv_;
-};
-
-static std::string MapPropertyToArg(const std::string& property,
-                                    const std::string& format,
-                                    const std::string& default_value = "") {
-  std::string prop = GetProperty(property, default_value);
-  if (!prop.empty()) {
-    return StringPrintf(format.c_str(), prop.c_str());
-  }
-  return "";
-}
-
-static std::string MapPropertyToArgWithBackup(const std::string& property,
-                                              const std::string& backupProperty,
-                                              const std::string& format,
-                                              const std::string& default_value = "") {
-  std::string value = GetProperty(property, default_value);
-  if (!value.empty()) {
-    return StringPrintf(format.c_str(), value.c_str());
-  }
-  return MapPropertyToArg(backupProperty, format, default_value);
-}
-
 // Determines which binary we should use for execution (the debug or non-debug version).
 // e.g. dex2oatd vs dex2oat
 static const char* select_execution_binary(const char* binary, const char* debug_binary,
@@ -311,9 +217,6 @@
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
 static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
-// Location of the JIT Zygote image.
-static const char* kJitZygoteImage =
-    "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
 
 // Phenotype property name for enabling profiling the boot class path.
 static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -328,288 +231,11 @@
     return profile_boot_class_path == "true";
 }
 
-class RunDex2Oat : public ExecVHelper {
-  public:
-    RunDex2Oat(int zip_fd,
-               int oat_fd,
-               int input_vdex_fd,
-               int output_vdex_fd,
-               int image_fd,
-               const char* input_file_name,
-               const char* output_file_name,
-               int swap_fd,
-               const char* instruction_set,
-               const char* compiler_filter,
-               bool debuggable,
-               bool post_bootcomplete,
-               bool for_restore,
-               bool background_job_compile,
-               int profile_fd,
-               const char* class_loader_context,
-               const std::string& class_loader_context_fds,
-               int target_sdk_version,
-               bool enable_hidden_api_checks,
-               bool generate_compact_dex,
-               int dex_metadata_fd,
-               const char* compilation_reason) {
-        // Get the relative path to the input file.
-        const char* relative_input_file_name = get_location_from_path(input_file_name);
-
-        std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
-        std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
-
-        std::string threads_format = "-j%s";
-        std::string dex2oat_threads_arg = post_bootcomplete
-                ? (for_restore
-                    ? MapPropertyToArgWithBackup(
-                            "dalvik.vm.restore-dex2oat-threads",
-                            "dalvik.vm.dex2oat-threads",
-                            threads_format)
-                    : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
-                : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
-        std::string cpu_set_format = "--cpu-set=%s";
-        std::string dex2oat_cpu_set_arg = post_bootcomplete
-                ? (for_restore
-                    ? MapPropertyToArgWithBackup(
-                            "dalvik.vm.restore-dex2oat-cpu-set",
-                            "dalvik.vm.dex2oat-cpu-set",
-                            cpu_set_format)
-                    : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
-                : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
-
-        std::string bootclasspath;
-        char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
-        if (dex2oat_bootclasspath != nullptr) {
-            bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
-        }
-        // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
-        // BOOTCLASSPATH.
-
-        const std::string dex2oat_isa_features_key =
-                StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
-        std::string instruction_set_features_arg =
-            MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
-
-        const std::string dex2oat_isa_variant_key =
-                StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
-        std::string instruction_set_variant_arg =
-            MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
-
-        const char* dex2oat_norelocation = "-Xnorelocate";
-
-        const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
-        std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
-        ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
-
-        // If we are booting without the real /data, don't spend time compiling.
-        std::string vold_decrypt = GetProperty("vold.decrypt", "");
-        bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
-                                vold_decrypt == "1";
-
-        std::string updatable_bcp_packages =
-            MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
-                             "--updatable-bcp-packages-file=%s");
-        if (updatable_bcp_packages.empty()) {
-          // Make dex2oat fail by providing non-existent file name.
-          updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
-        }
-
-        std::string resolve_startup_string_arg =
-                MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
-                                 "--resolve-startup-const-strings=%s");
-        if (resolve_startup_string_arg.empty()) {
-          // If empty, fall back to system property.
-          resolve_startup_string_arg =
-                MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
-                                 "--resolve-startup-const-strings=%s");
-        }
-
-        const std::string image_block_size_arg =
-                MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
-                                 "--max-image-block-size=%s");
-
-        const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
-
-        std::string image_format_arg;
-        if (image_fd >= 0) {
-            image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
-        }
-
-        std::string dex2oat_large_app_threshold_arg =
-            MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
-
-
-
-        // Decide whether to use dex2oat64.
-        bool use_dex2oat64 = false;
-        // Check whether the device even supports 64-bit ABIs.
-        if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
-          use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
-        }
-        const char* dex2oat_bin = select_execution_binary(
-            (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
-            (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
-            background_job_compile);
-
-        bool generate_minidebug_info = kEnableMinidebugInfo &&
-                GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
-
-        std::string boot_image;
-        std::string use_jitzygote_image =
-            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
-                                                                 ENABLE_JITZYGOTE_IMAGE,
-                                                                 /*default_value=*/ "");
-
-        if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) {
-          boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
-        } else {
-          boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
-        }
-
-        // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
-        // use arraysize instead.
-        std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
-        std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);
-        std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
-        std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
-        std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
-        std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
-        std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
-        std::string dex2oat_compiler_filter_arg;
-        std::string dex2oat_swap_fd;
-        std::string dex2oat_image_fd;
-        std::string target_sdk_version_arg;
-        if (target_sdk_version != 0) {
-            target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
-        }
-        std::string class_loader_context_arg;
-        std::string class_loader_context_fds_arg;
-        if (class_loader_context != nullptr) {
-            class_loader_context_arg = StringPrintf("--class-loader-context=%s",
-                                                    class_loader_context);
-            if (!class_loader_context_fds.empty()) {
-                class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
-                                                            class_loader_context_fds.c_str());
-            }
-        }
-
-        if (swap_fd >= 0) {
-            dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
-        }
-        if (image_fd >= 0) {
-            dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
-        }
-
-        // Compute compiler filter.
-        bool have_dex2oat_relocation_skip_flag = false;
-        if (skip_compilation) {
-            dex2oat_compiler_filter_arg = "--compiler-filter=extract";
-            have_dex2oat_relocation_skip_flag = true;
-        } else if (compiler_filter != nullptr) {
-            dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
-        }
-
-        if (dex2oat_compiler_filter_arg.empty()) {
-            dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
-                                                           "--compiler-filter=%s");
-        }
-
-        // Check whether all apps should be compiled debuggable.
-        if (!debuggable) {
-            debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
-        }
-        std::string profile_arg;
-        if (profile_fd != -1) {
-            profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
-        }
-
-        // Get the directory of the apk to pass as a base classpath directory.
-        std::string base_dir;
-        std::string apk_dir(input_file_name);
-        unsigned long dir_index = apk_dir.rfind('/');
-        bool has_base_dir = dir_index != std::string::npos;
-        if (has_base_dir) {
-            apk_dir = apk_dir.substr(0, dir_index);
-            base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
-        }
-
-        std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
-
-        std::string compilation_reason_arg = compilation_reason == nullptr
-                ? ""
-                : std::string("--compilation-reason=") + compilation_reason;
-
-        ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
-
-        // Disable cdex if update input vdex is true since this combination of options is not
-        // supported.
-        const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
-
-        AddArg(zip_fd_arg);
-        AddArg(zip_location_arg);
-        AddArg(input_vdex_fd_arg);
-        AddArg(output_vdex_fd_arg);
-        AddArg(oat_fd_arg);
-        AddArg(oat_location_arg);
-        AddArg(instruction_set_arg);
-
-        AddArg(instruction_set_variant_arg);
-        AddArg(instruction_set_features_arg);
-
-        AddArg(boot_image);
-
-        AddRuntimeArg(bootclasspath);
-        AddRuntimeArg(dex2oat_Xms_arg);
-        AddRuntimeArg(dex2oat_Xmx_arg);
-
-        AddArg(updatable_bcp_packages);
-        AddArg(resolve_startup_string_arg);
-        AddArg(image_block_size_arg);
-        AddArg(dex2oat_compiler_filter_arg);
-        AddArg(dex2oat_threads_arg);
-        AddArg(dex2oat_cpu_set_arg);
-        AddArg(dex2oat_swap_fd);
-        AddArg(dex2oat_image_fd);
-
-        if (generate_debug_info) {
-            AddArg("--generate-debug-info");
-        }
-        if (debuggable) {
-            AddArg("--debuggable");
-        }
-        AddArg(image_format_arg);
-        AddArg(dex2oat_large_app_threshold_arg);
-
-        if (have_dex2oat_relocation_skip_flag) {
-            AddRuntimeArg(dex2oat_norelocation);
-        }
-        AddArg(profile_arg);
-        AddArg(base_dir);
-        AddArg(class_loader_context_arg);
-        AddArg(class_loader_context_fds_arg);
-        if (generate_minidebug_info) {
-            AddArg(kMinidebugDex2oatFlag);
-        }
-        if (disable_cdex) {
-            AddArg(kDisableCompactDexFlag);
-        }
-        AddRuntimeArg(target_sdk_version_arg);
-        if (enable_hidden_api_checks) {
-            AddRuntimeArg("-Xhidden-api-policy:enabled");
-        }
-
-        if (dex_metadata_fd > -1) {
-            AddArg(dex_metadata_fd_arg);
-        }
-
-        AddArg(compilation_reason_arg);
-
-        // Do not add args after dex2oat_flags, they should override others for debugging.
-        args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());
-
-        PrepareArgs(dex2oat_bin);
+static void UnlinkIgnoreResult(const std::string& path) {
+    if (unlink(path.c_str()) < 0) {
+        PLOG(ERROR) << "Failed to unlink " << path;
     }
-};
+}
 
 /*
  * Whether dexopt should use a swap file when compiling an APK.
@@ -727,6 +353,16 @@
     return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
 }
 
+static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
+        const std::string& location, bool read_write, bool is_secondary_dex) {
+    std::string profile_path = create_reference_profile_path(package_name, location,
+                                                             is_secondary_dex);
+    unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+    return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
+        clear_profile(path);
+    });
+}
+
 static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
         const std::string& location) {
     std::string profile = create_snapshot_profile_path(package_name, location);
@@ -814,6 +450,20 @@
             AddArg("--boot-image-merge");
         }
 
+        uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
+            "dalvik.vm.bgdexopt.new-classes-percent", /*default*/-1);
+        if (min_new_classes_percent_change >= 0 && min_new_classes_percent_change <= 100) {
+          AddArg("--min-new-classes-percent-change=" +
+                 std::to_string(min_new_classes_percent_change));
+        }
+
+        uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
+            "dalvik.vm.bgdexopt.new-methods-percent", /*default*/-1);
+        if (min_new_methods_percent_change >=0 && min_new_methods_percent_change <= 100) {
+          AddArg("--min-new-methods-percent-change=" +
+                 std::to_string(min_new_methods_percent_change));
+        }
+
         // Do not add after dex2oat_flags, they should override others for debugging.
         PrepareArgs(profman_bin);
     }
@@ -868,6 +518,7 @@
                   /*for_boot_image*/false);
     }
 
+    using ExecVHelper::Exec;  // To suppress -Wno-overloaded-virtual
     void Exec() {
         ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
     }
@@ -1022,7 +673,7 @@
         PLOG(ERROR) << "installd cannot open " << code_path.c_str();
         return false;
     }
-    dex_locations.push_back(get_location_from_path(code_path.c_str()));
+    dex_locations.push_back(Basename(code_path));
     apk_fds.push_back(std::move(apk_fd));
 
 
@@ -1216,118 +867,14 @@
     return true;
 }
 
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-//   Dex2oatFileWrapper file(open(...),
-//                                                   [name]() {
-//                                                       unlink(name.c_str());
-//                                                   });
-//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-//            wrapper if captured as a reference.
-//
-//   if (file.get() == -1) {
-//       // Error opening...
-//   }
-//
-//   ...
-//   if (error) {
-//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-//       // and delete the file (after the fd is closed).
-//       return -1;
-//   }
-//
-//   (Success case)
-//   file.SetCleanup(false);
-//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-//   // (leaving the file around; after the fd is closed).
-//
-class Dex2oatFileWrapper {
- public:
-    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
-    }
-
-    Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
-            : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
-
-    Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
-        value_ = other.value_;
-        cleanup_ = other.cleanup_;
-        do_cleanup_ = other.do_cleanup_;
-        auto_close_ = other.auto_close_;
-        other.release();
-    }
-
-    Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
-        value_ = other.value_;
-        cleanup_ = other.cleanup_;
-        do_cleanup_ = other.do_cleanup_;
-        auto_close_ = other.auto_close_;
-        other.release();
-        return *this;
-    }
-
-    ~Dex2oatFileWrapper() {
-        reset(-1);
-    }
-
-    int get() {
-        return value_;
-    }
-
-    void SetCleanup(bool cleanup) {
-        do_cleanup_ = cleanup;
-    }
-
-    void reset(int new_value) {
-        if (auto_close_ && value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-    }
-
-    void reset(int new_value, std::function<void ()> new_cleanup) {
-        if (auto_close_ && value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-        cleanup_ = new_cleanup;
-    }
-
-    void DisableAutoClose() {
-        auto_close_ = false;
-    }
-
- private:
-    void release() {
-        value_ = -1;
-        do_cleanup_ = false;
-        cleanup_ = nullptr;
-    }
-    int value_;
-    std::function<void ()> cleanup_;
-    bool do_cleanup_;
-    bool auto_close_;
-};
-
 // (re)Creates the app image if needed.
-Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
+UniqueFile maybe_open_app_image(const std::string& out_oat_path,
         bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
 
     const std::string image_path = create_image_filename(out_oat_path);
     if (image_path.empty()) {
         // Happens when the out_oat_path has an unknown extension.
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // In case there is a stale image, remove it now. Ignore any error.
@@ -1335,18 +882,19 @@
 
     // Not enabled, exit.
     if (!generate_app_image) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
     std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
     if (app_image_format.empty()) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
     // Recreate is true since we do not want to modify a mapped image. If the app is
     // already running and we modify the image file, it can cause crashes (b/27493510).
-    Dex2oatFileWrapper wrapper_fd(
+    UniqueFile image_file(
             open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
-            [image_path]() { unlink(image_path.c_str()); });
-    if (wrapper_fd.get() < 0) {
+            image_path,
+            UnlinkIgnoreResult);
+    if (image_file.fd() < 0) {
         // Could not create application image file. Go on since we can compile without it.
         LOG(ERROR) << "installd could not create '" << image_path
                 << "' for image file during dexopt";
@@ -1357,21 +905,21 @@
             }
         }
     } else if (!set_permissions_and_ownership(
-                wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
+                image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
-        wrapper_fd.reset(-1);
+        image_file.reset();
     }
 
-    return wrapper_fd;
+    return image_file;
 }
 
 // Creates the dexopt swap file if necessary and return its fd.
 // Returns -1 if there's no need for a swap or in case of errors.
-unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+unique_fd maybe_open_dexopt_swap_file(const std::string& out_oat_path) {
     if (!ShouldUseSwapFileForDexopt()) {
         return invalid_unique_fd();
     }
-    auto swap_file_name = std::string(out_oat_path) + ".swap";
+    auto swap_file_name = out_oat_path + ".swap";
     unique_fd swap_fd(open_output_file(
             swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600));
     if (swap_fd.get() < 0) {
@@ -1389,13 +937,13 @@
 
 // Opens the reference profiles if needed.
 // Note that the reference profile might not exist so it's OK if the fd will be -1.
-Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
+UniqueFile maybe_open_reference_profile(const std::string& pkgname,
         const std::string& dex_path, const char* profile_name, bool profile_guided,
         bool is_public, int uid, bool is_secondary_dex) {
     // If we are not profile guided compilation, or we are compiling system server
     // do not bother to open the profiles; we won't be using them.
     if (!profile_guided || (pkgname[0] == '*')) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // If this is a secondary dex path which is public do not open the profile.
@@ -1407,7 +955,7 @@
     // compiling with a public profile from the .dm file the PackageManager will
     // set is_public toghether with the profile guided compilation.
     if (is_secondary_dex && is_public) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // Open reference profile in read only mode as dex2oat does not get write permissions.
@@ -1417,33 +965,28 @@
     } else {
         if (profile_name == nullptr) {
             // This path is taken for system server re-compilation lunched from ZygoteInit.
-            return Dex2oatFileWrapper();
+            return UniqueFile();
         } else {
             location = profile_name;
         }
     }
-    unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false,
-            is_secondary_dex);
-    const auto& cleanup = [pkgname, location, is_secondary_dex]() {
-        clear_reference_profile(pkgname, location, is_secondary_dex);
-    };
-    return Dex2oatFileWrapper(ufd.release(), cleanup);
+    return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
+                                                 is_secondary_dex);
 }
 
-// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
-// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
+// out_vdex_wrapper. Returns true for success or false in case of errors.
 bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
         const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
-        bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
-        Dex2oatFileWrapper* out_vdex_wrapper_fd) {
-    CHECK(in_vdex_wrapper_fd != nullptr);
-    CHECK(out_vdex_wrapper_fd != nullptr);
+        bool profile_guided, UniqueFile* in_vdex_wrapper,
+        UniqueFile* out_vdex_wrapper) {
+    CHECK(in_vdex_wrapper != nullptr);
+    CHECK(out_vdex_wrapper != nullptr);
     // Open the existing VDEX. We do this before creating the new output VDEX, which will
     // unlink the old one.
     char in_odex_path[PKG_PATH_MAX];
     int dexopt_action = abs(dexopt_needed);
     bool is_odex_location = dexopt_needed < 0;
-    std::string in_vdex_path_str;
 
     // Infer the name of the output VDEX.
     const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
@@ -1465,7 +1008,7 @@
         } else {
             path = out_oat_path;
         }
-        in_vdex_path_str = create_vdex_filename(path);
+        std::string in_vdex_path_str = create_vdex_filename(path);
         if (in_vdex_path_str.empty()) {
             ALOGE("installd cannot compute input vdex location for '%s'\n", path);
             return false;
@@ -1483,13 +1026,15 @@
             !profile_guided;
         if (update_vdex_in_place) {
             // Open the file read-write to be able to update it.
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
-            if (in_vdex_wrapper_fd->get() == -1) {
+            in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
+                                   in_vdex_path_str);
+            if (in_vdex_wrapper->fd() == -1) {
                 // If we failed to open the file, we cannot update it in place.
                 update_vdex_in_place = false;
             }
         } else {
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+            in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
+                                   in_vdex_path_str);
         }
     }
 
@@ -1498,22 +1043,24 @@
     if (update_vdex_in_place) {
         // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
         // have bogus stale vdex files.
-        out_vdex_wrapper_fd->reset(
-              in_vdex_wrapper_fd->get(),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        out_vdex_wrapper->reset(
+              in_vdex_wrapper->fd(),
+              out_vdex_path_str,
+              UnlinkIgnoreResult);
         // Disable auto close for the in wrapper fd (it will be done when destructing the out
         // wrapper).
-        in_vdex_wrapper_fd->DisableAutoClose();
+        in_vdex_wrapper->DisableAutoClose();
     } else {
-        out_vdex_wrapper_fd->reset(
+        out_vdex_wrapper->reset(
               open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-        if (out_vdex_wrapper_fd->get() < 0) {
+              out_vdex_path_str,
+              UnlinkIgnoreResult);
+        if (out_vdex_wrapper->fd() < 0) {
             ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
             return false;
         }
     }
-    if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+    if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
             out_vdex_path_str.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
         return false;
@@ -1524,25 +1071,24 @@
 }
 
 // Opens the output oat file for the given apk.
-// If successful it stores the output path into out_oat_path and returns true.
-Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
-        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
-        char* out_oat_path) {
+UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
+        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+    char out_oat_path[PKG_PATH_MAX];
     if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
-    const std::string out_oat_path_str(out_oat_path);
-    Dex2oatFileWrapper wrapper_fd(
+    UniqueFile oat(
             open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
-            [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
-    if (wrapper_fd.get() < 0) {
+            out_oat_path,
+            UnlinkIgnoreResult);
+    if (oat.fd() < 0) {
         PLOG(ERROR) << "installd cannot open output during dexopt" <<  out_oat_path;
     } else if (!set_permissions_and_ownership(
-                wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+                oat.fd(), is_public, uid, out_oat_path, is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
-        wrapper_fd.reset(-1);
+        oat.reset();
     }
-    return wrapper_fd;
+    return oat;
 }
 
 // Creates RDONLY fds for oat and vdex files, if exist.
@@ -2149,8 +1695,8 @@
     }
 
     // Open the input file.
-    unique_fd input_fd(open(dex_path, O_RDONLY, 0));
-    if (input_fd.get() < 0) {
+    UniqueFile in_dex(open(dex_path, O_RDONLY, 0), dex_path);
+    if (in_dex.fd() < 0) {
         *error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path);
         LOG(ERROR) << *error_msg;
         return -1;
@@ -2164,19 +1710,19 @@
     }
 
     // Create the output OAT file.
-    char out_oat_path[PKG_PATH_MAX];
-    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
-            instruction_set, is_secondary_dex, out_oat_path);
-    if (out_oat_fd.get() < 0) {
+    UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+            instruction_set, is_secondary_dex);
+    if (out_oat.fd() < 0) {
         *error_msg = "Could not open out oat file.";
         return -1;
     }
 
     // Open vdex files.
-    Dex2oatFileWrapper in_vdex_fd;
-    Dex2oatFileWrapper out_vdex_fd;
-    if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,
-            is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+    UniqueFile in_vdex;
+    UniqueFile out_vdex;
+    if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
+            instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
+            &out_vdex)) {
         *error_msg = "Could not open vdex files.";
         return -1;
     }
@@ -2196,53 +1742,72 @@
     }
 
     // Create a swap file if necessary.
-    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat.path());
 
     // Open the reference profile if needed.
-    Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+    UniqueFile reference_profile = maybe_open_reference_profile(
             pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
 
-    if (reference_profile_fd.get() == -1) {
+    if (reference_profile.fd() == -1) {
         // We don't create an app image without reference profile since there is no speedup from
         // loading it in that case and instead will be a small overhead.
         generate_app_image = false;
     }
 
     // Create the app image file if needed.
-    Dex2oatFileWrapper image_fd = maybe_open_app_image(
-            out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
+    UniqueFile out_image = maybe_open_app_image(
+            out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
 
-    unique_fd dex_metadata_fd;
+    UniqueFile dex_metadata;
     if (dex_metadata_path != nullptr) {
-        dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
-        if (dex_metadata_fd.get() < 0) {
+        dex_metadata.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)),
+                           dex_metadata_path);
+        if (dex_metadata.fd() < 0) {
             PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
         }
     }
 
+    std::string jitzygote_flag = server_configurable_flags::GetServerConfigurableFlag(
+        RUNTIME_NATIVE_BOOT_NAMESPACE,
+        ENABLE_JITZYGOTE_IMAGE,
+        /*default_value=*/ "");
+    bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+
+    // Decide whether to use dex2oat64.
+    bool use_dex2oat64 = false;
+    // Check whether the device even supports 64-bit ABIs.
+    if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
+      use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
+    }
+    const char* dex2oat_bin = select_execution_binary(
+        (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
+        (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
+        background_job_compile);
+
+    auto execv_helper = std::make_unique<ExecVHelper>();
+
     LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
 
-    RunDex2Oat runner(input_fd.get(),
-                      out_oat_fd.get(),
-                      in_vdex_fd.get(),
-                      out_vdex_fd.get(),
-                      image_fd.get(),
-                      dex_path,
-                      out_oat_path,
+    RunDex2Oat runner(dex2oat_bin, execv_helper.get());
+    runner.Initialize(out_oat,
+                      out_vdex,
+                      out_image,
+                      in_dex,
+                      in_vdex,
+                      dex_metadata,
+                      reference_profile,
+                      class_loader_context,
+                      join_fds(context_input_fds),
                       swap_fd.get(),
                       instruction_set,
                       compiler_filter,
                       debuggable,
                       boot_complete,
                       for_restore,
-                      background_job_compile,
-                      reference_profile_fd.get(),
-                      class_loader_context,
-                      join_fds(context_input_fds),
                       target_sdk_version,
                       enable_hidden_api_checks,
                       generate_compact_dex,
-                      dex_metadata_fd.get(),
+                      use_jitzygote_image,
                       compilation_reason);
 
     pid_t pid = fork();
@@ -2251,8 +1816,8 @@
         drop_capabilities(uid);
 
         SetDex2OatScheduling(boot_complete);
-        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
-            PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
+        if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
+            PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
             _exit(DexoptReturnCodes::kFlock);
         }
 
@@ -2269,13 +1834,13 @@
         }
     }
 
-    update_out_oat_access_times(dex_path, out_oat_path);
+    update_out_oat_access_times(dex_path, out_oat.path().c_str());
 
     // We've been successful, don't delete output.
-    out_oat_fd.SetCleanup(false);
-    out_vdex_fd.SetCleanup(false);
-    image_fd.SetCleanup(false);
-    reference_profile_fd.SetCleanup(false);
+    out_oat.DisableCleanup();
+    out_vdex.DisableCleanup();
+    out_image.DisableCleanup();
+    reference_profile.DisableCleanup();
 
     return 0;
 }
diff --git a/cmds/installd/dexopt_return_codes.h b/cmds/installd/dexopt_return_codes.h
index bbecfa4..e5198ad 100644
--- a/cmds/installd/dexopt_return_codes.h
+++ b/cmds/installd/dexopt_return_codes.h
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <dex2oat_return_codes.h>
-
 namespace android {
 namespace installd {
 
@@ -70,48 +68,21 @@
     return nullptr;
 }
 
-inline const char* get_dex2oat_return_code_name(art::dex2oat::ReturnCode code) {
-    switch (code) {
-        case art::dex2oat::ReturnCode::kNoFailure:
-            return "dex2oat success";
-        case art::dex2oat::ReturnCode::kOther:
-            return "unspecified dex2oat error";
-        case art::dex2oat::ReturnCode::kCreateRuntime:
-            return "dex2oat failed to create a runtime";
+inline const char* get_dex2oat_return_code_name(int code) {
+    if (code == 0) {
+        return "dex2oat success";
+    } else {
+        return "dex2oat error";
     }
-    return nullptr;
 }
 
-// Get some slightly descriptive string for the return code. Handles both DexoptReturnCodes (local
-// exit codes) as well as art::dex2oat::ReturnCode.
+// Get some slightly descriptive string for the return code.
 inline const char* get_return_code_name(int code) {
-    // Try to enforce non-overlap (see comment on DexoptReturnCodes)
-    // TODO: How could switch-case checks be used to enforce completeness?
-    switch (code) {
-        case kSetGid:
-        case kSetUid:
-        case kCapSet:
-        case kFlock:
-        case kProfmanExec:
-        case kSetSchedPolicy:
-        case kSetPriority:
-        case kDex2oatExec:
-        case kInstructionSetLength:
-        case kHashValidatePath:
-        case kHashOpenPath:
-        case kHashReadDex:
-        case kHashWrite:
-            break;
-        case static_cast<int>(art::dex2oat::ReturnCode::kNoFailure):
-        case static_cast<int>(art::dex2oat::ReturnCode::kOther):
-        case static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime):
-            break;
-    }
     const char* value = get_installd_return_code_name(static_cast<DexoptReturnCodes>(code));
     if (value != nullptr) {
         return value;
     }
-    value = get_dex2oat_return_code_name(static_cast<art::dex2oat::ReturnCode>(code));
+    value = get_dex2oat_return_code_name(code);
     return value;
 }
 
diff --git a/cmds/installd/execv_helper.cpp b/cmds/installd/execv_helper.cpp
new file mode 100644
index 0000000..a2d240a
--- /dev/null
+++ b/cmds/installd/execv_helper.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "installd"
+
+#include "execv_helper.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+namespace android {
+namespace installd {
+
+// Store a placeholder for the binary name.
+ExecVHelper::ExecVHelper() : args_(1u, std::string()) {}
+
+ExecVHelper::~ExecVHelper() {}
+
+void ExecVHelper::PrepareArgs(const std::string& bin) {
+    CHECK(!args_.empty());
+    CHECK(args_[0].empty());
+    args_[0] = bin;
+    // Write char* into array.
+    for (const std::string& arg : args_) {
+        argv_.push_back(arg.c_str());
+    }
+    argv_.push_back(nullptr);  // Add null terminator.
+}
+
+void ExecVHelper::Exec(int exit_code) {
+    execv(argv_[0], (char * const *)&argv_[0]);
+    PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
+    exit(exit_code);
+}
+
+void ExecVHelper::AddArg(const std::string& arg) {
+    if (!arg.empty()) {
+        args_.push_back(arg);
+    }
+}
+
+void ExecVHelper::AddRuntimeArg(const std::string& arg) {
+    if (!arg.empty()) {
+        args_.push_back("--runtime-arg");
+        args_.push_back(arg);
+    }
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/execv_helper.h b/cmds/installd/execv_helper.h
new file mode 100644
index 0000000..9adfc0e
--- /dev/null
+++ b/cmds/installd/execv_helper.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_EXECV_HELPER_H
+#define ANDROID_INSTALLD_EXECV_HELPER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace installd {
+
+// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
+// need to be performed between the fork and exec.
+class ExecVHelper {
+  public:
+    ExecVHelper();
+    virtual ~ExecVHelper();
+
+    [[ noreturn ]]
+    virtual void Exec(int exit_code);
+
+    void PrepareArgs(const std::string& bin);
+
+    // Add an arg if it's not empty.
+    void AddArg(const std::string& arg);
+
+    // Add a runtime arg if it's not empty.
+    void AddRuntimeArg(const std::string& arg);
+
+  protected:
+    // Holder arrays for backing arg storage.
+    std::vector<std::string> args_;
+
+    // Argument poiners.
+    std::vector<const char*> argv_;
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_EXECV_HELPER_H
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 18f8268..443821c 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -31,10 +31,8 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <art_image_values.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
-#include <dex2oat_return_codes.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
@@ -96,7 +94,7 @@
 
 template<typename T>
 static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
-    return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
+    return (x & -n);
 }
 
 template<typename T>
@@ -523,6 +521,7 @@
     // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
     static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
         constexpr size_t kPageSize = PAGE_SIZE;
+        static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two");
         CHECK_EQ(min_delta % kPageSize, 0u);
         CHECK_EQ(max_delta % kPageSize, 0u);
         CHECK_LT(min_delta, max_delta);
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
new file mode 100644
index 0000000..17ea903
--- /dev/null
+++ b/cmds/installd/run_dex2oat.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "installd"
+
+#include "run_dex2oat.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+
+#include "unique_file.h"
+
+using android::base::Basename;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+namespace {
+
+// Should minidebug info be included in compiled artifacts? Even if this value is
+// "true," usage might still be conditional to other constraints, e.g., system
+// property overrides.
+static constexpr bool kEnableMinidebugInfo = true;
+
+static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
+static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
+static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
+static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
+
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+    "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
+
+std::vector<std::string> SplitBySpaces(const std::string& str) {
+    if (str.empty()) {
+        return {};
+    }
+    return android::base::Split(str, " ");
+}
+
+}  // namespace
+
+RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper)
+  : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {}
+
+void RunDex2Oat::Initialize(const UniqueFile& output_oat,
+                            const UniqueFile& output_vdex,
+                            const UniqueFile& output_image,
+                            const UniqueFile& input_dex,
+                            const UniqueFile& input_vdex,
+                            const UniqueFile& dex_metadata,
+                            const UniqueFile& profile,
+                            const char* class_loader_context,
+                            const std::string& class_loader_context_fds,
+                            int swap_fd,
+                            const char* instruction_set,
+                            const char* compiler_filter,
+                            bool debuggable,
+                            bool post_bootcomplete,
+                            bool for_restore,
+                            int target_sdk_version,
+                            bool enable_hidden_api_checks,
+                            bool generate_compact_dex,
+                            bool use_jitzygote_image,
+                            const char* compilation_reason) {
+    PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+
+    PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
+                          dex_metadata, profile, swap_fd, class_loader_context,
+                          class_loader_context_fds);
+
+    PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter,
+                               debuggable, target_sdk_version, enable_hidden_api_checks,
+                               generate_compact_dex, compilation_reason);
+
+    PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
+
+    const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
+    std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
+    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
+
+    // Do not add args after dex2oat_flags, they should override others for debugging.
+    for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) {
+        AddArg(*it);
+    }
+
+    execv_helper_->PrepareArgs(dex2oat_bin_);
+}
+
+RunDex2Oat::~RunDex2Oat() {}
+
+void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+    std::string boot_image;
+    if (use_jitzygote_image) {
+        boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+    } else {
+        boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+    }
+    AddArg(boot_image);
+
+    // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
+    // BOOTCLASSPATH.
+    char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
+    if (dex2oat_bootclasspath != nullptr) {
+        AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
+    }
+
+    std::string updatable_bcp_packages =
+            MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
+                             "--updatable-bcp-packages-file=%s");
+    if (updatable_bcp_packages.empty()) {
+        // Make dex2oat fail by providing non-existent file name.
+        updatable_bcp_packages =
+                "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
+    }
+    AddArg(updatable_bcp_packages);
+}
+
+void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
+                                       const UniqueFile& output_vdex,
+                                       const UniqueFile& output_image,
+                                       const UniqueFile& input_dex,
+                                       const UniqueFile& input_vdex,
+                                       const UniqueFile& dex_metadata,
+                                       const UniqueFile& profile,
+                                       int swap_fd,
+                                       const char* class_loader_context,
+                                       const std::string& class_loader_context_fds) {
+    std::string input_basename = Basename(input_dex.path());
+    LOG(VERBOSE) << "Running " << dex2oat_bin_ << " in=" << input_basename << " out="
+                 << output_oat.path();
+
+    AddArg(StringPrintf("--zip-fd=%d", input_dex.fd()));
+    AddArg(StringPrintf("--zip-location=%s", input_basename.c_str()));
+    AddArg(StringPrintf("--oat-fd=%d", output_oat.fd()));
+    AddArg(StringPrintf("--oat-location=%s", output_oat.path().c_str()));
+    AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex.fd()));
+    AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex.fd()));
+
+    if (output_image.fd() >= 0) {
+        AddArg(StringPrintf("--app-image-fd=%d", output_image.fd()));
+        AddArg(MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"));
+    }
+    if (dex_metadata.fd() > -1) {
+        AddArg("--dm-fd=" + std::to_string(dex_metadata.fd()));
+    }
+    if (profile.fd() != -1) {
+        AddArg(StringPrintf("--profile-file-fd=%d", profile.fd()));
+    }
+    if (swap_fd >= 0) {
+        AddArg(StringPrintf("--swap-fd=%d", swap_fd));
+    }
+
+    // Get the directory of the apk to pass as a base classpath directory.
+    {
+        std::string apk_dir(input_dex.path());
+        size_t dir_index = apk_dir.rfind('/');
+        if (dir_index != std::string::npos) {
+            apk_dir = apk_dir.substr(0, dir_index);
+            AddArg(StringPrintf("--classpath-dir=%s", apk_dir.c_str()));
+        }
+    }
+
+    if (class_loader_context != nullptr) {
+        AddArg(StringPrintf("--class-loader-context=%s", class_loader_context));
+        if (!class_loader_context_fds.empty()) {
+            AddArg(StringPrintf("--class-loader-context-fds=%s",
+                                class_loader_context_fds.c_str()));
+        }
+    }
+}
+
+void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+                                            const UniqueFile& output_vdex,
+                                            const char* instruction_set,
+                                            const char* compiler_filter,
+                                            bool debuggable,
+                                            int target_sdk_version,
+                                            bool enable_hidden_api_checks,
+                                            bool generate_compact_dex,
+                                            const char* compilation_reason) {
+    // Disable cdex if update input vdex is true since this combination of options is not
+    // supported.
+    const bool disable_cdex = !generate_compact_dex || (input_vdex.fd() == output_vdex.fd());
+    if (disable_cdex) {
+        AddArg(kDisableCompactDexFlag);
+    }
+
+    // ISA related
+    {
+        AddArg(StringPrintf("--instruction-set=%s", instruction_set));
+
+        const std::string dex2oat_isa_features_key =
+                StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
+        std::string instruction_set_features_arg =
+                MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
+        AddArg(instruction_set_features_arg);
+
+        const std::string dex2oat_isa_variant_key =
+                StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
+        std::string instruction_set_variant_arg =
+                MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
+        AddArg(instruction_set_variant_arg);
+    }
+
+    // Compute compiler filter.
+    {
+        std::string dex2oat_compiler_filter_arg;
+        {
+            // If we are booting without the real /data, don't spend time compiling.
+            std::string vold_decrypt = GetProperty("vold.decrypt", "");
+            bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
+                    vold_decrypt == "1";
+
+            bool have_dex2oat_relocation_skip_flag = false;
+            if (skip_compilation) {
+                dex2oat_compiler_filter_arg = "--compiler-filter=extract";
+                have_dex2oat_relocation_skip_flag = true;
+            } else if (compiler_filter != nullptr) {
+                dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
+                                                           compiler_filter);
+            }
+            if (have_dex2oat_relocation_skip_flag) {
+                AddRuntimeArg("-Xnorelocate");
+            }
+        }
+
+        if (dex2oat_compiler_filter_arg.empty()) {
+            dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
+                                                           "--compiler-filter=%s");
+        }
+        AddArg(dex2oat_compiler_filter_arg);
+
+        if (compilation_reason != nullptr) {
+            AddArg(std::string("--compilation-reason=") + compilation_reason);
+        }
+    }
+
+    AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+                            "--max-image-block-size=%s"));
+
+    AddArg(MapPropertyToArg("dalvik.vm.dex2oat-very-large",
+                            "--very-large-app-threshold=%s"));
+
+    std::string resolve_startup_string_arg = MapPropertyToArg(
+        "persist.device_config.runtime.dex2oat_resolve_startup_strings",
+        "--resolve-startup-const-strings=%s");
+    if (resolve_startup_string_arg.empty()) {
+        // If empty, fall back to system property.
+        resolve_startup_string_arg =
+                MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
+                                 "--resolve-startup-const-strings=%s");
+    }
+    AddArg(resolve_startup_string_arg);
+
+    // Debug related
+    {
+        // Check whether all apps should be compiled debuggable.
+        if (!debuggable) {
+            debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
+        }
+        if (debuggable) {
+            AddArg("--debuggable");
+        }
+
+        const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
+        if (generate_debug_info) {
+            AddArg("--generate-debug-info");
+        }
+        {
+            bool generate_minidebug_info = kEnableMinidebugInfo &&
+                    GetBoolProperty(kMinidebugInfoSystemProperty,
+                                    kMinidebugInfoSystemPropertyDefault);
+            if (generate_minidebug_info) {
+                AddArg(kMinidebugDex2oatFlag);
+            }
+        }
+    }
+
+    if (target_sdk_version != 0) {
+        AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version));
+    }
+
+    if (enable_hidden_api_checks) {
+        AddRuntimeArg("-Xhidden-api-policy:enabled");
+    }
+}
+
+void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+                                                          bool for_restore) {
+    // CPU set
+    {
+        std::string cpu_set_format = "--cpu-set=%s";
+        std::string dex2oat_cpu_set_arg = post_bootcomplete
+                ? (for_restore
+                   ? MapPropertyToArgWithBackup(
+                           "dalvik.vm.restore-dex2oat-cpu-set",
+                           "dalvik.vm.dex2oat-cpu-set",
+                           cpu_set_format)
+                   : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
+                : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
+        AddArg(dex2oat_cpu_set_arg);
+    }
+
+    // Number of threads
+    {
+        std::string threads_format = "-j%s";
+        std::string dex2oat_threads_arg = post_bootcomplete
+                ? (for_restore
+                   ? MapPropertyToArgWithBackup(
+                           "dalvik.vm.restore-dex2oat-threads",
+                           "dalvik.vm.dex2oat-threads",
+                           threads_format)
+                   : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
+                : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
+        AddArg(dex2oat_threads_arg);
+    }
+
+    AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"));
+    AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"));
+}
+
+void RunDex2Oat::Exec(int exit_code) {
+    execv_helper_->Exec(exit_code);
+}
+
+void RunDex2Oat::AddArg(const std::string& arg) {
+    execv_helper_->AddArg(arg);
+}
+
+void RunDex2Oat::AddRuntimeArg(const std::string& arg) {
+    execv_helper_->AddRuntimeArg(arg);
+}
+
+std::string RunDex2Oat::GetProperty(const std::string& key,
+                                    const std::string& default_value) {
+    return android::base::GetProperty(key, default_value);
+}
+
+bool RunDex2Oat::GetBoolProperty(const std::string& key, bool default_value) {
+    return android::base::GetBoolProperty(key, default_value);
+}
+
+std::string RunDex2Oat::MapPropertyToArg(const std::string& property,
+                                         const std::string& format,
+                                         const std::string& default_value) {
+    std::string prop = GetProperty(property, default_value);
+    if (!prop.empty()) {
+        return StringPrintf(format.c_str(), prop.c_str());
+    }
+    return "";
+}
+
+std::string RunDex2Oat::MapPropertyToArgWithBackup(
+        const std::string& property,
+        const std::string& backupProperty,
+        const std::string& format,
+        const std::string& default_value) {
+    std::string value = GetProperty(property, default_value);
+    if (!value.empty()) {
+        return StringPrintf(format.c_str(), value.c_str());
+    }
+    return MapPropertyToArg(backupProperty, format, default_value);
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
new file mode 100644
index 0000000..325a3a2
--- /dev/null
+++ b/cmds/installd/run_dex2oat.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_RUN_DEX2OAT_H
+#define ANDROID_INSTALLD_RUN_DEX2OAT_H
+
+#include <memory>
+#include <string>
+
+#include "execv_helper.h"
+
+namespace android {
+namespace installd {
+
+class UniqueFile;
+
+class RunDex2Oat {
+  public:
+    explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
+    virtual ~RunDex2Oat();
+
+    void Initialize(const UniqueFile& output_oat,
+                    const UniqueFile& output_vdex,
+                    const UniqueFile& output_image,
+                    const UniqueFile& input_dex,
+                    const UniqueFile& input_vdex,
+                    const UniqueFile& dex_metadata,
+                    const UniqueFile& profile,
+                    const char* class_loader_context,
+                    const std::string& class_loader_context_fds,
+                    int swap_fd,
+                    const char* instruction_set,
+                    const char* compiler_filter,
+                    bool debuggable,
+                    bool post_bootcomplete,
+                    bool for_restore,
+                    int target_sdk_version,
+                    bool enable_hidden_api_checks,
+                    bool generate_compact_dex,
+                    bool use_jitzygote_image,
+                    const char* compilation_reason);
+
+    void Exec(int exit_code);
+
+  protected:
+    void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+    void PrepareInputFileFlags(const UniqueFile& output_oat,
+                               const UniqueFile& output_vdex,
+                               const UniqueFile& output_image,
+                               const UniqueFile& input_dex,
+                               const UniqueFile& input_vdex,
+                               const UniqueFile& dex_metadata,
+                               const UniqueFile& profile,
+                               int swap_fd,
+                               const char* class_loader_context,
+                               const std::string& class_loader_context_fds);
+    void PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+                                    const UniqueFile& output_vdex,
+                                    const char* instruction_set,
+                                    const char* compiler_filter,
+                                    bool debuggable,
+                                    int target_sdk_version,
+                                    bool enable_hidden_api_checks,
+                                    bool generate_compact_dex,
+                                    const char* compilation_reason);
+    void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+
+    virtual std::string GetProperty(const std::string& key, const std::string& default_value);
+    virtual bool GetBoolProperty(const std::string& key, bool default_value);
+
+  private:
+    void AddArg(const std::string& arg);
+    void AddRuntimeArg(const std::string& arg);
+
+    std::string MapPropertyToArg(const std::string& property,
+                                 const std::string& format,
+                                 const std::string& default_value = "");
+
+    std::string MapPropertyToArgWithBackup(const std::string& property,
+                                           const std::string& backupProperty,
+                                           const std::string& format,
+                                           const std::string& default_value = "");
+
+    const std::string dex2oat_bin_;
+    ExecVHelper* execv_helper_;  // not owned
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_RUN_DEX2OAT_H
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
new file mode 100644
index 0000000..3813cf7
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include <gtest/gtest.h>
+
+#include "execv_helper.h"
+#include "run_dex2oat.h"
+#include "unique_file.h"
+
+namespace android {
+namespace installd {
+
+class RunDex2OatTest : public testing::Test {
+  public:
+    static constexpr const char* INPUT_PATH = "/dir/input/basename.apk";
+    static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat";
+    static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}";
+
+    // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest
+    // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2).
+    static constexpr int ZIP_FD = 3;
+    static constexpr int OAT_FD = 4;
+    static constexpr int INPUT_VDEX_FD = 5;
+    static constexpr int OUTPUT_VDEX_FD = 6;
+    static constexpr int IMAGE_FD = 7;
+    static constexpr int PROFILE_FD = 8;
+    static constexpr int DEX_METADATA_FD = 9;
+    static constexpr int SWAP_FD = 10;
+
+    using FakeSystemProperties = std::map<std::string, std::string>;
+
+    // A fake RunDex2Oat that allows to override (fake) system properties and starts with none.
+    class FakeRunDex2Oat : public RunDex2Oat {
+      private:
+        static constexpr const char* TRUE_STR = "true";
+        static constexpr const char* FALSE_STR = "false";
+
+      public:
+        FakeRunDex2Oat(ExecVHelper* execv_helper, FakeSystemProperties* properties)
+          : RunDex2Oat("/dir/bin/dex2oat", execv_helper), properties_(properties) { }
+
+        virtual ~FakeRunDex2Oat() {}
+
+        virtual std::string GetProperty(const std::string& key,
+                                        const std::string& default_value) override {
+            if (!properties_) {
+                return default_value;
+            }
+            auto iter = properties_->find(key);
+            if (iter == properties_->end()) {
+                return default_value;
+            }
+            return iter->second;
+        }
+
+        virtual bool GetBoolProperty(const std::string& key, bool default_value) override {
+            std::string value = GetProperty(key, "");
+            if (value == "") {
+                return default_value;
+            }
+            return value == TRUE_STR;
+        }
+
+      private:
+        FakeSystemProperties* properties_;
+    };
+
+    struct RunDex2OatArgs {
+        static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() {
+            auto args = std::make_unique<RunDex2OatArgs>();
+            args->input_dex.reset(ZIP_FD, INPUT_PATH);
+            args->output_oat.reset(OAT_FD, OUTPUT_PATH);
+            args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+            args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH");
+            args->instruction_set = "arm64";
+            args->compilation_reason = "rundex2oattest";
+            return args;
+        }
+
+        UniqueFile output_oat;
+        UniqueFile output_vdex;
+        UniqueFile output_image;
+        UniqueFile input_dex;
+        UniqueFile input_vdex;
+        UniqueFile dex_metadata;
+        UniqueFile profile;
+        int swap_fd = -1;
+        const char* instruction_set = nullptr;
+        const char* compiler_filter = "extract";
+        bool debuggable = false;
+        bool post_bootcomplete = false;
+        bool for_restore = false;
+        const char* class_loader_context = nullptr;
+        std::string class_loader_context_fds;
+        int target_sdk_version = 0;
+        bool enable_hidden_api_checks = false;
+        bool generate_compact_dex = true;
+        bool use_jitzygote_image = false;
+        const char* compilation_reason = nullptr;
+    };
+
+    class FakeExecVHelper : public ExecVHelper {
+      public:
+        bool HasArg(const std::string& arg) const {
+            auto end = argv_.end() - 1;  // To exclude the terminating nullptr
+            return find(argv_.begin(), end, arg) != end;
+        }
+
+        bool FlagNotUsed(const std::string& flag) const {
+            auto has_prefix = [flag](const char* arg) {
+                return strncmp(arg, flag.c_str(), flag.size()) == 0;
+            };
+            auto end = argv_.end() - 1;  // To exclude the terminating nullptr
+            return find_if(argv_.begin(), end, has_prefix) == end;
+        }
+
+        virtual void Exec(int exit_code) override {
+            std::string cmd;
+            for (auto arg : argv_) {
+                if (arg == nullptr) {
+                  continue;
+                }
+                cmd += arg;
+                cmd += " ";
+            }
+            LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n";
+        }
+    };
+
+    virtual void SetUp() override {
+        execv_helper_.reset(new FakeExecVHelper());
+        system_properties_.clear();
+        initializeDefaultExpectedFlags();
+    }
+
+    // Initializes the default flags expected to a run.  It currently matches to the expected flags
+    // with RunDex2OatArgs::MakeDefaultTestArgs.
+    //
+    // default_expected_flags_ defines a mapping of <flag_name, expected_value>, where flag_name is
+    // something like "--flag-name", and expected_value can be "=value" or ":value" (depending on
+    // its delimiter), "" (if no value is needed), or a special value of FLAG_UNUSED to indicates
+    // that it should not be used.
+    void initializeDefaultExpectedFlags() {
+        default_expected_flags_.clear();
+
+        // Files
+        default_expected_flags_["--zip-fd"] = "=" + std::to_string(ZIP_FD);
+        default_expected_flags_["--zip-location"] = "=basename.apk";
+        default_expected_flags_["--oat-fd"] = "=" + std::to_string(OAT_FD);
+        default_expected_flags_["--oat-location"] = "=" + std::string(OUTPUT_PATH);
+        default_expected_flags_["--input-vdex-fd"] = "=" + std::to_string(INPUT_VDEX_FD);
+        default_expected_flags_["--output-vdex-fd"] = "=" + std::to_string(OUTPUT_VDEX_FD);
+        default_expected_flags_["--classpath-dir"] = "=/dir/input";
+        default_expected_flags_["--app-image-fd"] = FLAG_UNUSED;
+        default_expected_flags_["--profile-file-fd"] = FLAG_UNUSED;
+        default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
+        default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
+        default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
+        default_expected_flags_["--updatable-bcp-packages-file"] =
+                "=/nonx/updatable-bcp-packages.txt";
+
+        // Arch
+        default_expected_flags_["--instruction-set"] = "=arm64";
+        default_expected_flags_["--instruction-set-features"] = FLAG_UNUSED;
+        default_expected_flags_["--instruction-set-variant"] = FLAG_UNUSED;
+        default_expected_flags_["--cpu-set"] = FLAG_UNUSED;
+
+        // Misc
+        default_expected_flags_["--compiler-filter"] = "=extract";
+        default_expected_flags_["--compilation-reason"] = "=rundex2oattest";
+        default_expected_flags_["--compact-dex-level"] = FLAG_UNUSED;
+        default_expected_flags_["-j"] = FLAG_UNUSED;
+        default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED;
+        default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED;
+        default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED;
+
+        // Debug
+        default_expected_flags_["--debuggable"] = FLAG_UNUSED;
+        default_expected_flags_["--generate-debug-info"] = FLAG_UNUSED;
+        default_expected_flags_["--generate-mini-debug-info"] = FLAG_UNUSED;
+
+        // Runtime
+        // TODO(victorhsieh): Check if the previous flag is actually --runtime-arg.
+        default_expected_flags_["-Xms"] = FLAG_UNUSED;
+        default_expected_flags_["-Xmx"] = FLAG_UNUSED;
+        default_expected_flags_["-Xbootclasspath"] = FLAG_UNUSED;
+        default_expected_flags_["-Xtarget-sdk-version"] = FLAG_UNUSED;
+        default_expected_flags_["-Xhidden-api-policy"] = FLAG_UNUSED;
+        default_expected_flags_["-Xnorelocate"] = FLAG_UNUSED;
+
+        // Test only
+        default_expected_flags_["--foo"] = FLAG_UNUSED;
+        default_expected_flags_["--bar"] = FLAG_UNUSED;
+        default_expected_flags_["--baz"] = FLAG_UNUSED;
+    }
+
+    void SetExpectedFlagUsed(const std::string& flag, const std::string& value) {
+        auto iter = default_expected_flags_.find(flag);
+        ASSERT_NE(iter, default_expected_flags_.end()) << "Must define the default value";
+        iter->second = value;
+    }
+
+    void VerifyExpectedFlags() {
+        for (auto const& [flag, value] : default_expected_flags_) {
+            if (value == FLAG_UNUSED) {
+                EXPECT_TRUE(execv_helper_->FlagNotUsed(flag))
+                    << "Flag " << flag << " should be unused, but got the value " << value;
+            } else if (value == "") {
+                EXPECT_TRUE(execv_helper_->HasArg(flag))
+                    << "Flag " << flag << " should be specified without value, but got " << value;
+            } else {
+                EXPECT_TRUE(execv_helper_->HasArg(flag + value))
+                    << "Flag " << flag << value << " is not specificed";
+            }
+        }
+    }
+
+    void setSystemProperty(const std::string& key, const std::string& value) {
+        system_properties_[key] = value;
+    }
+
+    void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) {
+        FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_);
+        runner.Initialize(args->output_oat,
+                          args->output_vdex,
+                          args->output_image,
+                          args->input_dex,
+                          args->input_vdex,
+                          args->dex_metadata,
+                          args->profile,
+                          args->class_loader_context,
+                          args->class_loader_context_fds,
+                          args->swap_fd,
+                          args->instruction_set,
+                          args->compiler_filter,
+                          args->debuggable,
+                          args->post_bootcomplete,
+                          args->for_restore,
+                          args->target_sdk_version,
+                          args->enable_hidden_api_checks,
+                          args->generate_compact_dex,
+                          args->use_jitzygote_image,
+                          args->compilation_reason);
+        runner.Exec(/*exit_code=*/ 0);
+    }
+
+  private:
+    std::unique_ptr<FakeExecVHelper> execv_helper_;
+    std::map<std::string, std::string> default_expected_flags_;
+    FakeSystemProperties system_properties_;
+};
+
+TEST_F(RunDex2OatTest, BasicInputOutput) {
+    auto execv_helper = std::make_unique<FakeExecVHelper>();
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithAllOtherInputFds) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->output_image.reset(IMAGE_FD, "UNUSED_PATH");
+    args->profile.reset(PROFILE_FD, "UNUSED_PATH");
+    args->swap_fd = SWAP_FD;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--app-image-fd", "=" + std::to_string(IMAGE_FD));
+    SetExpectedFlagUsed("--profile-file-fd", "=" + std::to_string(PROFILE_FD));
+    SetExpectedFlagUsed("--swap-fd", "=" + std::to_string(SWAP_FD));
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContext) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->class_loader_context = "CLASS_LOADER_CONTEXT";
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+    SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContextAndFds) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->class_loader_context = "CLASS_LOADER_CONTEXT";
+    args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+    SetExpectedFlagUsed("--class-loader-context-fds", "=CLASS_LOADER_CONTEXT_FDS");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--class-loader-context", FLAG_UNUSED);
+    SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
+    ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
+    ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
+        << "Failed to setenv: " << strerror(errno);
+
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
+    VerifyExpectedFlags();
+
+    ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
+        << "Failed to setenv: " << strerror(errno);
+}
+
+TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
+    setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->generate_compact_dex = false;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--compact-dex-level", "=none");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->generate_compact_dex = true;
+    args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+    args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--compact-dex-level", "=none");
+    SetExpectedFlagUsed("--output-vdex-fd", "=" + std::to_string(INPUT_VDEX_FD));
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ISA) {
+    setSystemProperty("dalvik.vm.isa.x86.features", "a-x86-feature");
+    setSystemProperty("dalvik.vm.isa.x86.variant", "a-x86-variant");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->instruction_set = "x86";
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--instruction-set", "=x86");
+    SetExpectedFlagUsed("--instruction-set-features", "=a-x86-feature");
+    SetExpectedFlagUsed("--instruction-set-variant", "=a-x86-variant");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPreBootComplete) {
+    setSystemProperty("dalvik.vm.boot-dex2oat-cpu-set", "1,2");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = false;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--cpu-set", "=1,2");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) {
+    setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = false;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--cpu-set", "=1,2");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) {
+    setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2");
+    setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--cpu-set", "=1,2");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore_Backup) {
+    setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "");
+    setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--cpu-set", "=1,2");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Runtime) {
+    setSystemProperty("dalvik.vm.dex2oat-Xms", "1234m");
+    setSystemProperty("dalvik.vm.dex2oat-Xmx", "5678m");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->target_sdk_version = 30;
+    args->enable_hidden_api_checks = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-Xms", "1234m");
+    SetExpectedFlagUsed("-Xmx", "5678m");
+    SetExpectedFlagUsed("-Xtarget-sdk-version", ":30");
+    SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+    SetExpectedFlagUsed("-Xnorelocate", FLAG_UNUSED);
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
+    setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--compiler-filter", "=extract");
+    SetExpectedFlagUsed("-Xnorelocate", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
+    setSystemProperty("vold.decrypt", "1");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--compiler-filter", "=extract");
+    SetExpectedFlagUsed("-Xnorelocate", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
+    setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->compiler_filter = nullptr;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--compiler-filter", "=speed");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartings) {
+    setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--resolve-startup-const-strings", "=false");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartingsOverride) {
+    setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+    setSystemProperty("persist.device_config.runtime.dex2oat_resolve_startup_strings", "true");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--resolve-startup-const-strings", "=true");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPreBootComplete) {
+    setSystemProperty("dalvik.vm.boot-dex2oat-threads", "2");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = false;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-j", "2");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) {
+    setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = false;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-j", "3");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) {
+    setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4");
+    setSystemProperty("dalvik.vm.dex2oat-threads", "5");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-j", "4");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore_Backup) {
+    setSystemProperty("dalvik.vm.restore-dex2oat-threads", "");
+    setSystemProperty("dalvik.vm.dex2oat-threads", "5");
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->post_bootcomplete = true;
+    args->for_restore = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-j", "5");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Debuggable) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->debuggable = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("--debuggable", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, AlwaysDebuggable) {
+    setSystemProperty("dalvik.vm.always_debuggable", "1");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--debuggable", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, GenerateDebugInfo) {
+    setSystemProperty("debug.generate-debug-info", "true");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--generate-debug-info", "");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, HiddenApiCheck) {
+    auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+    args->enable_hidden_api_checks = true;
+    CallRunDex2Oat(std::move(args));
+
+    SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Misc) {
+    setSystemProperty("dalvik.vm.dex2oat-max-image-block-size", "524288");
+    setSystemProperty("dalvik.vm.dex2oat-very-large", "100000");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--max-image-block-size", "=524288");
+    SetExpectedFlagUsed("--very-large-app-threshold", "=100000");
+    VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ExtraFlags) {
+    setSystemProperty("dalvik.vm.dex2oat-flags", "--foo=123 --bar:456 --baz");
+    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+    SetExpectedFlagUsed("--foo", "=123");
+    SetExpectedFlagUsed("--bar", ":456");
+    SetExpectedFlagUsed("--baz", "");
+    VerifyExpectedFlags();
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/run_dex2oat_test.xml b/cmds/installd/run_dex2oat_test.xml
new file mode 100644
index 0000000..2467fe4
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Unittest of run_dex2oat">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <option name="null-device" value="true" />
+    <test class="com.android.tradefed.testtype.HostGTest" >
+        <option name="module-name" value="run_dex2oat_test" />
+    </test>
+</configuration>
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index bd45005..7c9e3b2 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -41,6 +41,21 @@
         "liblogwrap",
     ],
     test_config: "installd_cache_test.xml",
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "QuotaUtils.cpp",
+            ],
+            static_libs: [
+                "libarcdiskquota",
+                "arc_services_aidl",
+            ],
+            cflags: [
+                "-DUSE_ARC",
+            ],
+        },
+    },
 }
 
 cc_test {
@@ -66,6 +81,21 @@
         "liblogwrap",
     ],
     test_config: "installd_service_test.xml",
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "QuotaUtils.cpp",
+            ],
+            static_libs: [
+                "libarcdiskquota",
+                "arc_services_aidl",
+            ],
+            cflags: [
+                "-DUSE_ARC",
+            ],
+        },
+    },
 }
 
 cc_test {
@@ -93,6 +123,21 @@
         "libz",
     ],
     test_config: "installd_dexopt_test.xml",
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "QuotaUtils.cpp",
+            ],
+            static_libs: [
+                "libarcdiskquota",
+                "arc_services_aidl",
+            ],
+            cflags: [
+                "-DUSE_ARC",
+            ],
+        },
+    },
 }
 
 cc_test {
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 96f5e44..fbf1e0c 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -663,7 +663,7 @@
                           &status);
     EXPECT_STREQ(status.toString8().c_str(),
                  "Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for "
-                 "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'");
+                 "/data/app/com.installd.test.dexopt/base.jar failed: dex2oat error'");
 }
 
 TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) {
diff --git a/cmds/installd/unique_file.cpp b/cmds/installd/unique_file.cpp
new file mode 100644
index 0000000..e99ce1e
--- /dev/null
+++ b/cmds/installd/unique_file.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unique_file.h"
+
+#include <string>
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace installd {
+
+UniqueFile::UniqueFile() : UniqueFile(-1, "") {}
+
+UniqueFile::UniqueFile(int value, std::string path) : UniqueFile(value, path, nullptr) {}
+
+UniqueFile::UniqueFile(int value, std::string path, CleanUpFunction cleanup)
+        : value_(value), path_(path), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+UniqueFile::UniqueFile(UniqueFile&& other) {
+    *this = std::move(other);
+}
+
+UniqueFile::~UniqueFile() {
+    reset();
+}
+
+UniqueFile& UniqueFile::operator=(UniqueFile&& other) {
+    value_ = other.value_;
+    path_ = other.path_;
+    cleanup_ = other.cleanup_;
+    do_cleanup_ = other.do_cleanup_;
+    auto_close_ = other.auto_close_;
+    other.release();
+    return *this;
+}
+
+void UniqueFile::reset() {
+    reset(-1, "");
+}
+
+void UniqueFile::reset(int new_value, std::string path, CleanUpFunction new_cleanup) {
+    if (auto_close_ && value_ >= 0) {
+        if (close(value_) < 0) {
+            PLOG(ERROR) << "Failed to close fd " << value_ << ", with path " << path;
+        }
+    }
+    if (do_cleanup_ && cleanup_ != nullptr) {
+        cleanup_(path_);
+    }
+
+    value_ = new_value;
+    path_ = path;
+    cleanup_ = new_cleanup;
+}
+
+void UniqueFile::release() {
+    value_ = -1;
+    path_ = "";
+    do_cleanup_ = false;
+    cleanup_ = nullptr;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/unique_file.h b/cmds/installd/unique_file.h
new file mode 100644
index 0000000..e85e23b
--- /dev/null
+++ b/cmds/installd/unique_file.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_UNIQUE_FILE_H
+#define ANDROID_INSTALLD_UNIQUE_FILE_H
+
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+// A file management helper that serves two purposes:
+//
+// 1. Closes the file description on destruction, similar unique_fd.
+// 2. Runs a cleanup function on after close, if not cancelled.
+//
+// The class does not assume the relationship between the given fd and file path.
+//
+// Example:
+//
+//   UniqueFile file(open(...),
+//                           filepath,
+//                           [](const std::string& path) {
+//                               unlink(path.c_str());
+//                           });
+//   if (file.fd() == -1) {
+//       // Error opening...
+//   }
+//
+//   ...
+//   if (error) {
+//       // At this point, when the UniqueFile is destructed, the cleanup function will run
+//       // (e.g. to delete the file) after the fd is closed.
+//       return -1;
+//   }
+//
+//   (Success case)
+//   file.DisableCleanup();
+//   // At this point, when the UniqueFile is destructed, the cleanup function will not run
+//   // (e.g. leaving the file around) after the fd is closed.
+//
+class UniqueFile {
+ private:
+    using CleanUpFunction = std::function<void (const std::string&)>;
+
+ public:
+    UniqueFile();
+    UniqueFile(int value, std::string path);
+    UniqueFile(int value, std::string path, CleanUpFunction cleanup);
+    UniqueFile(UniqueFile&& other);
+    ~UniqueFile();
+
+    UniqueFile& operator=(UniqueFile&& other);
+
+    int fd() const {
+        return value_;
+    }
+
+    const std::string& path() const {
+      return path_;
+    }
+
+    void DisableAutoClose() {
+        auto_close_ = false;
+    }
+
+    void DisableCleanup() {
+        do_cleanup_ = false;
+    }
+
+    void reset();
+    void reset(int new_value, std::string path, CleanUpFunction new_cleanup = nullptr);
+
+ private:
+    void release();
+
+    int value_;
+    std::string path_;
+    CleanUpFunction cleanup_;
+    bool do_cleanup_;
+    bool auto_close_;
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_UNIQUE_FILE_H
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 5afae4b..987adaf 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -75,7 +75,7 @@
     defaults: ["lshal_defaults"],
     gtest: true,
     static_libs: [
-        "android.hardware.tests.baz@1.0",
+        "android.hardware.tests.inheritance@1.0",
         "libgmock",
     ],
     shared_libs: [
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index af22ac9..72958bd 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -39,7 +39,7 @@
     // Optargs cannnot be used because the flag should not be considered set
     // if it should really be contained in mOptions.
     if (std::string(arg.argv[optind]) == "-E") {
-        mExcludesParentInstances = true;
+        mParentDebugInfoLevel = ParentDebugInfoLevel::NOTHING;
         optind++;
     }
 
@@ -67,7 +67,7 @@
 
     return mLshal.emitDebugInfo(
             pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
-            mExcludesParentInstances,
+            mParentDebugInfoLevel,
             mLshal.out().buf(),
             mLshal.err());
 }
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index cd57e31..317cc28 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -21,6 +21,7 @@
 #include <android-base/macros.h>
 
 #include "Command.h"
+#include "ParentDebugInfoLevel.h"
 #include "utils.h"
 
 namespace android {
@@ -42,9 +43,8 @@
     std::string mInterfaceName;
     std::vector<std::string> mOptions;
 
-    // Outputs the actual descriptor of a hal instead of the debug output
-    // if the arguments provided are a superclass of the actual hal impl.
-    bool mExcludesParentInstances;
+    // See comment on ParentDebugInfoLevel.
+    ParentDebugInfoLevel mParentDebugInfoLevel = ParentDebugInfoLevel::FULL;
 
     DISALLOW_COPY_AND_ASSIGN(DebugCommand);
 };
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index fb11cee..92958d9 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -555,7 +555,7 @@
                 std::stringstream ss;
                 auto pair = splitFirst(iName, '/');
                 mLshal.emitDebugInfo(pair.first, pair.second, {},
-                                     false /* excludesParentInstances */, ss,
+                                     ParentDebugInfoLevel::FQNAME_ONLY, ss,
                                      NullableOStream<std::ostream>(nullptr));
                 return ss.str();
             };
@@ -916,6 +916,18 @@
     }
 }
 
+// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST.
+// T::LAST is not included in the returned list.
+template <typename T>
+std::vector<T> GetAllValues() {
+    using BaseType = std::underlying_type_t<T>;
+    std::vector<T> ret;
+    for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) {
+        ret.push_back(static_cast<T>(i));
+    }
+    return ret;
+}
+
 void ListCommand::registerAllOptions() {
     int v = mOptions.size();
     // A list of acceptable command line options
@@ -989,6 +1001,15 @@
        "    - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
        "    - N/A: no information for passthrough HALs."});
 
+    mOptions.push_back({'A', "all", no_argument, v++,
+                        [](ListCommand* thiz, const char*) {
+                            auto allColumns = GetAllValues<TableColumnType>();
+                            thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(),
+                                                          allColumns.begin(), allColumns.end());
+                            return OK;
+                        },
+                        "print all columns"});
+
     // long options without short alternatives
     mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
         thiz->mVintf = true;
@@ -1019,46 +1040,55 @@
         thiz->mNeat = true;
         return OK;
     }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
-    mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
-        if (!arg) { return USAGE; }
+    mOptions.push_back(
+            {'\0', "types", required_argument, v++,
+             [](ListCommand* thiz, const char* arg) {
+                 if (!arg) {
+                     return USAGE;
+                 }
 
-        static const std::map<std::string, HalType> kHalTypeMap {
-            {"binderized", HalType::BINDERIZED_SERVICES},
-            {"b", HalType::BINDERIZED_SERVICES},
-            {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
-            {"c", HalType::PASSTHROUGH_CLIENTS},
-            {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
-            {"l", HalType::PASSTHROUGH_LIBRARIES},
-            {"vintf", HalType::VINTF_MANIFEST},
-            {"v", HalType::VINTF_MANIFEST},
-            {"lazy", HalType::LAZY_HALS},
-            {"z", HalType::LAZY_HALS},
-        };
+                 static const std::map<std::string, std::vector<HalType>> kHalTypeMap{
+                         {"binderized", {HalType::BINDERIZED_SERVICES}},
+                         {"b", {HalType::BINDERIZED_SERVICES}},
+                         {"passthrough_clients", {HalType::PASSTHROUGH_CLIENTS}},
+                         {"c", {HalType::PASSTHROUGH_CLIENTS}},
+                         {"passthrough_libs", {HalType::PASSTHROUGH_LIBRARIES}},
+                         {"l", {HalType::PASSTHROUGH_LIBRARIES}},
+                         {"vintf", {HalType::VINTF_MANIFEST}},
+                         {"v", {HalType::VINTF_MANIFEST}},
+                         {"lazy", {HalType::LAZY_HALS}},
+                         {"z", {HalType::LAZY_HALS}},
+                         {"all", GetAllValues<HalType>()},
+                         {"a", GetAllValues<HalType>()},
+                 };
 
-        std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
-        for (const auto& halTypeArg : halTypesArgs) {
-            if (halTypeArg.empty()) continue;
+                 std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
+                 for (const auto& halTypeArg : halTypesArgs) {
+                     if (halTypeArg.empty()) continue;
 
-            const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
-            if (halTypeIter == kHalTypeMap.end()) {
+                     const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
+                     if (halTypeIter == kHalTypeMap.end()) {
+                         thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
+                         return USAGE;
+                     }
 
-                thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
-                return USAGE;
-            }
+                     // Append unique (non-repeated) HAL types to the reporting list
+                     for (auto halType : halTypeIter->second) {
+                         if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
+                             thiz->mListTypes.end()) {
+                             thiz->mListTypes.push_back(halType);
+                         }
+                     }
+                 }
 
-            // Append unique (non-repeated) HAL types to the reporting list
-            HalType halType = halTypeIter->second;
-            if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
-                thiz->mListTypes.end()) {
-                thiz->mListTypes.push_back(halType);
-            }
-        }
-
-        if (thiz->mListTypes.empty()) { return USAGE; }
-        return OK;
-    }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
-       "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
-       "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
+                 if (thiz->mListTypes.empty()) {
+                     return USAGE;
+                 }
+                 return OK;
+             },
+             "comma-separated list of one or more sections.\nThe output is restricted to the "
+             "selected section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
+             "passthrough_libs), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."});
 }
 
 // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index acc0dcf..412aadd 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -52,6 +52,9 @@
     PASSTHROUGH_LIBRARIES,
     VINTF_MANIFEST,
     LAZY_HALS,
+
+    // Not a real HalType. Used to determine all HalTypes.
+    LAST,
 };
 
 class ListCommand : public Command {
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 132b31e..99cb93a 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -101,7 +101,7 @@
         const std::string &interfaceName,
         const std::string &instanceName,
         const std::vector<std::string> &options,
-        bool excludesParentInstances,
+        ParentDebugInfoLevel parentDebugInfoLevel,
         std::ostream &out,
         NullableOStream<std::ostream> err) const {
     using android::hidl::base::V1_0::IBase;
@@ -126,7 +126,7 @@
         return NO_INTERFACE;
     }
 
-    if (excludesParentInstances) {
+    if (parentDebugInfoLevel != ParentDebugInfoLevel::FULL) {
         const std::string descriptor = getDescriptor(base.get());
         if (descriptor.empty()) {
             std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed";
@@ -134,11 +134,14 @@
             LOG(ERROR) << msg;
         }
         if (descriptor != interfaceName) {
+            if (parentDebugInfoLevel == ParentDebugInfoLevel::FQNAME_ONLY) {
+                out << "[See " << descriptor << "/" << instanceName << "]";
+            }
             return OK;
         }
     }
 
-    PipeRelay relay(out);
+    PipeRelay relay(out, err, interfaceName, instanceName);
 
     if (relay.initCheck() != OK) {
         std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 830bd87..50279d4 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -25,6 +25,7 @@
 
 #include "Command.h"
 #include "NullableOStream.h"
+#include "ParentDebugInfoLevel.h"
 #include "utils.h"
 
 namespace android {
@@ -49,7 +50,7 @@
             const std::string &interfaceName,
             const std::string &instanceName,
             const std::vector<std::string> &options,
-            bool excludesParentInstances,
+            ParentDebugInfoLevel parentDebugInfoLevel,
             std::ostream &out,
             NullableOStream<std::ostream> err) const;
 
diff --git a/cmds/lshal/ParentDebugInfoLevel.h b/cmds/lshal/ParentDebugInfoLevel.h
new file mode 100644
index 0000000..12ac9c8
--- /dev/null
+++ b/cmds/lshal/ParentDebugInfoLevel.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace android {
+namespace lshal {
+
+// Describe verbosity when dumping debug information on a HAL service by
+// referring to a parent HAL interface FQName (for example, when dumping debug information
+// on foo@1.0::IFoo but the HAL implementation is foo@1.1::IFoo).
+enum class ParentDebugInfoLevel {
+    // Write nothing.
+    NOTHING,
+    // Write a short description that includes the FQName of the real implementation.
+    FQNAME_ONLY,
+    // Write full debug info.
+    FULL,
+};
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 820679f..4e97636 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -23,7 +23,6 @@
 
 #include <atomic>
 
-#include <android-base/logging.h>
 #include <utils/Thread.h>
 
 namespace android {
@@ -31,8 +30,15 @@
 
 static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
 
+static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
+    auto dot = interfaceName.rfind(".");
+    if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
+    return "RelayThread_" + interfaceName + "_" + instanceName;
+}
+
 struct PipeRelay::RelayThread : public Thread {
-    explicit RelayThread(int fd, std::ostream &os);
+    explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
+                         const std::string &fqName);
 
     bool threadLoop() override;
     void setFinished();
@@ -40,6 +46,7 @@
 private:
     int mFd;
     std::ostream &mOutStream;
+    NullableOStream<std::ostream> mErrStream;
 
     // If we were to use requestExit() and exitPending() instead, threadLoop()
     // may not run at all by the time ~PipeRelay is called (i.e. debug() has
@@ -47,13 +54,17 @@
     // read() are executed until data are drained.
     std::atomic_bool mFinished;
 
+    std::string mFqName;
+
     DISALLOW_COPY_AND_ASSIGN(RelayThread);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
-      : mFd(fd), mOutStream(os), mFinished(false) {}
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
+                                    const NullableOStream<std::ostream> &err,
+                                    const std::string &fqName)
+      : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
 
 bool PipeRelay::RelayThread::threadLoop() {
     char buffer[1024];
@@ -66,13 +77,14 @@
 
     int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
     if (res < 0) {
-        PLOG(INFO) << "select() failed";
+        mErrStream << "debug " << mFqName << ": select() failed";
         return false;
     }
 
     if (res == 0 || !FD_ISSET(mFd, &set)) {
         if (mFinished) {
-            LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
+            mErrStream << "debug " << mFqName
+                       << ": timeout reading from pipe, output may be truncated.";
             return false;
         }
         // timeout, but debug() has not returned, so wait for HAL to finish.
@@ -83,7 +95,7 @@
     ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
 
     if (n < 0) {
-        PLOG(ERROR) << "read() failed";
+        mErrStream << "debug " << mFqName << ": read() failed";
     }
 
     if (n <= 0) {
@@ -101,8 +113,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-PipeRelay::PipeRelay(std::ostream &os)
-    : mInitCheck(NO_INIT) {
+PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
+                     const std::string &interfaceName, const std::string &instanceName)
+      : mInitCheck(NO_INIT) {
     int res = pipe(mFds);
 
     if (res < 0) {
@@ -110,8 +123,8 @@
         return;
     }
 
-    mThread = new RelayThread(mFds[0], os);
-    mInitCheck = mThread->run("RelayThread");
+    mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
+    mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
 }
 
 void PipeRelay::CloseFd(int *fd) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 835016041..bd994b4 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -21,6 +21,8 @@
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
+#include "NullableOStream.h"
+
 namespace android {
 namespace lshal {
 
@@ -28,7 +30,10 @@
  * written to the "write"-end of the pair to the specified output stream "os".
  */
 struct PipeRelay {
-    explicit PipeRelay(std::ostream &os);
+    explicit PipeRelay(std::ostream& os,
+                       const NullableOStream<std::ostream>& err,
+                       const std::string& interfaceName,
+                       const std::string& instanceName);
     ~PipeRelay();
 
     status_t initCheck() const;
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 0ff0c96..3c36813 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -35,19 +35,25 @@
 using Pids = std::vector<int32_t>;
 
 enum class TableColumnType : unsigned int {
-    INTERFACE_NAME,
+    INTERFACE_NAME = 0,
     TRANSPORT,
     SERVER_PID,
-    SERVER_CMD,
     SERVER_ADDR,
-    CLIENT_PIDS,
-    CLIENT_CMDS,
     ARCH,
     THREADS,
     RELEASED,
     HASH,
     VINTF,
     SERVICE_STATUS,
+    CLIENT_PIDS,
+
+    // Not a real TableColumnType. Used to determine all TableColumnTypes.
+    LAST,
+
+    // Not included in all TableColumnTypes because they replace *PID(S) when the
+    // --cmdline option is set.
+    SERVER_CMD,
+    CLIENT_CMDS,
 };
 
 enum : unsigned int {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 3d550ba..ba6cdf1 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -24,7 +24,7 @@
 
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
-#include <android/hardware/tests/baz/1.0/IQuux.h>
+#include <android/hardware/tests/inheritance/1.0/IChild.h>
 #include <hidl/HidlTransportSupport.h>
 #include <vintf/parse_xml.h>
 
@@ -44,6 +44,7 @@
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
 using android::vintf::Arch;
 using android::vintf::CompatibilityMatrix;
 using android::vintf::gCompatibilityMatrixConverter;
@@ -59,10 +60,14 @@
 namespace android {
 namespace hardware {
 namespace tests {
-namespace baz {
+namespace inheritance {
 namespace V1_0 {
 namespace implementation {
-struct Quux : android::hardware::tests::baz::V1_0::IQuux {
+struct Child : android::hardware::tests::inheritance::V1_0::IChild {
+    ::android::hardware::Return<void> doChild() override { return Void(); }
+    ::android::hardware::Return<void> doParent() override { return Void(); }
+    ::android::hardware::Return<void> doGrandparent() override { return Void(); }
+
     ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
         const native_handle_t *handle = hh.getNativeHandle();
         if (handle->numFds < 1) {
@@ -76,7 +81,7 @@
         }
         ssize_t written = write(fd, content.c_str(), content.size());
         if (written != (ssize_t)content.size()) {
-            LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
+            LOG(WARNING) << "SERVER(Child) debug writes " << written << " bytes < "
                     << content.size() << " bytes, errno = " << errno;
         }
         return Void();
@@ -85,7 +90,7 @@
 
 } // namespace implementation
 } // namespace V1_0
-} // namespace baz
+} // namespace inheritance
 } // namespace tests
 } // namespace hardware
 
@@ -124,18 +129,24 @@
 class DebugTest : public ::testing::Test {
 public:
     void SetUp() override {
-        using ::android::hardware::tests::baz::V1_0::IQuux;
-        using ::android::hardware::tests::baz::V1_0::implementation::Quux;
+        using ::android::hardware::tests::inheritance::V1_0::IChild;
+        using ::android::hardware::tests::inheritance::V1_0::IParent;
+        using ::android::hardware::tests::inheritance::V1_0::IGrandparent;
+        using ::android::hardware::tests::inheritance::V1_0::implementation::Child;
 
         err.str("");
         out.str("");
         serviceManager = new testing::NiceMock<MockServiceManager>();
-        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
-            [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
-                if (iface == IQuux::descriptor && inst == "default")
-                    return new Quux();
-                return nullptr;
-            }));
+        ON_CALL(*serviceManager, get(_, _))
+                .WillByDefault(
+                        Invoke([](const auto& iface,
+                                  const auto& inst) -> ::android::hardware::Return<sp<IBase>> {
+                            if (inst != "default") return nullptr;
+                            if (iface == IChild::descriptor || iface == IParent::descriptor ||
+                                iface == IGrandparent::descriptor)
+                                return new Child();
+                            return nullptr;
+                        }));
 
         lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
     }
@@ -159,17 +170,17 @@
 
 TEST_F(DebugTest, Debug) {
     EXPECT_EQ(0u, callMain(lshal, {
-        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
+        "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild/default", "foo", "bar"
     }));
-    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
+    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nfoo\nbar"));
     EXPECT_THAT(err.str(), IsEmpty());
 }
 
 TEST_F(DebugTest, Debug2) {
     EXPECT_EQ(0u, callMain(lshal, {
-        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
+        "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild", "baz", "quux"
     }));
-    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
+    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nbaz\nquux"));
     EXPECT_THAT(err.str(), IsEmpty());
 }
 
@@ -180,6 +191,22 @@
     EXPECT_THAT(err.str(), HasSubstr("does not exist"));
 }
 
+TEST_F(DebugTest, DebugParent) {
+    EXPECT_EQ(0u, callMain(lshal, {
+        "lshal", "debug", "android.hardware.tests.inheritance@1.0::IParent", "calling parent"
+    }));
+    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\ncalling parent"));
+    EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(DebugTest, DebugParentExclude) {
+    EXPECT_EQ(0u, callMain(lshal, {
+        "lshal", "debug", "-E", "android.hardware.tests.inheritance@1.0::IParent", "excluding"
+    }));
+    EXPECT_THAT(out.str(), IsEmpty());
+    EXPECT_THAT(err.str(), IsEmpty());
+}
+
 class MockLshal : public Lshal {
 public:
     MockLshal() {}
@@ -452,8 +479,7 @@
 }
 
 TEST_F(ListTest, DumpVintf) {
-    const std::string expected = "<manifest version=\"2.0\" type=\"device\">\n"
-                                 "    <hal format=\"hidl\">\n"
+    const std::string expected = "    <hal format=\"hidl\">\n"
                                  "        <name>a.h.foo1</name>\n"
                                  "        <transport>hwbinder</transport>\n"
                                  "        <fqname>@1.0::IFoo/1</fqname>\n"
@@ -472,8 +498,7 @@
                                  "        <name>a.h.foo4</name>\n"
                                  "        <transport arch=\"32\">passthrough</transport>\n"
                                  "        <fqname>@4.0::IFoo/4</fqname>\n"
-                                 "    </hal>\n"
-                                 "</manifest>";
+                                 "    </hal>\n";
 
     optind = 1; // mimic Lshal::parseArg()
     EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
@@ -681,8 +706,8 @@
 
 TEST_F(ListTest, UnknownHalType) {
     optind = 1; // mimic Lshal::parseArg()
-    EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"})));
-    EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
+    EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"})));
+    EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r"));
 }
 
 TEST_F(ListTest, Vintf) {
@@ -766,6 +791,156 @@
     EXPECT_EQ("", err.str());
 }
 
+TEST_F(ListTest, AllColumns) {
+    // clang-format off
+    const std::string expected =
+        "[fake description 0]\n"
+        "Interface            Transport Server PTR              Arch Thread Use R Hash                                                             VINTF Status Clients\n"
+        "a.h.foo1@1.0::IFoo/1 hwbinder  1      0000000000002711 64   11/21      N 0000000000000000000000000000000000000000000000000000000000000000 X     alive  2 4\n"
+        "a.h.foo2@2.0::IFoo/2 hwbinder  2      0000000000002712 64   12/22      Y 0202020202020202020202020202020202020202020202020202020202020202 X     alive  3 5\n"
+        "\n"
+        "[fake description 1]\n"
+        "Interface            Transport   Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+        "a.h.foo3@3.0::IFoo/3 passthrough N/A    N/A 32   N/A        ?      X     N/A    4 6\n"
+        "a.h.foo4@4.0::IFoo/4 passthrough N/A    N/A 32   N/A        ?      X     N/A    5 7\n"
+        "\n"
+        "[fake description 2]\n"
+        "Interface            Transport   Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+        "a.h.foo5@5.0::IFoo/5 passthrough N/A    N/A 32   N/A        ?      X     N/A    6 8\n"
+        "a.h.foo6@6.0::IFoo/6 passthrough N/A    N/A 32   N/A        ?      X     N/A    7 9\n"
+        "\n";
+    // clang-format on
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllColumnsWithCmd) {
+    // clang-format off
+    const std::string expected =
+        "[fake description 0]\n"
+        "Interface            Transport Server CMD     PTR              Arch Thread Use R Hash                                                             VINTF Status Clients CMD\n"
+        "a.h.foo1@1.0::IFoo/1 hwbinder  command_line_1 0000000000002711 64   11/21      N 0000000000000000000000000000000000000000000000000000000000000000 X     alive  command_line_2;command_line_4\n"
+        "a.h.foo2@2.0::IFoo/2 hwbinder  command_line_2 0000000000002712 64   12/22      Y 0202020202020202020202020202020202020202020202020202020202020202 X     alive  command_line_3;command_line_5\n"
+        "\n"
+        "[fake description 1]\n"
+        "Interface            Transport   Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+        "a.h.foo3@3.0::IFoo/3 passthrough            N/A 32   N/A        ?      X     N/A    command_line_4;command_line_6\n"
+        "a.h.foo4@4.0::IFoo/4 passthrough            N/A 32   N/A        ?      X     N/A    command_line_5;command_line_7\n"
+        "\n"
+        "[fake description 2]\n"
+        "Interface            Transport   Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+        "a.h.foo5@5.0::IFoo/5 passthrough            N/A 32   N/A        ?      X     N/A    command_line_6;command_line_8\n"
+        "a.h.foo6@6.0::IFoo/6 passthrough            N/A 32   N/A        ?      X     N/A    command_line_7;command_line_9\n"
+        "\n";
+    // clang-format on
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllSections) {
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"})));
+    using HalTypeBase = std::underlying_type_t<HalType>;
+    for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) {
+        EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]"));
+    }
+    EXPECT_THAT(out.str(),
+                Not(HasSubstr("[fake description " +
+                              std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]")));
+    EXPECT_EQ("", err.str());
+}
+
+// Fake service returned by mocked IServiceManager::get for DumpDebug.
+// The interfaceChain and getHashChain functions returns
+// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
+class InheritingService : public IBase {
+public:
+    explicit InheritingService(pid_t id) : mId(id) {}
+    android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+        cb(getInterfaceName(mId));
+        return hardware::Void();
+    }
+    android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+        std::vector<hidl_string> ret;
+        for (auto i = mId; i > 0; --i) {
+            ret.push_back(getInterfaceName(i));
+        }
+        ret.push_back(IBase::descriptor);
+        cb(ret);
+        return hardware::Void();
+    }
+    android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+        std::vector<hidl_hash> ret;
+        for (auto i = mId; i > 0; --i) {
+            ret.push_back(getHashFromId(i));
+        }
+        ret.push_back(getHashFromId(0xff));
+        cb(ret);
+        return hardware::Void();
+    }
+    android::hardware::Return<void> debug(const hidl_handle& hh,
+                                          const hidl_vec<hidl_string>&) override {
+        const native_handle_t* handle = hh.getNativeHandle();
+        if (handle->numFds < 1) {
+            return Void();
+        }
+        int fd = handle->data[0];
+        std::string content = "debug info for ";
+        content += getInterfaceName(mId);
+        ssize_t written = write(fd, content.c_str(), content.size());
+        if (written != (ssize_t)content.size()) {
+            LOG(WARNING) << "SERVER(" << descriptor << ") debug writes " << written << " bytes < "
+                         << content.size() << " bytes, errno = " << errno;
+        }
+        return Void();
+    }
+
+private:
+    pid_t mId;
+};
+
+TEST_F(ListTest, DumpDebug) {
+    size_t inheritanceLevel = 3;
+    sp<IBase> service = new InheritingService(inheritanceLevel);
+
+    EXPECT_CALL(*serviceManager, list(_)).WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+        std::vector<hidl_string> ret;
+        for (auto i = 1; i <= inheritanceLevel; ++i) {
+            ret.push_back(getInterfaceName(i) + "/default");
+        }
+        cb(ret);
+        return hardware::Void();
+    }));
+    EXPECT_CALL(*serviceManager, get(_, _))
+            .WillRepeatedly(
+                    Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
+                        int id = getIdFromInstanceName(instance);
+                        if (id > inheritanceLevel) return nullptr;
+                        return sp<IBase>(service);
+                    }));
+
+    const std::string expected = "[fake description 0]\n"
+                                 "Interface\n"
+                                 "a.h.foo1@1.0::IFoo/default\n"
+                                 "[See a.h.foo3@3.0::IFoo/default]\n"
+                                 "a.h.foo2@2.0::IFoo/default\n"
+                                 "[See a.h.foo3@3.0::IFoo/default]\n"
+                                 "a.h.foo3@3.0::IFoo/default\n"
+                                 "debug info for a.h.foo3@3.0::IFoo\n"
+                                 "\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=b", "-id"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
 class ListVintfTest : public ListTest {
 public:
     virtual void SetUp() override {
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 7277e85..b139251 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,9 @@
     cflags: [
         "-DVENDORSERVICEMANAGER=1",
     ],
+    required: [
+        "vndservice",
+    ],
     srcs: ["main.cpp"],
 }
 
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 1f9892a..7aac7da 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -37,6 +37,27 @@
 namespace android {
 
 #ifndef VENDORSERVICEMANAGER
+struct ManifestWithDescription {
+    std::shared_ptr<const vintf::HalManifest> manifest;
+    const char* description;
+};
+// func true -> stop search and forEachManifest will return true
+static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
+    for (const ManifestWithDescription& mwd : {
+            ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+            ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
+        }) {
+        if (mwd.manifest == nullptr) {
+          LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
+          // note, we explicitly do not retry here, so that we can detect VINTF
+          // or other bugs (b/151696835)
+          continue;
+        }
+        if (func(mwd)) return true;
+    }
+    return false;
+}
+
 static bool isVintfDeclared(const std::string& name) {
     size_t firstSlash = name.find('/');
     size_t lastDot = name.rfind('.', firstSlash);
@@ -49,31 +70,41 @@
     const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
     const std::string instance = name.substr(firstSlash+1);
 
-    struct ManifestWithDescription {
-        std::shared_ptr<const vintf::HalManifest> manifest;
-        const char* description;
-    };
-    for (const ManifestWithDescription& mwd : {
-            ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
-            ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
-        }) {
-        if (mwd.manifest == nullptr) {
-          LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
-          // note, we explicitly do not retry here, so that we can detect VINTF
-          // or other bugs (b/151696835)
-          continue;
-        }
+    bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
         if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
             LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
             return true;
         }
+        return false;  // continue
+    });
+
+    if (!found) {
+        // Although it is tested, explicitly rebuilding qualified name, in case it
+        // becomes something unexpected.
+        LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+                   << " in the VINTF manifest.";
     }
 
-    // Although it is tested, explicitly rebuilding qualified name, in case it
-    // becomes something unexpected.
-    LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
-               << " in the VINTF manifest.";
-    return false;
+    return found;
+}
+
+static std::vector<std::string> getVintfInstances(const std::string& interface) {
+    size_t lastDot = interface.rfind('.');
+    if (lastDot == std::string::npos) {
+        LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+        return {};
+    }
+    const std::string package = interface.substr(0, lastDot);
+    const std::string iface = interface.substr(lastDot+1);
+
+    std::vector<std::string> ret;
+    (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+        auto instances = mwd.manifest->getAidlInstances(package, iface);
+        ret.insert(ret.end(), instances.begin(), instances.end());
+        return false;  // continue
+    });
+
+    return ret;
 }
 
 static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
@@ -213,17 +244,18 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    auto entry = mNameToService.emplace(name, Service {
+    // Overwrite the old service if it exists
+    mNameToService[name] = Service {
         .binder = binder,
         .allowIsolated = allowIsolated,
         .dumpPriority = dumpPriority,
         .debugPid = ctx.debugPid,
-    });
+    };
 
     auto it = mNameToRegistrationCallback.find(name);
     if (it != mNameToRegistrationCallback.end()) {
         for (const sp<IServiceCallback>& cb : it->second) {
-            entry.first->second.guaranteeClient = true;
+            mNameToService[name].guaranteeClient = true;
             // permission checked in registerForNotifications
             cb->onRegistration(name, binder);
         }
@@ -330,6 +362,29 @@
     return Status::ok();
 }
 
+binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+    auto ctx = mAccess->getCallingContext();
+
+    std::vector<std::string> allInstances;
+#ifndef VENDORSERVICEMANAGER
+    allInstances = getVintfInstances(interface);
+#endif
+
+    outReturn->clear();
+
+    for (const std::string& instance : allInstances) {
+        if (mAccess->canFind(ctx, interface + "/" + instance)) {
+            outReturn->push_back(instance);
+        }
+    }
+
+    if (outReturn->size() == 0 && allInstances.size() != 0) {
+        return Status::fromExceptionCode(Status::EX_SECURITY);
+    }
+
+    return Status::ok();
+}
+
 void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
                                     ServiceCallbackMap::iterator* it,
                                     bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index a2fc5a8..9f43eb4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,6 +44,7 @@
                                               const sp<IServiceCallback>& callback) override;
 
     binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+    binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
     binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
                                           const sp<IClientCallback>& cb) override;
     binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2618906..b1bc6dc 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -130,7 +130,7 @@
     }
 
     IPCThreadState::self()->setTheContextObject(manager);
-    ps->becomeContextManager(nullptr, nullptr);
+    ps->becomeContextManager();
 
     sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 152ac28..6d5070f 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -3,16 +3,11 @@
     user system
     group system readproc
     critical
-    onrestart restart healthd
-    onrestart restart zygote
+    onrestart restart apexd
     onrestart restart audioserver
-    onrestart restart media
-    onrestart restart surfaceflinger
-    onrestart restart inputflinger
-    onrestart restart drm
-    onrestart restart cameraserver
-    onrestart restart keystore
     onrestart restart gatekeeperd
-    onrestart restart thermalservice
+    onrestart class_restart main
+    onrestart class_restart hal
+    onrestart class_restart early_hal
     writepid /dev/cpuset/system-background/tasks
     shutdown critical
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 25245be..fb9f9df 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -135,6 +135,26 @@
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
 }
 
+TEST(AddService, OverwriteExistingService) {
+    auto sm = getPermissiveServiceManager();
+    sp<IBinder> serviceA = getBinder();
+    EXPECT_TRUE(sm->addService("foo", serviceA, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+    sp<IBinder> outA;
+    EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+    EXPECT_EQ(serviceA, outA);
+
+    // serviceA should be overwritten by serviceB
+    sp<IBinder> serviceB = getBinder();
+    EXPECT_TRUE(sm->addService("foo", serviceB, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+    sp<IBinder> outB;
+    EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+    EXPECT_EQ(serviceB, outB);
+}
+
 TEST(AddService, NoPermissions) {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index 3fa4d7d..756f6c3 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -3,4 +3,7 @@
     user system
     group system readproc
     writepid /dev/cpuset/system-background/tasks
+    onrestart class_restart main
+    onrestart class_restart hal
+    onrestart class_restart early_hal
     shutdown critical
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
index cc4c842..32bcc83 100644
--- a/cmds/surfacereplayer/OWNERS
+++ b/cmds/surfacereplayer/OWNERS
@@ -1,2 +1 @@
-mathias@google.com
-racarr@google.com
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index b574098..c6f656b 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,8 +25,10 @@
     repeated SurfaceChange surface_change = 1;
     repeated DisplayChange display_change = 2;
 
-    required bool synchronous = 3;
-    required bool animation   = 4;
+    required bool   synchronous      = 3;
+    required bool   animation        = 4;
+    optional Origin origin           = 5;
+    optional uint64 id               = 6;
 }
 
 message SurfaceChange {
@@ -39,7 +41,6 @@
         LayerChange                 layer                   = 5;
         CropChange                  crop                    = 6;
         MatrixChange                matrix                  = 8;
-        OverrideScalingModeChange   override_scaling_mode   = 9;
         TransparentRegionHintChange transparent_region_hint = 10;
         LayerStackChange            layer_stack             = 11;
         HiddenFlagChange            hidden_flag             = 12;
@@ -53,6 +54,7 @@
         ReparentChildrenChange      reparent_children       = 20;
         BackgroundBlurRadiusChange  background_blur_radius  = 21;
         ShadowRadiusChange          shadow_radius           = 22;
+        BlurRegionsChange           blur_regions            = 23;
     }
 }
 
@@ -93,10 +95,6 @@
     required float dtdy = 4;
 }
 
-message OverrideScalingModeChange {
-    required int32 override_scaling_mode = 1;
-}
-
 message TransparentRegionHintChange {
     repeated Rectangle region = 1;
 }
@@ -208,4 +206,26 @@
 
 message ShadowRadiusChange {
     required float radius = 1;
+}
+
+message BlurRegionsChange {
+    repeated BlurRegionChange blur_regions = 1;
+}
+
+message BlurRegionChange {
+    required uint32 blur_radius = 1;
+    required float corner_radius_tl = 2;
+    required float corner_radius_tr = 3;
+    required float corner_radius_bl = 4;
+    required float corner_radius_br = 5;
+    required float alpha = 6;
+    required int32 left = 7;
+    required int32 top = 8;
+    required int32 right = 9;
+    required int32 bottom = 10;
+}
+
+message Origin {
+    required int32 pid = 1;
+    required int32 uid = 2;
 }
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 2b5667d..5849212 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -387,10 +387,6 @@
             case SurfaceChange::SurfaceChangeCase::kMatrix:
                 setMatrix(transaction, change.id(), change.matrix());
                 break;
-            case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
-                setOverrideScalingMode(transaction, change.id(),
-                        change.override_scaling_mode());
-                break;
             case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
                 setTransparentRegionHint(transaction, change.id(),
                         change.transparent_region_hint());
@@ -427,6 +423,9 @@
             case SurfaceChange::SurfaceChangeCase::kShadowRadius:
                 setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+                setBlurRegionsChange(transaction, change.id(), change.blur_regions());
+                break;
             default:
                 status = 1;
                 break;
@@ -525,12 +524,6 @@
     t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
 }
 
-void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
-        layer_id id, const OverrideScalingModeChange& osmc) {
-    ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
-    t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode());
-}
-
 void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
         layer_id id, const TransparentRegionHintChange& trhc) {
     ALOGV("Setting Transparent Region Hint");
@@ -584,9 +577,7 @@
         return;
     }
 
-    auto handle = mLayers[dtc.layer_id()]->getHandle();
-
-    t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number());
+    t.deferTransactionUntil_legacy(mLayers[id], mLayers[dtc.layer_id()], dtc.frame_number());
 }
 
 void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
@@ -706,11 +697,11 @@
 
 void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t,
         layer_id id, const ReparentChange& c) {
-    sp<IBinder> newParentHandle = nullptr;
+    sp<SurfaceControl> newSurfaceControl = nullptr;
     if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) {
-        newParentHandle = mLayers[c.parent_id()]->getHandle();
+        newSurfaceControl = mLayers[c.parent_id()];
     }
-    t.reparent(mLayers[id], newParentHandle);
+    t.reparent(mLayers[id], newSurfaceControl);
 }
 
 void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t,
@@ -719,7 +710,7 @@
         ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id());
         return;
     }
-    t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z());
+    t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
 }
 
 void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
@@ -733,10 +724,31 @@
         ALOGE("Layer %d not found in reparent children transaction", c.parent_id());
         return;
     }
-    t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
+    t.reparentChildren(mLayers[id], mLayers[c.parent_id()]);
 }
 
 void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
         layer_id id, const ShadowRadiusChange& c) {
     t.setShadowRadius(mLayers[id], c.radius());
 }
+
+void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const BlurRegionsChange& c) {
+    std::vector<BlurRegion> regions;
+    for(size_t i=0; i < c.blur_regions_size(); i++) {
+        auto protoRegion = c.blur_regions(i);
+        regions.push_back(BlurRegion{
+            .blurRadius = protoRegion.blur_radius(),
+            .alpha = protoRegion.alpha(),
+            .cornerRadiusTL = protoRegion.corner_radius_tl(),
+            .cornerRadiusTR = protoRegion.corner_radius_tr(),
+            .cornerRadiusBL = protoRegion.corner_radius_bl(),
+            .cornerRadiusBR = protoRegion.corner_radius_br(),
+            .left = protoRegion.left(),
+            .top = protoRegion.top(),
+            .right = protoRegion.right(),
+            .bottom = protoRegion.bottom()
+        });
+    }
+    t.setBlurRegions(mLayers[id], regions);
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 95857e1..a22262a 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -96,10 +96,10 @@
             layer_id id, const CornerRadiusChange& cc);
     void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
             layer_id id, const BackgroundBlurRadiusChange& cc);
+    void setBlurRegions(SurfaceComposerClient::Transaction& t,
+            layer_id id, const BlurRegionsChange& cc);
     void setMatrix(SurfaceComposerClient::Transaction& t,
             layer_id id, const MatrixChange& mc);
-    void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
-            layer_id id, const OverrideScalingModeChange& osmc);
     void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
             layer_id id, const TransparentRegionHintChange& trgc);
     void setLayerStack(SurfaceComposerClient::Transaction& t,
@@ -122,6 +122,8 @@
             layer_id id, const ReparentChildrenChange& c);
     void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
             layer_id id, const ShadowRadiusChange& c);
+    void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const BlurRegionsChange& c);
 
     void setDisplaySurface(SurfaceComposerClient::Transaction& t,
             display_id id, const DispSurfaceChange& dsc);
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
index d63d97f..58bfbf3 100644
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -69,7 +69,6 @@
     print ("5. Crop Change")
     print ("6. Final Crop Change")
     print ("7. Matrix Change")
-    print ("8. Override Scaling Mode Change")
     print ("9. Transparent Region Hint Change")
     print ("10. Layer Stack Change")
     print ("11. Hidden Flag Change")
@@ -128,9 +127,6 @@
             change.matrix.dtdx,\
             change.matrix.dsdy,\
             change.matrix.dtdy = layer()
-        elif option == 8:
-            change.override_scaling_mode.override_scaling_mode \
-                                     = override_scaling_mode()
         elif option == 9:
             for rect in transparent_region_hint():
                 new = increment.transparent_region_hint.region.add()
@@ -227,11 +223,6 @@
 
     return float(dsdx)
 
-def override_scaling_mode():
-    mode = input("Enter override scaling mode: ")
-
-    return int(mode)
-
 def transparent_region_hint():
     num = input("Enter number of rectangles in region: ")
 
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..83b6aa0
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,7 @@
+prebuilt_etc {
+    name: "android.hardware.biometrics.face.xml",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "android.hardware.biometrics.face.xml",
+    filename_from_src: true,
+}
\ No newline at end of file
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 50f117d..ccf4dc8 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,12 +38,12 @@
     <!-- basic system services -->
     <feature name="android.software.connectionservice" />
     <feature name="android.software.voice_recognizers" notLowRam="true" />
-    <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
+    <feature name="android.software.input_methods" />
 
     <!-- devices with GPS must include android.hardware.location.gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
new file mode 100644
index 0000000..5defb2f
--- /dev/null
+++ b/data/etc/cec_config.xml
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<cec-settings>
+  <setting name="hdmi_cec_enabled"
+           value-type="int"
+           user-configurable="true">
+    <allowed-values>
+      <value int-value="0" />
+      <value int-value="1" />
+    </allowed-values>
+    <default-value int-value="1" />
+  </setting>
+  <setting name="hdmi_cec_version"
+           value-type="int"
+           user-configurable="true">
+    <allowed-values>
+      <value int-value="0x05" />
+      <value int-value="0x06" />
+    </allowed-values>
+    <default-value int-value="0x05" />
+  </setting>
+  <setting name="send_standby_on_sleep"
+           value-type="string"
+           user-configurable="true">
+    <allowed-values>
+      <value string-value="to_tv" />
+      <value string-value="broadcast" />
+      <value string-value="none" />
+    </allowed-values>
+    <default-value string-value="to_tv" />
+  </setting>
+  <setting name="power_state_change_on_active_source_lost"
+           value-type="string"
+           user-configurable="false">
+    <allowed-values>
+      <value string-value="none" />
+      <value string-value="standby_now" />
+    </allowed-values>
+    <default-value string-value="none" />
+  </setting>
+  <setting name="system_audio_mode_muting"
+           value-type="int"
+           user-configurable="false">
+    <allowed-values>
+      <value int-value="0" />
+      <value int-value="1" />
+    </allowed-values>
+    <default-value int-value="1" />
+  </setting>
+</cec-settings>
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index dc37bbd..e65b224 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -321,6 +321,46 @@
     OMX_VIDEO_DolbyVisionLevelmax     = 0x7FFFFFFF
 } OMX_VIDEO_DOLBYVISIONLEVELTYPE;
 
+/** AV1 Profile enum type */
+typedef enum OMX_VIDEO_AV1PROFILETYPE {
+    OMX_VIDEO_AV1ProfileMain8           = 0x00000001,
+    OMX_VIDEO_AV1ProfileMain10          = 0x00000002,
+    OMX_VIDEO_AV1ProfileMain10HDR10     = 0x00001000,
+    OMX_VIDEO_AV1ProfileMain10HDR10Plus = 0x00002000,
+    OMX_VIDEO_AV1ProfileUnknown         = 0x6EFFFFFF,
+    OMX_VIDEO_AV1ProfileMax             = 0x7FFFFFFF
+} OMX_VIDEO_AV1PROFILETYPE;
+
+/** AV1 Level enum type */
+typedef enum OMX_VIDEO_AV1LEVELTYPE {
+    OMX_VIDEO_AV1Level2         = 0x1,
+    OMX_VIDEO_AV1Level21        = 0x2,
+    OMX_VIDEO_AV1Level22        = 0x4,
+    OMX_VIDEO_AV1Level23        = 0x8,
+    OMX_VIDEO_AV1Level3         = 0x10,
+    OMX_VIDEO_AV1Level31        = 0x20,
+    OMX_VIDEO_AV1Level32        = 0x40,
+    OMX_VIDEO_AV1Level33        = 0x80,
+    OMX_VIDEO_AV1Level4         = 0x100,
+    OMX_VIDEO_AV1Level41        = 0x200,
+    OMX_VIDEO_AV1Level42        = 0x400,
+    OMX_VIDEO_AV1Level43        = 0x800,
+    OMX_VIDEO_AV1Level5         = 0x1000,
+    OMX_VIDEO_AV1Level51        = 0x2000,
+    OMX_VIDEO_AV1Level52        = 0x4000,
+    OMX_VIDEO_AV1Level53        = 0x8000,
+    OMX_VIDEO_AV1Level6         = 0x10000,
+    OMX_VIDEO_AV1Level61        = 0x20000,
+    OMX_VIDEO_AV1Level62        = 0x40000,
+    OMX_VIDEO_AV1Level63        = 0x80000,
+    OMX_VIDEO_AV1Level7         = 0x100000,
+    OMX_VIDEO_AV1Level71        = 0x200000,
+    OMX_VIDEO_AV1Level72        = 0x400000,
+    OMX_VIDEO_AV1Level73        = 0x800000,
+    OMX_VIDEO_AV1LevelUnknown   = 0x6EFFFFFF,
+    OMX_VIDEO_AV1LevelMax       = 0x7FFFFFFF
+} OMX_VIDEO_AV1LEVELTYPE;
+
 /**
  * Structure for configuring video compression intra refresh period
  *
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index bdf11e4..e9f559c 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -130,6 +130,13 @@
  * This api is thread-safe. Any thread is allowed to register a new refresh
  * rate callback for the choreographer instance.
  *
+ * Note that in API level 30, this api is not guaranteed to be atomic with
+ * DisplayManager. That is, calling Display#getRefreshRate very soon after
+ * a refresh rate callback is invoked may return a stale refresh rate. If any
+ * Display properties would be required by this callback, then it is recommended
+ * to listen directly to DisplayManager.DisplayListener#onDisplayChanged events
+ * instead.
+ *
  * Available since API level 30.
  */
 void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
index 293e5ac..ae208a6 100644
--- a/include/android/hardware_buffer_jni.h
+++ b/include/android/hardware_buffer_jni.h
@@ -39,9 +39,9 @@
  * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object.
  *
  * This method does not acquire any additional reference to the AHardwareBuffer
- * that is returned. To keep the AHardwareBuffer live after the Java
- * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire()
- * to acquire an additional reference.
+ * that is returned. To keep the AHardwareBuffer alive after the Java
+ * HardwareBuffer object is closed, explicitly or by the garbage collector, be
+ * sure to use AHardwareBuffer_acquire() to acquire an additional reference.
  *
  * Available since API level 26.
  */
@@ -50,7 +50,18 @@
 
 /**
  * Return a new Java HardwareBuffer object that wraps the passed native
- * AHardwareBuffer object.
+ * AHardwareBuffer object. The Java HardwareBuffer will acquire a reference to
+ * the internal buffer and manage its lifetime. For example:
+ *
+ * <pre><code>
+ * AHardwareBuffer* buffer;
+ * AHardwareBuffer_allocate(..., &buffer);  // `buffer` has reference count 1
+ * jobject java_result = AHardwareBuffer_toHardwareBuffer(buffer);  // `buffer` has reference count 2.
+ * AHardwareBuffer_release(buffer); // `buffer` has reference count 1
+ * return result;  // The underlying buffer is kept alive by `java_result` and
+ *                 // will be set to 0 when it is closed on the Java side with
+ *                 // HardwareBuffer::close().
+ * </code></pre>
  *
  * Available since API level 26.
  */
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index d7e6e41..3dd1534 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -158,7 +158,8 @@
  * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
  *   supported.
  */
-int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+int AImageDecoder_createFromAAsset(struct AAsset* _Nonnull asset,
+                                   AImageDecoder* _Nonnull * _Nonnull outDecoder)
         __INTRODUCED_IN(30);
 
 /**
@@ -189,7 +190,8 @@
  * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
  *   supported.
  */
-int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nonnull * _Nonnull outDecoder)
+        __INTRODUCED_IN(30);
 
 /**
  * Create a new AImageDecoder from a buffer.
@@ -218,15 +220,16 @@
  * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
  *   supported.
  */
-int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
-                                   AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromBuffer(const void* _Nonnull buffer, size_t length,
+                                   AImageDecoder* _Nonnull * _Nonnull outDecoder)
+        __INTRODUCED_IN(30);
 
 /**
  * Delete the AImageDecoder.
  *
  * Available since API level 30.
  */
-void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
+void AImageDecoder_delete(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
 
 /**
  * Choose the desired output format.
@@ -247,7 +250,7 @@
  * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
  *   {@link AndroidBitmapFormat} is incompatible with the image.
  */
-int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* _Nonnull decoder,
         int32_t format) __INTRODUCED_IN(30);
 
 /**
@@ -270,7 +273,7 @@
  * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
  *   {@link AImageDecoder} is null.
  */
-int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*,
+int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* _Nonnull decoder,
                                              bool unpremultipliedRequired) __INTRODUCED_IN(30);
 
 /**
@@ -295,7 +298,8 @@
  *   {@link AImageDecoder} is null or |dataspace| does not correspond to an
  *   {@link ADataSpace} value.
  */
-int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
+int AImageDecoder_setDataSpace(AImageDecoder* _Nonnull decoder, int32_t dataspace)
+        __INTRODUCED_IN(30);
 
 /**
  * Specify the output size for a decoded image.
@@ -324,7 +328,8 @@
  *   or the scale is incompatible with a previous call to
  *   {@link AImageDecoder_setUnpremultipliedRequired}(true).
  */
-int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
+int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width,
+                                int32_t height) __INTRODUCED_IN(30);
 
 
 /**
@@ -353,8 +358,9 @@
  * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
  *   {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1.
  */
-int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
-                                     int32_t* width, int32_t* height) __INTRODUCED_IN(30);
+int AImageDecoder_computeSampledSize(const AImageDecoder* _Nonnull decoder, int sampleSize,
+                                     int32_t* _Nonnull width, int32_t* _Nonnull height)
+        __INTRODUCED_IN(30);
 /**
  * Specify how to crop the output after scaling (if any).
  *
@@ -380,7 +386,7 @@
  *   {@link AImageDecoder} is null or the crop is not contained by the
  *   (possibly scaled) image dimensions.
  */
-int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
+int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30);
 
 struct AImageDecoderHeaderInfo;
 /**
@@ -399,8 +405,8 @@
  *
  * Available since API level 30.
  */
-const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
-        const AImageDecoder*) __INTRODUCED_IN(30);
+const AImageDecoderHeaderInfo* _Nonnull  AImageDecoder_getHeaderInfo(
+        const AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
 
 /**
  * Report the native width of the encoded image. This is also the logical
@@ -410,7 +416,8 @@
  *
  * Available since API level 30.
  */
-int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* _Nonnull)
+        __INTRODUCED_IN(30);
 
 /**
  * Report the native height of the encoded image. This is also the logical
@@ -420,7 +427,8 @@
  *
  * Available since API level 30.
  */
-int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* _Nonnull)
+        __INTRODUCED_IN(30);
 
 /**
  * Report the mimeType of the encoded image.
@@ -429,8 +437,8 @@
  *
  * @return a string literal describing the mime type.
  */
-const char* AImageDecoderHeaderInfo_getMimeType(
-        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+const char* _Nonnull  AImageDecoderHeaderInfo_getMimeType(
+        const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
 
 /**
  * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
@@ -441,7 +449,7 @@
  * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
-        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+        const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
 
 /**
  * Report how the {@link AImageDecoder} will handle alpha by default. If the image
@@ -453,7 +461,7 @@
  * Available since API level 30.
  */
 int AImageDecoderHeaderInfo_getAlphaFlags(
-        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+        const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
 
 /**
  * Report the dataspace the AImageDecoder will decode to by default.
@@ -474,7 +482,7 @@
  *         no corresponding {@link ADataSpace}.
  */
 int32_t AImageDecoderHeaderInfo_getDataSpace(
-        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+        const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
 
 /**
  * Return the minimum stride that can be used in
@@ -489,13 +497,17 @@
  *
  * Available since API level 30.
  */
-size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
+size_t AImageDecoder_getMinimumStride(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
 
 /**
  * Decode the image into pixels, using the settings of the {@link AImageDecoder}.
  *
  * Available since API level 30.
  *
+ * Starting in API level 31, it can be used to decode all of the frames of an
+ * animated image (i.e. GIF, WebP, HEIF) using new APIs (TODO (scroggo): list
+ * and describe here).
+ *
  * @param decoder Opaque object representing the decoder.
  * @param pixels On success, will be filled with the result
  *               of the decode. Must be large enough to hold |size| bytes.
@@ -523,12 +535,64 @@
  * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
  *   failure to allocate memory.
  */
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
-                              void* pixels, size_t stride,
+int AImageDecoder_decodeImage(AImageDecoder* _Nonnull decoder,
+                              void* _Nonnull pixels, size_t stride,
                               size_t size) __INTRODUCED_IN(30);
 
 #endif // __ANDROID_API__ >= 30
 
+#if __ANDROID_API__ >= 31
+
+/**
+ * Return true iff the image is animated - i.e. has multiple frames.
+ *
+ * Introduced in API 31.
+ *
+ * This may require seeking past the first frame to verify whether
+ * there is a following frame (e.g. for GIF).
+ *
+ * Errors:
+ * - returns false if |decoder| is null.
+ */
+bool AImageDecoder_isAnimated(AImageDecoder* _Nonnull decoder)
+        __INTRODUCED_IN(31);
+
+enum {
+    /*
+     * Reported by {@link AImageDecoder_getRepeatCount} if the
+     * animation should repeat forever.
+     */
+    ANDROID_IMAGE_DECODER_INFINITE = INT32_MAX,
+};
+
+/**
+ * Report how many times the animation should repeat.
+ *
+ * Introduced in API 31.
+ *
+ * This does not include the first play through. e.g. a repeat
+ * count of 4 means that each frame is played 5 times.
+ *
+ * {@link ANDROID_IMAGE_DECODER_INFINITE} means to repeat forever.
+ *
+ * This may require seeking.
+ *
+ * For non-animated formats, this returns 0. It may return non-zero for
+ * an image with only one frame (i.e. {@link AImageDecoder_isAnimated} returns
+ * false) if the encoded image contains a repeat count.
+ *
+ * @return Number of times to repeat on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ *   is null.
+ */
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder);
+        __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/android/input.h b/include/android/input.h
index 7c39234..b04775b 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -988,11 +988,9 @@
 int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
 
 /**
- * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent.
- * The result may be used with generic and KeyEvent-specific AInputEvent_* functions.
- * The object returned by this function must be disposed using {@link AInputEvent_release()}.
- * User must guarantee that lifetime for object referenced by keyEvent is prolongated
- * up to release of returned AInputEvent*.
+ * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
+ * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
+ * returned by this function must be disposed using {@link AInputEvent_release()}.
  */
 const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
 
@@ -1312,11 +1310,10 @@
         int32_t axis, size_t pointer_index, size_t history_index);
 
 /**
- * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent.
- * The result may be used with generic and MotionEvent-specific AInputEvent_* functions.
- * The object returned by this function must be disposed using {@link AInputEvent_release()}.
- * User must guarantee that object referenced by motionEvent won't be recycled and
- * its lifetime is prolongated up to release of returned AInputEvent*.
+ * Creates a native AInputEvent* object that is a copy of the specified Java
+ * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
+ * AInputEvent_* functions. The object returned by this function must be disposed using
+ * {@link AInputEvent_release()}.
  */
 const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
 
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 59b1deb..c6d1c94 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -126,8 +126,8 @@
     ANDROID_RESOLV_NO_RETRY = 1 << 0,
 
     /**
-     * Do not cache the result of the lookup. The lookup may return a result that is already
-     * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified.
+     * Don't lookup this request in the cache, and don't cache the result of the lookup.
+     * This flag implies {@link #ANDROID_RESOLV_NO_CACHE_LOOKUP}.
      */
     ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1,
 
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
new file mode 100644
index 0000000..7817126
--- /dev/null
+++ b/include/android/permission_manager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PERMISSION_MANAGER_H
+#define ANDROID_PERMISSION_MANAGER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/**
+ * Permission check results.
+ *
+ * Introduced in API 31.
+ */
+enum {
+    /**
+     * This is returned by APermissionManager_checkPermission()
+     * if the permission has been granted to the given package.
+     */
+    PERMISSION_MANAGER_PERMISSION_GRANTED = 0,
+    /**
+     * This is returned by APermissionManager_checkPermission()
+     * if the permission has not been granted to the given package.
+     */
+    PERMISSION_MANAGER_PERMISSION_DENIED = -1,
+};
+
+/**
+ * Permission check return status values.
+ *
+ * Introduced in API 31.
+ */
+enum {
+    /**
+     * This is returned if the permission check completed without errors.
+     * The output result is valid and contains one of {PERMISSION_MANAGER_PERMISSION_GRANTED,
+     * PERMISSION_MANAGER_PERMISSION_DENIED}.
+     */
+    PERMISSION_MANAGER_STATUS_OK = 0,
+    /**
+     * This is returned if the permission check encountered an unspecified error.
+     * The output result is unmodified.
+     */
+    PERMISSION_MANAGER_STATUS_ERROR_UNKNOWN = -1,
+    /**
+     * This is returned if the permission check failed because the service is
+     * unavailable. The output result is unmodified.
+     */
+    PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Checks whether the package with the given pid/uid has been granted a permission.
+ *
+ * Note that the Java API of Context#checkPermission() is usually faster due to caching,
+ * thus is preferred over this API wherever possible.
+ *
+ * @param permission the permission to be checked.
+ * @param pid the process id of the package to be checked.
+ * @param uid the uid of the package to be checked.
+ * @param outResult output of the permission check result.
+ *
+ * @return error codes if any error happened during the check.
+ */
+int32_t APermissionManager_checkPermission(const char* permission,
+                                           pid_t pid,
+                                           uid_t uid,
+                                           int32_t* outResult) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+__END_DECLS
+
+#endif  // ANDROID_PERMISSION_MANAGER_H
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 6efa4f7..5f74682 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -65,6 +65,10 @@
  * another process. File descriptors may also be sent to other processes over a Unix domain
  * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
  *
+ * If you intend to share this file descriptor with a child process after
+ * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
+ * to clear the FD_CLOEXEC flag for this to work on all versions of Android.
+ *
  * Available since API level 26.
  *
  * \param name an optional name.
diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h
new file mode 100644
index 0000000..571a361
--- /dev/null
+++ b/include/attestation/HmacKeyManager.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+
+namespace android {
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
+class HmacKeyManager {
+public:
+    HmacKeyManager();
+    std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
+private:
+    const std::array<uint8_t, 128> mHmacKey;
+};
+} // namespace android
\ No newline at end of file
diff --git a/include/binder/Enum.h b/include/binder/Enum.h
new file mode 100644
index 0000000..4c25654
--- /dev/null
+++ b/include/binder/Enum.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#error Do not rely on global include files. All Android cc_* programs are given access to \
+    include_dirs for frameworks/native/include via global configuration, but this is legacy \
+    configuration. Instead, you should have a direct dependency on libbinder OR one of your \
+    dependencies should re-export libbinder headers with export_shared_lib_headers.
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
new file mode 100644
index 0000000..28f717a
--- /dev/null
+++ b/include/ftl/ArrayTraits.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <new>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+    using value_type = T;
+    using size_type = size_t;
+    using difference_type = ptrdiff_t;
+
+    using pointer = value_type*;
+    using reference = value_type&;
+    using iterator = pointer;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using const_pointer = const value_type*;
+    using const_reference = const value_type&;
+    using const_iterator = const_pointer;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    // TODO: Replace with std::construct_at in C++20.
+    template <typename... Args>
+    static pointer construct_at(const_iterator it, Args&&... args) {
+        void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+        return new (ptr) value_type{std::forward<Args>(args)...};
+    }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+    FTL_ARRAY_TRAIT(T, size_type);
+
+    FTL_ARRAY_TRAIT(T, reference);
+    FTL_ARRAY_TRAIT(T, iterator);
+    FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+    FTL_ARRAY_TRAIT(T, const_reference);
+    FTL_ARRAY_TRAIT(T, const_iterator);
+    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+    Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+public:
+    const_iterator begin() const { return cbegin(); }
+    const_iterator cbegin() const { return self().begin(); }
+
+    const_iterator end() const { return cend(); }
+    const_iterator cend() const { return self().end(); }
+
+    reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+    const_reverse_iterator rbegin() const { return crbegin(); }
+    const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+    reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+    const_reverse_iterator rend() const { return crend(); }
+    const_reverse_iterator crend() const { return self().rend(); }
+
+    iterator last() { return self().end() - 1; }
+    const_iterator last() const { return self().last(); }
+
+    reference front() { return *self().begin(); }
+    const_reference front() const { return self().front(); }
+
+    reference back() { return *last(); }
+    const_reference back() const { return self().back(); }
+
+    reference operator[](size_type i) { return *(self().begin() + i); }
+    const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, size_t> class Array>
+struct ArrayComparators {
+    template <typename T, size_t N, size_t M>
+    friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+    }
+
+    template <typename T, size_t N, size_t M>
+    friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+    }
+
+    template <typename T, size_t N, size_t M>
+    friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return rhs < lhs;
+    }
+
+    template <typename T, size_t N, size_t M>
+    friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return !(lhs == rhs);
+    }
+
+    template <typename T, size_t N, size_t M>
+    friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return !(lhs < rhs);
+    }
+
+    template <typename T, size_t N, size_t M>
+    friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+        return !(lhs > rhs);
+    }
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
new file mode 100644
index 0000000..cecec7f
--- /dev/null
+++ b/include/ftl/SmallVector.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/ArrayTraits.h>
+#include <ftl/StaticVector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct IsSmallVector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+//     ftl::SmallVector<char, 3> vector;
+//     assert(vector.empty());
+//     assert(!vector.dynamic());
+//
+//     vector = {'a', 'b', 'c'};
+//     assert(vector.size() == 3u);
+//     assert(!vector.dynamic());
+//
+//     vector.push_back('d');
+//     assert(vector.dynamic());
+//
+//     vector.unstable_erase(vector.begin());
+//     assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+//     vector.pop_back();
+//     assert(vector.back() == 'b');
+//     assert(vector.dynamic());
+//
+//     const char array[] = "hi";
+//     vector = ftl::SmallVector(array);
+//     assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+//     assert(!vector.dynamic());
+//
+template <typename T, size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+    using Static = StaticVector<T, N>;
+    using Dynamic = SmallVector<T, 0>;
+
+    // TODO: Replace with std::remove_cvref_t in C++20.
+    template <typename U>
+    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+public:
+    FTL_ARRAY_TRAIT(T, value_type);
+    FTL_ARRAY_TRAIT(T, size_type);
+    FTL_ARRAY_TRAIT(T, difference_type);
+
+    FTL_ARRAY_TRAIT(T, pointer);
+    FTL_ARRAY_TRAIT(T, reference);
+    FTL_ARRAY_TRAIT(T, iterator);
+    FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+    FTL_ARRAY_TRAIT(T, const_pointer);
+    FTL_ARRAY_TRAIT(T, const_reference);
+    FTL_ARRAY_TRAIT(T, const_iterator);
+    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+    // Creates an empty vector.
+    SmallVector() = default;
+
+    // Constructs at most N elements. See StaticVector for underlying constructors.
+    template <typename Arg, typename... Args,
+              typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>>
+    SmallVector(Arg&& arg, Args&&... args)
+          : mVector(std::in_place_type<Static>, std::forward<Arg>(arg),
+                    std::forward<Args>(args)...) {}
+
+    // Copies at most N elements from a smaller convertible vector.
+    template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+    SmallVector(const SmallVector<U, M>& other)
+          : SmallVector(IteratorRange, other.begin(), other.end()) {}
+
+    void swap(SmallVector& other) { mVector.swap(other.mVector); }
+
+    // Returns whether the vector is backed by static or dynamic storage.
+    bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); }
+
+    // Avoid std::visit as it generates a dispatch table.
+#define DISPATCH(T, F, ...)                                                                \
+    T F() __VA_ARGS__ {                                                                    \
+        return dynamic() ? std::get<Dynamic>(mVector).F() : std::get<Static>(mVector).F(); \
+    }
+
+    DISPATCH(size_type, max_size, const)
+    DISPATCH(size_type, size, const)
+    DISPATCH(bool, empty, const)
+
+    // noexcept to suppress warning about zero variadic macro arguments.
+    DISPATCH(iterator, begin, noexcept)
+    DISPATCH(const_iterator, begin, const)
+    DISPATCH(const_iterator, cbegin, const)
+
+    DISPATCH(iterator, end, noexcept)
+    DISPATCH(const_iterator, end, const)
+    DISPATCH(const_iterator, cend, const)
+
+    DISPATCH(reverse_iterator, rbegin, noexcept)
+    DISPATCH(const_reverse_iterator, rbegin, const)
+    DISPATCH(const_reverse_iterator, crbegin, const)
+
+    DISPATCH(reverse_iterator, rend, noexcept)
+    DISPATCH(const_reverse_iterator, rend, const)
+    DISPATCH(const_reverse_iterator, crend, const)
+
+    DISPATCH(iterator, last, noexcept)
+    DISPATCH(const_iterator, last, const)
+
+    DISPATCH(reference, front, noexcept)
+    DISPATCH(const_reference, front, const)
+
+    DISPATCH(reference, back, noexcept)
+    DISPATCH(const_reference, back, const)
+
+#undef DISPATCH
+
+    reference operator[](size_type i) {
+        return dynamic() ? std::get<Dynamic>(mVector)[i] : std::get<Static>(mVector)[i];
+    }
+
+    const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
+
+    // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+    // replacing at end() is erroneous.
+    //
+    // The element is emplaced via move constructor, so type T does not need to define copy/move
+    // assignment, e.g. its data members may be const.
+    //
+    // The arguments may directly or indirectly refer to the element being replaced.
+    //
+    // Iterators to the replaced element point to its replacement, and others remain valid.
+    //
+    template <typename... Args>
+    reference replace(const_iterator it, Args&&... args) {
+        if (dynamic()) {
+            return std::get<Dynamic>(mVector).replace(it, std::forward<Args>(args)...);
+        } else {
+            return std::get<Static>(mVector).replace(it, std::forward<Args>(args)...);
+        }
+    }
+
+    // Appends an element, and returns a reference to it.
+    //
+    // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+    // Otherwise, only the end() iterator is invalidated.
+    //
+    template <typename... Args>
+    reference emplace_back(Args&&... args) {
+        constexpr auto insertStatic = &Static::template emplace_back<Args...>;
+        constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>;
+        return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...);
+    }
+
+    // Appends an element.
+    //
+    // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+    // Otherwise, only the end() iterator is invalidated.
+    //
+    void push_back(const value_type& v) {
+        constexpr auto insertStatic =
+                static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+        constexpr auto insertDynamic =
+                static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+        insert<insertStatic, insertDynamic>(v);
+    }
+
+    void push_back(value_type&& v) {
+        constexpr auto insertStatic =
+                static_cast<bool (Static::*)(value_type&&)>(&Static::push_back);
+        constexpr auto insertDynamic =
+                static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back);
+        insert<insertStatic, insertDynamic>(std::move(v));
+    }
+
+    // Removes the last element. The vector must not be empty, or the call is erroneous.
+    //
+    // The last() and end() iterators are invalidated.
+    //
+    void pop_back() {
+        if (dynamic()) {
+            std::get<Dynamic>(mVector).pop_back();
+        } else {
+            std::get<Static>(mVector).pop_back();
+        }
+    }
+
+    // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+    // this moves the last element to the slot of the erased element.
+    //
+    // The last() and end() iterators, as well as those to the erased element, are invalidated.
+    //
+    void unstable_erase(iterator it) {
+        if (dynamic()) {
+            std::get<Dynamic>(mVector).unstable_erase(it);
+        } else {
+            std::get<Static>(mVector).unstable_erase(it);
+        }
+    }
+
+private:
+    template <auto insertStatic, auto insertDynamic, typename... Args>
+    auto insert(Args&&... args) {
+        if (Dynamic* const vector = std::get_if<Dynamic>(&mVector)) {
+            return (vector->*insertDynamic)(std::forward<Args>(args)...);
+        }
+
+        auto& vector = std::get<Static>(mVector);
+        if (vector.full()) {
+            return (promote(vector).*insertDynamic)(std::forward<Args>(args)...);
+        } else {
+            return (vector.*insertStatic)(std::forward<Args>(args)...);
+        }
+    }
+
+    Dynamic& promote(Static& staticVector) {
+        assert(staticVector.full());
+
+        // Allocate double capacity to reduce probability of reallocation.
+        Dynamic vector;
+        vector.reserve(Static::max_size() * 2);
+        std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector));
+
+        return mVector.template emplace<Dynamic>(std::move(vector));
+    }
+
+    std::variant<Static, Dynamic> mVector;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+                                ArrayIterators<SmallVector<T, 0>, T>,
+                                std::vector<T> {
+    using ArrayTraits<T>::construct_at;
+
+    using Iter = ArrayIterators<SmallVector, T>;
+    using Impl = std::vector<T>;
+
+    friend Iter;
+
+public:
+    FTL_ARRAY_TRAIT(T, value_type);
+    FTL_ARRAY_TRAIT(T, size_type);
+    FTL_ARRAY_TRAIT(T, difference_type);
+
+    FTL_ARRAY_TRAIT(T, pointer);
+    FTL_ARRAY_TRAIT(T, reference);
+    FTL_ARRAY_TRAIT(T, iterator);
+    FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+    FTL_ARRAY_TRAIT(T, const_pointer);
+    FTL_ARRAY_TRAIT(T, const_reference);
+    FTL_ARRAY_TRAIT(T, const_iterator);
+    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+    using Impl::Impl;
+
+    using Impl::empty;
+    using Impl::max_size;
+    using Impl::size;
+
+    using Impl::reserve;
+
+    // std::vector iterators are not necessarily raw pointers.
+    iterator begin() { return Impl::data(); }
+    iterator end() { return Impl::data() + size(); }
+
+    using Iter::begin;
+    using Iter::end;
+
+    using Iter::cbegin;
+    using Iter::cend;
+
+    using Iter::rbegin;
+    using Iter::rend;
+
+    using Iter::crbegin;
+    using Iter::crend;
+
+    using Iter::last;
+
+    using Iter::back;
+    using Iter::front;
+
+    using Iter::operator[];
+
+    template <typename... Args>
+    reference replace(const_iterator it, Args&&... args) {
+        value_type element{std::forward<Args>(args)...};
+        std::destroy_at(it);
+        // This is only safe because exceptions are disabled.
+        return *construct_at(it, std::move(element));
+    }
+
+    template <typename... Args>
+    iterator emplace_back(Args&&... args) {
+        return &Impl::emplace_back(std::forward<Args>(args)...);
+    }
+
+    bool push_back(const value_type& v) {
+        Impl::push_back(v);
+        return true;
+    }
+
+    bool push_back(value_type&& v) {
+        Impl::push_back(std::move(v));
+        return true;
+    }
+
+    using Impl::pop_back;
+
+    void unstable_erase(iterator it) {
+        if (it != last()) std::iter_swap(it, last());
+        pop_back();
+    }
+
+    void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct IsSmallVector : std::false_type {};
+
+template <typename T, size_t N>
+struct IsSmallVector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+    lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
new file mode 100644
index 0000000..457095d
--- /dev/null
+++ b/include/ftl/StaticVector.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/ArrayTraits.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+constexpr struct IteratorRangeTag {} IteratorRange;
+
+// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
+// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
+// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
+// default constructor, since elements are constructed in-place as the vector grows. Operations that
+// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
+// adheres to standard containers, except the unstable_erase operation that does not preserve order,
+// and the replace operation that destructively emplaces.
+//
+// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
+//
+// Example usage:
+//
+//     ftl::StaticVector<char, 3> vector;
+//     assert(vector.empty());
+//
+//     vector = {'a', 'b'};
+//     assert(vector.size() == 2u);
+//
+//     vector.push_back('c');
+//     assert(vector.full());
+//
+//     assert(!vector.push_back('d'));
+//     assert(vector.size() == 3u);
+//
+//     vector.unstable_erase(vector.begin());
+//     assert(vector == (ftl::StaticVector{'c', 'b'}));
+//
+//     vector.pop_back();
+//     assert(vector.back() == 'c');
+//
+//     const char array[] = "hi";
+//     vector = ftl::StaticVector(array);
+//     assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
+//
+template <typename T, size_t N>
+class StaticVector final : ArrayTraits<T>,
+                           ArrayIterators<StaticVector<T, N>, T>,
+                           ArrayComparators<StaticVector> {
+    static_assert(N > 0);
+
+    using ArrayTraits<T>::construct_at;
+
+    using Iter = ArrayIterators<StaticVector, T>;
+    friend Iter;
+
+    // There is ambiguity when constructing from two iterator-like elements like pointers:
+    // they could be an iterator range, or arguments for in-place construction. Assume the
+    // latter unless they are input iterators and cannot be used to construct elements. If
+    // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
+    template <typename I, typename Traits = std::iterator_traits<I>>
+    using IsInputIterator = std::conjunction<
+            std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
+            std::negation<std::is_constructible<T, I>>>;
+
+public:
+    FTL_ARRAY_TRAIT(T, value_type);
+    FTL_ARRAY_TRAIT(T, size_type);
+    FTL_ARRAY_TRAIT(T, difference_type);
+
+    FTL_ARRAY_TRAIT(T, pointer);
+    FTL_ARRAY_TRAIT(T, reference);
+    FTL_ARRAY_TRAIT(T, iterator);
+    FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+    FTL_ARRAY_TRAIT(T, const_pointer);
+    FTL_ARRAY_TRAIT(T, const_reference);
+    FTL_ARRAY_TRAIT(T, const_iterator);
+    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+    // Creates an empty vector.
+    StaticVector() = default;
+
+    // Copies and moves a vector, respectively.
+    StaticVector(const StaticVector& other)
+          : StaticVector(IteratorRange, other.begin(), other.end()) {}
+    StaticVector(StaticVector&& other) { swap<Empty>(other); }
+
+    // Copies at most N elements from a smaller convertible vector.
+    template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+    StaticVector(const StaticVector<U, M>& other)
+          : StaticVector(IteratorRange, other.begin(), other.end()) {}
+
+    // Copies at most N elements from an array.
+    template <typename U, size_t M>
+    explicit StaticVector(U (&array)[M])
+          : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
+
+    // Copies at most N elements from the range [first, last).
+    //
+    // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
+    //
+    template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
+    StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
+        using V = typename std::iterator_traits<Iterator>::value_type;
+        static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
+    }
+
+    template <typename Iterator>
+    StaticVector(IteratorRangeTag, Iterator first, Iterator last)
+          : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
+        std::uninitialized_copy(first, first + mSize, begin());
+    }
+
+    // Constructs at most N elements. The template arguments T and N are inferred using the
+    // deduction guide defined below. Note that T is determined from the first element, and
+    // subsequent elements must have convertible types:
+    //
+    //     ftl::StaticVector vector = {1, 2, 3};
+    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
+    //
+    //     const auto copy = "quince"s;
+    //     auto move = "tart"s;
+    //     ftl::StaticVector vector = {copy, std::move(move)};
+    //
+    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
+    //
+    template <typename E, typename... Es,
+              typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
+    StaticVector(E&& element, Es&&... elements)
+          : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
+                         std::forward<Es>(elements)...) {
+        static_assert(sizeof...(elements) < N, "Too many elements");
+    }
+
+    // Constructs at most N elements. The template arguments T and N are inferred using the
+    // deduction guide defined below. Element types must be convertible to the specified T:
+    //
+    //     ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+    //
+    template <typename... Es>
+    explicit StaticVector(std::in_place_type_t<T>, Es... elements)
+          : StaticVector(std::forward<Es>(elements)...) {}
+
+    ~StaticVector() { std::destroy(begin(), end()); }
+
+    StaticVector& operator=(const StaticVector& other) {
+        StaticVector copy(other);
+        swap(copy);
+        return *this;
+    }
+
+    StaticVector& operator=(StaticVector&& other) {
+        std::destroy(begin(), end());
+        mSize = 0;
+        swap<Empty>(other);
+        return *this;
+    }
+
+    template <typename = void>
+    void swap(StaticVector&);
+
+    static constexpr size_type max_size() { return N; }
+    size_type size() const { return mSize; }
+
+    bool empty() const { return size() == 0; }
+    bool full() const { return size() == max_size(); }
+
+    iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
+    iterator end() { return begin() + size(); }
+
+    using Iter::begin;
+    using Iter::end;
+
+    using Iter::cbegin;
+    using Iter::cend;
+
+    using Iter::rbegin;
+    using Iter::rend;
+
+    using Iter::crbegin;
+    using Iter::crend;
+
+    using Iter::last;
+
+    using Iter::back;
+    using Iter::front;
+
+    using Iter::operator[];
+
+    // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+    // replacing at end() is erroneous.
+    //
+    // The element is emplaced via move constructor, so type T does not need to define copy/move
+    // assignment, e.g. its data members may be const.
+    //
+    // The arguments may directly or indirectly refer to the element being replaced.
+    //
+    // Iterators to the replaced element point to its replacement, and others remain valid.
+    //
+    template <typename... Args>
+    reference replace(const_iterator it, Args&&... args) {
+        value_type element{std::forward<Args>(args)...};
+        std::destroy_at(it);
+        // This is only safe because exceptions are disabled.
+        return *construct_at(it, std::move(element));
+    }
+
+    // Appends an element, and returns an iterator to it. If the vector is full, the element is not
+    // inserted, and the end() iterator is returned.
+    //
+    // On success, the end() iterator is invalidated.
+    //
+    template <typename... Args>
+    iterator emplace_back(Args&&... args) {
+        if (full()) return end();
+        const iterator it = construct_at(end(), std::forward<Args>(args)...);
+        ++mSize;
+        return it;
+    }
+
+    // Appends an element unless the vector is full, and returns whether the element was inserted.
+    //
+    // On success, the end() iterator is invalidated.
+    //
+    bool push_back(const value_type& v) {
+        // Two statements for sequence point.
+        const iterator it = emplace_back(v);
+        return it != end();
+    }
+
+    bool push_back(value_type&& v) {
+        // Two statements for sequence point.
+        const iterator it = emplace_back(std::move(v));
+        return it != end();
+    }
+
+    // Removes the last element. The vector must not be empty, or the call is erroneous.
+    //
+    // The last() and end() iterators are invalidated.
+    //
+    void pop_back() { unstable_erase(last()); }
+
+    // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+    // this moves the last element to the slot of the erased element.
+    //
+    // The last() and end() iterators, as well as those to the erased element, are invalidated.
+    //
+    void unstable_erase(const_iterator it) {
+        std::destroy_at(it);
+        if (it != last()) {
+            // Move last element and destroy its source for destructor side effects. This is only
+            // safe because exceptions are disabled.
+            construct_at(it, std::move(back()));
+            std::destroy_at(last());
+        }
+        --mSize;
+    }
+
+private:
+    struct Empty {};
+
+    // Recursion for variadic constructor.
+    template <size_t I, typename E, typename... Es>
+    StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
+          : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
+        construct_at(begin() + I, std::forward<E>(element));
+    }
+
+    // Base case for variadic constructor.
+    template <size_t I>
+    explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
+
+    size_type mSize = 0;
+    std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
+};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
+
+template <typename T, size_t N>
+template <typename E>
+void StaticVector<T, N>::swap(StaticVector& other) {
+    auto [to, from] = std::make_pair(this, &other);
+    if (from == this) return;
+
+    // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
+    auto [min, max] = std::make_pair(size(), other.size());
+
+    // No elements to swap if moving into an empty vector.
+    if constexpr (std::is_same_v<E, Empty>) {
+        assert(min == 0);
+    } else {
+        if (min > max) {
+            std::swap(from, to);
+            std::swap(min, max);
+        }
+
+        // Swap elements [0, min).
+        std::swap_ranges(begin(), begin() + min, other.begin());
+
+        // No elements to move if sizes are equal.
+        if (min == max) return;
+    }
+
+    // Move elements [min, max) and destroy their source for destructor side effects.
+    const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
+    std::uninitialized_move(first, last, to->begin() + min);
+    std::destroy(first, last);
+
+    std::swap(mSize, other.mSize);
+}
+
+template <typename T, size_t N>
+inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
+    lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format
deleted file mode 100644
index 86763a0..0000000
--- a/include/gfx/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-BinPackParameters: false
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
-ConstructorInitializerIndentWidth: 2
-ContinuationIndentWidth: 8
-IndentWidth: 4
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 2427a07..b90d57e 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,11 +17,12 @@
 #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
 #define _LIBINPUT_DISPLAY_VIEWPORT_H
 
-#include <cinttypes>
-#include <optional>
-
 #include <android-base/stringprintf.h>
 #include <input/Input.h>
+#include <input/NamedEnum.h>
+
+#include <cinttypes>
+#include <optional>
 
 using android::base::StringPrintf;
 
@@ -39,24 +40,11 @@
  * Keep in sync with values in InputManagerService.java.
  */
 enum class ViewportType : int32_t {
-    VIEWPORT_INTERNAL = 1,
-    VIEWPORT_EXTERNAL = 2,
-    VIEWPORT_VIRTUAL = 3,
+    INTERNAL = 1,
+    EXTERNAL = 2,
+    VIRTUAL = 3,
 };
 
-static const char* viewportTypeToString(ViewportType type) {
-    switch(type) {
-        case ViewportType::VIEWPORT_INTERNAL:
-            return "INTERNAL";
-        case ViewportType::VIEWPORT_EXTERNAL:
-            return "EXTERNAL";
-        case ViewportType::VIEWPORT_VIRTUAL:
-            return "VIRTUAL";
-        default:
-            return "UNKNOWN";
-    }
-}
-
 /*
  * Describes how coordinates are mapped on a physical display.
  * See com.android.server.display.DisplayViewport.
@@ -97,7 +85,7 @@
             isActive(false),
             uniqueId(),
             physicalPort(std::nullopt),
-            type(ViewportType::VIEWPORT_INTERNAL) {}
+            type(ViewportType::INTERNAL) {}
 
     bool operator==(const DisplayViewport& other) const {
         return displayId == other.displayId && orientation == other.orientation &&
@@ -134,7 +122,7 @@
         isActive = false;
         uniqueId.clear();
         physicalPort = std::nullopt;
-        type = ViewportType::VIEWPORT_INTERNAL;
+        type = ViewportType::INTERNAL;
     }
 
     std::string toString() const {
@@ -143,7 +131,7 @@
                             "physicalFrame=[%d, %d, %d, %d], "
                             "deviceSize=[%d, %d], "
                             "isActive=[%d]",
-                            viewportTypeToString(type), displayId, uniqueId.c_str(),
+                            NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
                             physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
                                          : "<none>",
                             orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
diff --git a/include/input/Flags.h b/include/input/Flags.h
new file mode 100644
index 0000000..072dd18
--- /dev/null
+++ b/include/input/Flags.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <type_traits>
+
+#include "NamedEnum.h"
+#include "utils/BitSet.h"
+
+#ifndef __UI_INPUT_FLAGS_H
+#define __UI_INPUT_FLAGS_H
+
+namespace android {
+
+namespace details {
+
+template <typename F>
+inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+
+template <typename F, typename T, T... I>
+constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
+    constexpr size_t count = seq.size();
+
+    std::array<F, count> values{};
+    for (size_t i = 0, v = 0; v < count; ++i) {
+        values[v++] = static_cast<F>(T{1} << i);
+    }
+
+    return values;
+}
+
+template <typename F>
+inline constexpr auto flag_values = generate_flag_values<F>(
+        std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
+
+template <typename F, std::size_t... I>
+constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
+    return std::array<std::optional<std::string_view>, sizeof...(I)>{
+            {enum_value_name<F, flag_values<F>[I]>()...}};
+}
+
+template <typename F>
+inline constexpr auto flag_names =
+        generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
+
+// A trait for determining whether a type is specifically an enum class or not.
+template <typename T, bool = std::is_enum_v<T>>
+struct is_enum_class : std::false_type {};
+
+// By definition, an enum class is an enum that is not implicitly convertible to its underlying
+// type.
+template <typename T>
+struct is_enum_class<T, true>
+      : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
+
+template <typename T>
+inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
+} // namespace details
+
+template <auto V>
+constexpr auto flag_name() {
+    using F = decltype(V);
+    return details::enum_value_name<F, V>();
+}
+
+template <typename F>
+constexpr std::optional<std::string_view> flag_name(F flag) {
+    using U = std::underlying_type_t<F>;
+    auto idx = __builtin_ctzl(static_cast<U>(flag));
+    return details::flag_names<F>[idx];
+}
+
+/* A class for handling flags defined by an enum or enum class in a type-safe way. */
+template <typename F>
+class Flags {
+    // F must be an enum or its underlying type is undefined. Theoretically we could specialize this
+    // further to avoid this restriction but in general we want to encourage the use of enums
+    // anyways.
+    static_assert(std::is_enum_v<F>, "Flags type must be an enum");
+    using U = typename std::underlying_type_t<F>;
+
+public:
+    constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+    constexpr Flags() : mFlags(0) {}
+    constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
+
+    // Provide a non-explicit construct for non-enum classes since they easily convert to their
+    // underlying types (e.g. when used with bitwise operators). For enum classes, however, we
+    // should force them to be explicitly constructed from their underlying types to make full use
+    // of the type checker.
+    template <typename T = U>
+    constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
+          : mFlags(t) {}
+    template <typename T = U>
+    explicit constexpr Flags(T t,
+                             typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+          : mFlags(t) {}
+
+    class Iterator {
+        // The type can't be larger than 64-bits otherwise it won't fit in BitSet64.
+        static_assert(sizeof(U) <= sizeof(uint64_t));
+
+    public:
+        Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
+        Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {}
+
+        // Pre-fix ++
+        Iterator& operator++() {
+            if (mRemainingFlags.isEmpty()) {
+                mCurrFlag = static_cast<F>(0);
+            } else {
+                uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left
+                const U flag = 1 << (64 - bit - 1);
+                mCurrFlag = static_cast<F>(flag);
+            }
+            return *this;
+        }
+
+        // Post-fix ++
+        Iterator operator++(int) {
+            Iterator iter = *this;
+            ++*this;
+            return iter;
+        }
+
+        bool operator==(Iterator other) const {
+            return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags;
+        }
+
+        bool operator!=(Iterator other) const { return !(*this == other); }
+
+        F operator*() { return mCurrFlag; }
+
+        // iterator traits
+
+        // In the future we could make this a bidirectional const iterator instead of a forward
+        // iterator but it doesn't seem worth the added complexity at this point. This could not,
+        // however, be made a non-const iterator as assigning one flag to another is a non-sensical
+        // operation.
+        using iterator_category = std::input_iterator_tag;
+        using value_type = F;
+        // Per the C++ spec, because input iterators are not assignable the iterator's reference
+        // type does not actually need to be a reference. In fact, making it a reference would imply
+        // that modifying it would change the underlying Flags object, which is obviously wrong for
+        // the same reason this can't be a non-const iterator.
+        using reference = F;
+        using difference_type = void;
+        using pointer = void;
+
+    private:
+        BitSet64 mRemainingFlags;
+        F mCurrFlag;
+    };
+
+    /*
+     * Tests whether the given flag is set.
+     */
+    bool test(F flag) const {
+        U f = static_cast<U>(flag);
+        return (f & mFlags) == f;
+    }
+
+    /* Tests whether any of the given flags are set */
+    bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; }
+
+    /* Tests whether all of the given flags are set */
+    bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; }
+
+    Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
+    Flags<F>& operator|=(Flags<F> rhs) {
+        mFlags = mFlags | rhs.mFlags;
+        return *this;
+    }
+
+    Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); }
+    Flags<F>& operator&=(Flags<F> rhs) {
+        mFlags = mFlags & rhs.mFlags;
+        return *this;
+    }
+
+    Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); }
+    Flags<F>& operator^=(Flags<F> rhs) {
+        mFlags = mFlags ^ rhs.mFlags;
+        return *this;
+    }
+
+    Flags<F> operator~() { return static_cast<F>(~mFlags); }
+
+    bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; }
+    bool operator!=(Flags<F> rhs) const { return !operator==(rhs); }
+
+    Flags<F>& operator=(const Flags<F>& rhs) {
+        mFlags = rhs.mFlags;
+        return *this;
+    }
+
+    Iterator begin() const { return Iterator(*this); }
+
+    Iterator end() const { return Iterator(); }
+
+    /*
+     * Returns the stored set of flags.
+     *
+     * Note that this returns the underlying type rather than the base enum class. This is because
+     * the value is no longer necessarily a strict member of the enum since the returned value could
+     * be multiple enum variants OR'd together.
+     */
+    U get() const { return mFlags; }
+
+    std::string string() const {
+        std::string result;
+        bool first = true;
+        U unstringified = 0;
+        for (const F f : *this) {
+            std::optional<std::string_view> flagString = flag_name(f);
+            if (flagString) {
+                appendFlag(result, flagString.value(), first);
+            } else {
+                unstringified |= static_cast<U>(f);
+            }
+        }
+
+        if (unstringified != 0) {
+            appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+        }
+
+        if (first) {
+            result += "0x0";
+        }
+
+        return result;
+    }
+
+private:
+    U mFlags;
+
+    static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
+        if (first) {
+            first = false;
+        } else {
+            str += " | ";
+        }
+        str += flag;
+    }
+};
+
+// This namespace provides operator overloads for enum classes to make it easier to work with them
+// as flags. In order to use these, add them via a `using namespace` declaration.
+namespace flag_operators {
+
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+inline Flags<F> operator~(F f) {
+    using U = typename std::underlying_type_t<F>;
+    return static_cast<F>(~static_cast<U>(f));
+}
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+Flags<F> operator|(F lhs, F rhs) {
+    using U = typename std::underlying_type_t<F>;
+    return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+}
+
+} // namespace flag_operators
+} // namespace android
+
+#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
deleted file mode 100644
index d23e3b7..0000000
--- a/include/input/IInputFlinger.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBINPUT_IINPUT_FLINGER_H
-#define _LIBINPUT_IINPUT_FLINGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-/*
- * This class defines the Binder IPC interface for accessing various
- * InputFlinger features.
- */
-class IInputFlinger : public IInterface {
-public:
-    DECLARE_META_INTERFACE(InputFlinger)
-
-    virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
-    virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
-    virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
-};
-
-
-/**
- * Binder implementation.
- */
-class BnInputFlinger : public BnInterface<IInputFlinger> {
-public:
-    enum {
-        SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        REGISTER_INPUT_CHANNEL_TRANSACTION,
-        UNREGISTER_INPUT_CHANNEL_TRANSACTION
-    };
-
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-            Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif // _LIBINPUT_IINPUT_FLINGER_H
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h
deleted file mode 100644
index 15d31b2..0000000
--- a/include/input/ISetInputWindowsListener.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class ISetInputWindowsListener : public IInterface {
-public:
-    DECLARE_META_INTERFACE(SetInputWindowsListener)
-    virtual void onSetInputWindowsFinished() = 0;
-};
-
-class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> {
-public:
-    enum SetInputWindowsTag : uint32_t {
-        ON_SET_INPUT_WINDOWS_FINISHED
-    };
-
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                uint32_t flags = 0) override;
-};
-
-}; // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index 7dcbaf0..3facfa5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,6 +26,7 @@
 #include <android/input.h>
 #include <math.h>
 #include <stdint.h>
+#include <ui/Transform.h>
 #include <utils/BitSet.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -86,6 +87,13 @@
 constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
         AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
+/**
+ * This flag indicates that the point up event has been canceled.
+ * Typically this is used for palm event when the user has accidental touches.
+ * TODO: Adjust flag to public api
+ */
+constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
+
 enum {
     /* Used when a motion event is not associated with any display.
      * Typically used for non-pointer events. */
@@ -133,14 +141,6 @@
 #define MAX_CONTROLLER_LEDS 4
 
 /*
- * SystemUiVisibility constants from View.
- */
-enum {
-    ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
-    ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
  * Maximum number of pointers supported per motion event.
  * Smallest number of pointers is 1.
  * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
@@ -177,7 +177,7 @@
 
 namespace android {
 
-#ifdef __ANDROID__
+#ifdef __linux__
 class Parcel;
 #endif
 
@@ -304,11 +304,6 @@
  */
 constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
 
-/**
- * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
- */
-constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
-
 /*
  * Pointer coordinate data.
  */
@@ -341,6 +336,8 @@
     void scale(float globalScale, float windowXScale, float windowYScale);
     void applyOffset(float xOffset, float yOffset);
 
+    void transform(const ui::Transform& transform);
+
     inline float getX() const {
         return getAxisValue(AMOTION_EVENT_AXIS_X);
     }
@@ -349,7 +346,7 @@
         return getAxisValue(AMOTION_EVENT_AXIS_Y);
     }
 
-#ifdef __ANDROID__
+#ifdef __linux__
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
 #endif
@@ -462,6 +459,8 @@
                     nsecs_t eventTime);
     void initialize(const KeyEvent& from);
 
+    static const char* actionToString(int32_t action);
+
 protected:
     int32_t mAction;
     int32_t mFlags;
@@ -515,13 +514,11 @@
 
     inline void setActionButton(int32_t button) { mActionButton = button; }
 
-    inline float getXScale() const { return mXScale; }
+    inline float getXOffset() const { return mTransform.tx(); }
 
-    inline float getYScale() const { return mYScale; }
+    inline float getYOffset() const { return mTransform.ty(); }
 
-    inline float getXOffset() const { return mXOffset; }
-
-    inline float getYOffset() const { return mYOffset; }
+    inline ui::Transform getTransform() const { return mTransform; }
 
     inline float getXPrecision() const { return mXPrecision; }
 
@@ -573,10 +570,18 @@
 
     float getAxisValue(int32_t axis, size_t pointerIndex) const;
 
+    /**
+     * Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+     * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+     */
     inline float getX(size_t pointerIndex) const {
         return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
     }
 
+    /**
+     * Get the Y coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+     * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+     */
     inline float getY(size_t pointerIndex) const {
         return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
     }
@@ -683,8 +688,8 @@
     void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
                     int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
-                    MotionClassification classification, float xScale, float yScale, float xOffset,
-                    float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+                    MotionClassification classification, const ui::Transform& transform,
+                    float xPrecision, float yPrecision, float rawXCursorPosition,
                     float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
                     size_t pointerCount, const PointerProperties* pointerProperties,
                     const PointerCoords* pointerCoords);
@@ -701,9 +706,9 @@
 
     // Apply 3x3 perspective matrix transformation.
     // Matrix is in row-major form and compatible with SkMatrix.
-    void transform(const float matrix[9]);
+    void transform(const std::array<float, 9>& matrix);
 
-#ifdef __ANDROID__
+#ifdef __linux__
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
 #endif
@@ -717,7 +722,7 @@
     inline const PointerProperties* getPointerProperties() const {
         return mPointerProperties.array();
     }
-    inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+    inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); }
     inline const PointerCoords* getSamplePointerCoords() const {
             return mSamplePointerCoords.array();
     }
@@ -725,6 +730,8 @@
     static const char* getLabel(int32_t axis);
     static int32_t getAxisFromLabel(const char* label);
 
+    static std::string actionToString(int32_t action);
+
 protected:
     int32_t mAction;
     int32_t mActionButton;
@@ -733,17 +740,14 @@
     int32_t mMetaState;
     int32_t mButtonState;
     MotionClassification mClassification;
-    float mXScale;
-    float mYScale;
-    float mXOffset;
-    float mYOffset;
+    ui::Transform mTransform;
     float mXPrecision;
     float mYPrecision;
     float mRawXCursorPosition;
     float mRawYCursorPosition;
     nsecs_t mDownTime;
     Vector<PointerProperties> mPointerProperties;
-    Vector<nsecs_t> mSampleEventTimes;
+    std::vector<nsecs_t> mSampleEventTimes;
     Vector<PointerCoords> mSamplePointerCoords;
 };
 
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 86de394..8e4fe79 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -19,35 +19,24 @@
 
 #include <string>
 
+#include <android/InputApplicationInfo.h>
+
 #include <binder/IBinder.h>
 #include <binder/Parcel.h>
+#include <binder/Parcelable.h>
 
 #include <input/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
 namespace android {
-
-/*
- * Describes the properties of an application that can receive input.
- */
-struct InputApplicationInfo {
-    sp<IBinder> token;
-    std::string name;
-    nsecs_t dispatchingTimeout;
-
-    status_t write(Parcel& output) const;
-    static InputApplicationInfo read(const Parcel& from);
-};
-
-
 /*
  * Handle for an application that can receive input.
  *
  * Used by the native input dispatcher as a handle for the window manager objects
  * that describe an application.
  */
-class InputApplicationHandle : public RefBase {
+class InputApplicationHandle {
 public:
     inline const InputApplicationInfo* getInfo() const {
         return &mInfo;
@@ -57,19 +46,22 @@
         return !mInfo.name.empty() ? mInfo.name : "<invalid>";
     }
 
-    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
-        return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
-    }
-
     inline std::chrono::nanoseconds getDispatchingTimeout(
             std::chrono::nanoseconds defaultValue) const {
-        return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+        return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis)
+                           : defaultValue;
     }
 
     inline sp<IBinder> getApplicationToken() const {
         return mInfo.token;
     }
 
+    bool operator==(const InputApplicationHandle& other) const {
+        return getName() == other.getName() && getApplicationToken() == other.getApplicationToken();
+    }
+
+    bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); }
+
     /**
      * Requests that the state of this object be updated to reflect
      * the most current available information about the application.
@@ -80,9 +72,10 @@
      * Returns true on success, or false if the handle is no longer valid.
      */
     virtual bool updateInfo() = 0;
+
 protected:
-    InputApplicationHandle();
-    virtual ~InputApplicationHandle();
+    InputApplicationHandle() = default;
+    virtual ~InputApplicationHandle() = default;
 
     InputApplicationInfo mInfo;
 };
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 20a17e3..60638ca 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -108,11 +108,11 @@
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
     inline int32_t getKeyboardType() const { return mKeyboardType; }
 
-    inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
+    inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
         mKeyCharacterMap = value;
     }
 
-    inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+    inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
         return mKeyCharacterMap;
     }
 
@@ -136,7 +136,7 @@
     bool mHasMic;
     uint32_t mSources;
     int32_t mKeyboardType;
-    sp<KeyCharacterMap> mKeyCharacterMap;
+    std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
     bool mHasVibrator;
     bool mHasButtonUnderPad;
 
@@ -144,10 +144,10 @@
 };
 
 /* Types of input device configuration files. */
-enum InputDeviceConfigurationFileType {
-    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0,     /* .idc file */
-    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1,        /* .kl file */
-    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+enum class InputDeviceConfigurationFileType : int32_t {
+    CONFIGURATION = 0,     /* .idc file */
+    KEY_LAYOUT = 1,        /* .kl file */
+    KEY_CHARACTER_MAP = 2, /* .kcm file */
 };
 
 /*
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b327d76..2a742f9 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -19,11 +19,7 @@
 
 #include <input/Input.h>
 #include <android/keycodes.h>
-
-#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
-#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
-#define DEFINE_LED(led) { #led, ALED_##led }
-#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
+#include <unordered_map>
 
 namespace android {
 
@@ -35,430 +31,41 @@
     int value;
 };
 
+//   NOTE: If you want a new key code, axis code, led code or flag code in keylayout file,
+//   then you must add it to InputEventLabels.cpp.
 
-static const InputEventLabel KEYCODES[] = {
-    // NOTE: If you add a new keycode here you must also add it to several other files.
-    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
-    DEFINE_KEYCODE(UNKNOWN),
-    DEFINE_KEYCODE(SOFT_LEFT),
-    DEFINE_KEYCODE(SOFT_RIGHT),
-    DEFINE_KEYCODE(HOME),
-    DEFINE_KEYCODE(BACK),
-    DEFINE_KEYCODE(CALL),
-    DEFINE_KEYCODE(ENDCALL),
-    DEFINE_KEYCODE(0),
-    DEFINE_KEYCODE(1),
-    DEFINE_KEYCODE(2),
-    DEFINE_KEYCODE(3),
-    DEFINE_KEYCODE(4),
-    DEFINE_KEYCODE(5),
-    DEFINE_KEYCODE(6),
-    DEFINE_KEYCODE(7),
-    DEFINE_KEYCODE(8),
-    DEFINE_KEYCODE(9),
-    DEFINE_KEYCODE(STAR),
-    DEFINE_KEYCODE(POUND),
-    DEFINE_KEYCODE(DPAD_UP),
-    DEFINE_KEYCODE(DPAD_DOWN),
-    DEFINE_KEYCODE(DPAD_LEFT),
-    DEFINE_KEYCODE(DPAD_RIGHT),
-    DEFINE_KEYCODE(DPAD_CENTER),
-    DEFINE_KEYCODE(VOLUME_UP),
-    DEFINE_KEYCODE(VOLUME_DOWN),
-    DEFINE_KEYCODE(POWER),
-    DEFINE_KEYCODE(CAMERA),
-    DEFINE_KEYCODE(CLEAR),
-    DEFINE_KEYCODE(A),
-    DEFINE_KEYCODE(B),
-    DEFINE_KEYCODE(C),
-    DEFINE_KEYCODE(D),
-    DEFINE_KEYCODE(E),
-    DEFINE_KEYCODE(F),
-    DEFINE_KEYCODE(G),
-    DEFINE_KEYCODE(H),
-    DEFINE_KEYCODE(I),
-    DEFINE_KEYCODE(J),
-    DEFINE_KEYCODE(K),
-    DEFINE_KEYCODE(L),
-    DEFINE_KEYCODE(M),
-    DEFINE_KEYCODE(N),
-    DEFINE_KEYCODE(O),
-    DEFINE_KEYCODE(P),
-    DEFINE_KEYCODE(Q),
-    DEFINE_KEYCODE(R),
-    DEFINE_KEYCODE(S),
-    DEFINE_KEYCODE(T),
-    DEFINE_KEYCODE(U),
-    DEFINE_KEYCODE(V),
-    DEFINE_KEYCODE(W),
-    DEFINE_KEYCODE(X),
-    DEFINE_KEYCODE(Y),
-    DEFINE_KEYCODE(Z),
-    DEFINE_KEYCODE(COMMA),
-    DEFINE_KEYCODE(PERIOD),
-    DEFINE_KEYCODE(ALT_LEFT),
-    DEFINE_KEYCODE(ALT_RIGHT),
-    DEFINE_KEYCODE(SHIFT_LEFT),
-    DEFINE_KEYCODE(SHIFT_RIGHT),
-    DEFINE_KEYCODE(TAB),
-    DEFINE_KEYCODE(SPACE),
-    DEFINE_KEYCODE(SYM),
-    DEFINE_KEYCODE(EXPLORER),
-    DEFINE_KEYCODE(ENVELOPE),
-    DEFINE_KEYCODE(ENTER),
-    DEFINE_KEYCODE(DEL),
-    DEFINE_KEYCODE(GRAVE),
-    DEFINE_KEYCODE(MINUS),
-    DEFINE_KEYCODE(EQUALS),
-    DEFINE_KEYCODE(LEFT_BRACKET),
-    DEFINE_KEYCODE(RIGHT_BRACKET),
-    DEFINE_KEYCODE(BACKSLASH),
-    DEFINE_KEYCODE(SEMICOLON),
-    DEFINE_KEYCODE(APOSTROPHE),
-    DEFINE_KEYCODE(SLASH),
-    DEFINE_KEYCODE(AT),
-    DEFINE_KEYCODE(NUM),
-    DEFINE_KEYCODE(HEADSETHOOK),
-    DEFINE_KEYCODE(FOCUS),   // *Camera* focus
-    DEFINE_KEYCODE(PLUS),
-    DEFINE_KEYCODE(MENU),
-    DEFINE_KEYCODE(NOTIFICATION),
-    DEFINE_KEYCODE(SEARCH),
-    DEFINE_KEYCODE(MEDIA_PLAY_PAUSE),
-    DEFINE_KEYCODE(MEDIA_STOP),
-    DEFINE_KEYCODE(MEDIA_NEXT),
-    DEFINE_KEYCODE(MEDIA_PREVIOUS),
-    DEFINE_KEYCODE(MEDIA_REWIND),
-    DEFINE_KEYCODE(MEDIA_FAST_FORWARD),
-    DEFINE_KEYCODE(MUTE),
-    DEFINE_KEYCODE(PAGE_UP),
-    DEFINE_KEYCODE(PAGE_DOWN),
-    DEFINE_KEYCODE(PICTSYMBOLS),
-    DEFINE_KEYCODE(SWITCH_CHARSET),
-    DEFINE_KEYCODE(BUTTON_A),
-    DEFINE_KEYCODE(BUTTON_B),
-    DEFINE_KEYCODE(BUTTON_C),
-    DEFINE_KEYCODE(BUTTON_X),
-    DEFINE_KEYCODE(BUTTON_Y),
-    DEFINE_KEYCODE(BUTTON_Z),
-    DEFINE_KEYCODE(BUTTON_L1),
-    DEFINE_KEYCODE(BUTTON_R1),
-    DEFINE_KEYCODE(BUTTON_L2),
-    DEFINE_KEYCODE(BUTTON_R2),
-    DEFINE_KEYCODE(BUTTON_THUMBL),
-    DEFINE_KEYCODE(BUTTON_THUMBR),
-    DEFINE_KEYCODE(BUTTON_START),
-    DEFINE_KEYCODE(BUTTON_SELECT),
-    DEFINE_KEYCODE(BUTTON_MODE),
-    DEFINE_KEYCODE(ESCAPE),
-    DEFINE_KEYCODE(FORWARD_DEL),
-    DEFINE_KEYCODE(CTRL_LEFT),
-    DEFINE_KEYCODE(CTRL_RIGHT),
-    DEFINE_KEYCODE(CAPS_LOCK),
-    DEFINE_KEYCODE(SCROLL_LOCK),
-    DEFINE_KEYCODE(META_LEFT),
-    DEFINE_KEYCODE(META_RIGHT),
-    DEFINE_KEYCODE(FUNCTION),
-    DEFINE_KEYCODE(SYSRQ),
-    DEFINE_KEYCODE(BREAK),
-    DEFINE_KEYCODE(MOVE_HOME),
-    DEFINE_KEYCODE(MOVE_END),
-    DEFINE_KEYCODE(INSERT),
-    DEFINE_KEYCODE(FORWARD),
-    DEFINE_KEYCODE(MEDIA_PLAY),
-    DEFINE_KEYCODE(MEDIA_PAUSE),
-    DEFINE_KEYCODE(MEDIA_CLOSE),
-    DEFINE_KEYCODE(MEDIA_EJECT),
-    DEFINE_KEYCODE(MEDIA_RECORD),
-    DEFINE_KEYCODE(F1),
-    DEFINE_KEYCODE(F2),
-    DEFINE_KEYCODE(F3),
-    DEFINE_KEYCODE(F4),
-    DEFINE_KEYCODE(F5),
-    DEFINE_KEYCODE(F6),
-    DEFINE_KEYCODE(F7),
-    DEFINE_KEYCODE(F8),
-    DEFINE_KEYCODE(F9),
-    DEFINE_KEYCODE(F10),
-    DEFINE_KEYCODE(F11),
-    DEFINE_KEYCODE(F12),
-    DEFINE_KEYCODE(NUM_LOCK),
-    DEFINE_KEYCODE(NUMPAD_0),
-    DEFINE_KEYCODE(NUMPAD_1),
-    DEFINE_KEYCODE(NUMPAD_2),
-    DEFINE_KEYCODE(NUMPAD_3),
-    DEFINE_KEYCODE(NUMPAD_4),
-    DEFINE_KEYCODE(NUMPAD_5),
-    DEFINE_KEYCODE(NUMPAD_6),
-    DEFINE_KEYCODE(NUMPAD_7),
-    DEFINE_KEYCODE(NUMPAD_8),
-    DEFINE_KEYCODE(NUMPAD_9),
-    DEFINE_KEYCODE(NUMPAD_DIVIDE),
-    DEFINE_KEYCODE(NUMPAD_MULTIPLY),
-    DEFINE_KEYCODE(NUMPAD_SUBTRACT),
-    DEFINE_KEYCODE(NUMPAD_ADD),
-    DEFINE_KEYCODE(NUMPAD_DOT),
-    DEFINE_KEYCODE(NUMPAD_COMMA),
-    DEFINE_KEYCODE(NUMPAD_ENTER),
-    DEFINE_KEYCODE(NUMPAD_EQUALS),
-    DEFINE_KEYCODE(NUMPAD_LEFT_PAREN),
-    DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN),
-    DEFINE_KEYCODE(VOLUME_MUTE),
-    DEFINE_KEYCODE(INFO),
-    DEFINE_KEYCODE(CHANNEL_UP),
-    DEFINE_KEYCODE(CHANNEL_DOWN),
-    DEFINE_KEYCODE(ZOOM_IN),
-    DEFINE_KEYCODE(ZOOM_OUT),
-    DEFINE_KEYCODE(TV),
-    DEFINE_KEYCODE(WINDOW),
-    DEFINE_KEYCODE(GUIDE),
-    DEFINE_KEYCODE(DVR),
-    DEFINE_KEYCODE(BOOKMARK),
-    DEFINE_KEYCODE(CAPTIONS),
-    DEFINE_KEYCODE(SETTINGS),
-    DEFINE_KEYCODE(TV_POWER),
-    DEFINE_KEYCODE(TV_INPUT),
-    DEFINE_KEYCODE(STB_POWER),
-    DEFINE_KEYCODE(STB_INPUT),
-    DEFINE_KEYCODE(AVR_POWER),
-    DEFINE_KEYCODE(AVR_INPUT),
-    DEFINE_KEYCODE(PROG_RED),
-    DEFINE_KEYCODE(PROG_GREEN),
-    DEFINE_KEYCODE(PROG_YELLOW),
-    DEFINE_KEYCODE(PROG_BLUE),
-    DEFINE_KEYCODE(APP_SWITCH),
-    DEFINE_KEYCODE(BUTTON_1),
-    DEFINE_KEYCODE(BUTTON_2),
-    DEFINE_KEYCODE(BUTTON_3),
-    DEFINE_KEYCODE(BUTTON_4),
-    DEFINE_KEYCODE(BUTTON_5),
-    DEFINE_KEYCODE(BUTTON_6),
-    DEFINE_KEYCODE(BUTTON_7),
-    DEFINE_KEYCODE(BUTTON_8),
-    DEFINE_KEYCODE(BUTTON_9),
-    DEFINE_KEYCODE(BUTTON_10),
-    DEFINE_KEYCODE(BUTTON_11),
-    DEFINE_KEYCODE(BUTTON_12),
-    DEFINE_KEYCODE(BUTTON_13),
-    DEFINE_KEYCODE(BUTTON_14),
-    DEFINE_KEYCODE(BUTTON_15),
-    DEFINE_KEYCODE(BUTTON_16),
-    DEFINE_KEYCODE(LANGUAGE_SWITCH),
-    DEFINE_KEYCODE(MANNER_MODE),
-    DEFINE_KEYCODE(3D_MODE),
-    DEFINE_KEYCODE(CONTACTS),
-    DEFINE_KEYCODE(CALENDAR),
-    DEFINE_KEYCODE(MUSIC),
-    DEFINE_KEYCODE(CALCULATOR),
-    DEFINE_KEYCODE(ZENKAKU_HANKAKU),
-    DEFINE_KEYCODE(EISU),
-    DEFINE_KEYCODE(MUHENKAN),
-    DEFINE_KEYCODE(HENKAN),
-    DEFINE_KEYCODE(KATAKANA_HIRAGANA),
-    DEFINE_KEYCODE(YEN),
-    DEFINE_KEYCODE(RO),
-    DEFINE_KEYCODE(KANA),
-    DEFINE_KEYCODE(ASSIST),
-    DEFINE_KEYCODE(BRIGHTNESS_DOWN),
-    DEFINE_KEYCODE(BRIGHTNESS_UP),
-    DEFINE_KEYCODE(MEDIA_AUDIO_TRACK),
-    DEFINE_KEYCODE(SLEEP),
-    DEFINE_KEYCODE(WAKEUP),
-    DEFINE_KEYCODE(PAIRING),
-    DEFINE_KEYCODE(MEDIA_TOP_MENU),
-    DEFINE_KEYCODE(11),
-    DEFINE_KEYCODE(12),
-    DEFINE_KEYCODE(LAST_CHANNEL),
-    DEFINE_KEYCODE(TV_DATA_SERVICE),
-    DEFINE_KEYCODE(VOICE_ASSIST),
-    DEFINE_KEYCODE(TV_RADIO_SERVICE),
-    DEFINE_KEYCODE(TV_TELETEXT),
-    DEFINE_KEYCODE(TV_NUMBER_ENTRY),
-    DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG),
-    DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL),
-    DEFINE_KEYCODE(TV_SATELLITE),
-    DEFINE_KEYCODE(TV_SATELLITE_BS),
-    DEFINE_KEYCODE(TV_SATELLITE_CS),
-    DEFINE_KEYCODE(TV_SATELLITE_SERVICE),
-    DEFINE_KEYCODE(TV_NETWORK),
-    DEFINE_KEYCODE(TV_ANTENNA_CABLE),
-    DEFINE_KEYCODE(TV_INPUT_HDMI_1),
-    DEFINE_KEYCODE(TV_INPUT_HDMI_2),
-    DEFINE_KEYCODE(TV_INPUT_HDMI_3),
-    DEFINE_KEYCODE(TV_INPUT_HDMI_4),
-    DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1),
-    DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2),
-    DEFINE_KEYCODE(TV_INPUT_COMPONENT_1),
-    DEFINE_KEYCODE(TV_INPUT_COMPONENT_2),
-    DEFINE_KEYCODE(TV_INPUT_VGA_1),
-    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION),
-    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP),
-    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN),
-    DEFINE_KEYCODE(TV_ZOOM_MODE),
-    DEFINE_KEYCODE(TV_CONTENTS_MENU),
-    DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU),
-    DEFINE_KEYCODE(TV_TIMER_PROGRAMMING),
-    DEFINE_KEYCODE(HELP),
-    DEFINE_KEYCODE(NAVIGATE_PREVIOUS),
-    DEFINE_KEYCODE(NAVIGATE_NEXT),
-    DEFINE_KEYCODE(NAVIGATE_IN),
-    DEFINE_KEYCODE(NAVIGATE_OUT),
-    DEFINE_KEYCODE(STEM_PRIMARY),
-    DEFINE_KEYCODE(STEM_1),
-    DEFINE_KEYCODE(STEM_2),
-    DEFINE_KEYCODE(STEM_3),
-    DEFINE_KEYCODE(DPAD_UP_LEFT),
-    DEFINE_KEYCODE(DPAD_DOWN_LEFT),
-    DEFINE_KEYCODE(DPAD_UP_RIGHT),
-    DEFINE_KEYCODE(DPAD_DOWN_RIGHT),
-    DEFINE_KEYCODE(MEDIA_SKIP_FORWARD),
-    DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD),
-    DEFINE_KEYCODE(MEDIA_STEP_FORWARD),
-    DEFINE_KEYCODE(MEDIA_STEP_BACKWARD),
-    DEFINE_KEYCODE(SOFT_SLEEP),
-    DEFINE_KEYCODE(CUT),
-    DEFINE_KEYCODE(COPY),
-    DEFINE_KEYCODE(PASTE),
-    DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP),
-    DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
-    DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
-    DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
-    DEFINE_KEYCODE(ALL_APPS),
-    DEFINE_KEYCODE(REFRESH),
-    DEFINE_KEYCODE(THUMBS_UP),
-    DEFINE_KEYCODE(THUMBS_DOWN),
-    DEFINE_KEYCODE(PROFILE_SWITCH),
+class InputEventLookup {
+public:
+    static int lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+                                  const char* literal);
 
-    { nullptr, 0 }
+    static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value);
+
+    static int32_t getKeyCodeByLabel(const char* label);
+
+    static const char* getLabelByKeyCode(int32_t keyCode);
+
+    static uint32_t getKeyFlagByLabel(const char* label);
+
+    static int32_t getAxisByLabel(const char* label);
+
+    static const char* getAxisLabel(int32_t axisId);
+
+    static int32_t getLedByLabel(const char* label);
+
+private:
+    static const std::unordered_map<std::string, int> KEYCODES;
+
+    static const std::vector<InputEventLabel> KEY_NAMES;
+
+    static const std::unordered_map<std::string, int> AXES;
+
+    static const std::vector<InputEventLabel> AXES_NAMES;
+
+    static const std::unordered_map<std::string, int> LEDS;
+
+    static const std::unordered_map<std::string, int> FLAGS;
 };
 
-static const InputEventLabel AXES[] = {
-    DEFINE_AXIS(X),
-    DEFINE_AXIS(Y),
-    DEFINE_AXIS(PRESSURE),
-    DEFINE_AXIS(SIZE),
-    DEFINE_AXIS(TOUCH_MAJOR),
-    DEFINE_AXIS(TOUCH_MINOR),
-    DEFINE_AXIS(TOOL_MAJOR),
-    DEFINE_AXIS(TOOL_MINOR),
-    DEFINE_AXIS(ORIENTATION),
-    DEFINE_AXIS(VSCROLL),
-    DEFINE_AXIS(HSCROLL),
-    DEFINE_AXIS(Z),
-    DEFINE_AXIS(RX),
-    DEFINE_AXIS(RY),
-    DEFINE_AXIS(RZ),
-    DEFINE_AXIS(HAT_X),
-    DEFINE_AXIS(HAT_Y),
-    DEFINE_AXIS(LTRIGGER),
-    DEFINE_AXIS(RTRIGGER),
-    DEFINE_AXIS(THROTTLE),
-    DEFINE_AXIS(RUDDER),
-    DEFINE_AXIS(WHEEL),
-    DEFINE_AXIS(GAS),
-    DEFINE_AXIS(BRAKE),
-    DEFINE_AXIS(DISTANCE),
-    DEFINE_AXIS(TILT),
-    DEFINE_AXIS(SCROLL),
-    DEFINE_AXIS(RELATIVE_X),
-    DEFINE_AXIS(RELATIVE_Y),
-    DEFINE_AXIS(GENERIC_1),
-    DEFINE_AXIS(GENERIC_2),
-    DEFINE_AXIS(GENERIC_3),
-    DEFINE_AXIS(GENERIC_4),
-    DEFINE_AXIS(GENERIC_5),
-    DEFINE_AXIS(GENERIC_6),
-    DEFINE_AXIS(GENERIC_7),
-    DEFINE_AXIS(GENERIC_8),
-    DEFINE_AXIS(GENERIC_9),
-    DEFINE_AXIS(GENERIC_10),
-    DEFINE_AXIS(GENERIC_11),
-    DEFINE_AXIS(GENERIC_12),
-    DEFINE_AXIS(GENERIC_13),
-    DEFINE_AXIS(GENERIC_14),
-    DEFINE_AXIS(GENERIC_15),
-    DEFINE_AXIS(GENERIC_16),
-
-    // NOTE: If you add a new axis here you must also add it to several other files.
-    //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
-    { nullptr, 0 }
-};
-
-static const InputEventLabel LEDS[] = {
-    DEFINE_LED(NUM_LOCK),
-    DEFINE_LED(CAPS_LOCK),
-    DEFINE_LED(SCROLL_LOCK),
-    DEFINE_LED(COMPOSE),
-    DEFINE_LED(KANA),
-    DEFINE_LED(SLEEP),
-    DEFINE_LED(SUSPEND),
-    DEFINE_LED(MUTE),
-    DEFINE_LED(MISC),
-    DEFINE_LED(MAIL),
-    DEFINE_LED(CHARGING),
-    DEFINE_LED(CONTROLLER_1),
-    DEFINE_LED(CONTROLLER_2),
-    DEFINE_LED(CONTROLLER_3),
-    DEFINE_LED(CONTROLLER_4),
-
-    // NOTE: If you add new LEDs here, you must also add them to Input.h
-    { nullptr, 0 }
-};
-
-static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
-                                        DEFINE_FLAG(FUNCTION),
-                                        DEFINE_FLAG(GESTURE),
-                                        DEFINE_FLAG(WAKE),
-
-                                        {nullptr, 0}};
-
-static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
-    while (list->literal) {
-        if (strcmp(literal, list->literal) == 0) {
-            return list->value;
-        }
-        list++;
-    }
-    return list->value;
-}
-
-static const char* lookupLabelByValue(int value, const InputEventLabel* list) {
-    while (list->literal) {
-        if (list->value == value) {
-            return list->literal;
-        }
-        list++;
-    }
-    return nullptr;
-}
-
-static inline int32_t getKeyCodeByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(label, KEYCODES));
-}
-
-static inline const char* getLabelByKeyCode(int32_t keyCode) {
-    if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
-        return KEYCODES[keyCode].literal;
-    }
-    return nullptr;
-}
-
-static inline uint32_t getKeyFlagByLabel(const char* label) {
-    return uint32_t(lookupValueByLabel(label, FLAGS));
-}
-
-static inline int32_t getAxisByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(label, AXES));
-}
-
-static inline const char* getAxisLabel(int32_t axisId) {
-    return lookupLabelByValue(axisId, AXES);
-}
-
-static inline int32_t getLedByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(label, LEDS));
-}
-
-
 } // namespace android
 #endif // _LIBINPUT_INPUT_EVENT_LABELS_H
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 7ca9031..ad0a14e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -34,12 +34,14 @@
 #include <android-base/chrono_utils.h>
 
 #include <binder/IBinder.h>
+#include <binder/Parcelable.h>
 #include <input/Input.h>
+#include <sys/stat.h>
+#include <ui/Transform.h>
 #include <utils/BitSet.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
-#include <utils/Vector.h>
 
 #include <android-base/unique_fd.h>
 
@@ -69,10 +71,7 @@
 
     struct Header {
         Type type; // 4 bytes
-        // We don't need this field in order to align the body below but we
-        // leave it here because InputMessage::size() and other functions
-        // compute the size of this structure as sizeof(Header) + sizeof(Body).
-        uint32_t padding;
+        uint32_t seq;
     } header;
 
     // Body *must* be 8 byte aligned.
@@ -81,8 +80,8 @@
     static_assert(sizeof(std::array<uint8_t, 32>) == 32);
     union Body {
         struct Key {
-            uint32_t seq;
             int32_t eventId;
+            uint32_t empty1;
             nsecs_t eventTime __attribute__((aligned(8)));
             int32_t deviceId;
             int32_t source;
@@ -101,8 +100,8 @@
         } key;
 
         struct Motion {
-            uint32_t seq;
             int32_t eventId;
+            uint32_t empty1;
             nsecs_t eventTime __attribute__((aligned(8)));
             int32_t deviceId;
             int32_t source;
@@ -117,10 +116,12 @@
             uint8_t empty2[3];                   // 3 bytes to fill gap created by classification
             int32_t edgeFlags;
             nsecs_t downTime __attribute__((aligned(8)));
-            float xScale;
-            float yScale;
-            float xOffset;
-            float yOffset;
+            float dsdx;
+            float dtdx;
+            float dtdy;
+            float dsdy;
+            float tx;
+            float ty;
             float xPrecision;
             float yPrecision;
             float xCursorPosition;
@@ -151,16 +152,14 @@
         } motion;
 
         struct Finished {
-            uint32_t seq;
+            uint32_t empty1;
             uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
 
             inline size_t size() const { return sizeof(Finished); }
         } finished;
 
         struct Focus {
-            uint32_t seq;
             int32_t eventId;
-            uint32_t empty1;
             // The following two fields take up 4 bytes total
             uint16_t hasFocus;    // actually a bool
             uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
@@ -172,6 +171,19 @@
     bool isValid(size_t actualSize) const;
     size_t size() const;
     void getSanitizedCopy(InputMessage* msg) const;
+
+    static const char* typeToString(Type type) {
+        switch (type) {
+            case Type::KEY:
+                return "KEY";
+            case Type::MOTION:
+                return "MOTION";
+            case Type::FINISHED:
+                return "FINISHED";
+            case Type::FOCUS:
+                return "FOCUS";
+        }
+    }
 };
 
 /*
@@ -182,14 +194,15 @@
  *
  * The input channel is closed when all references to it are released.
  */
-class InputChannel : public RefBase {
-protected:
-    virtual ~InputChannel();
-
+class InputChannel : public Parcelable {
 public:
-    static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
-                                   sp<IBinder> token);
-
+    static std::unique_ptr<InputChannel> create(const std::string& name,
+                                                android::base::unique_fd fd, sp<IBinder> token);
+    InputChannel() = default;
+    InputChannel(const InputChannel& other)
+          : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
+    InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+    virtual ~InputChannel();
     /**
      * Create a pair of input channels.
      * The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -198,10 +211,12 @@
      * Return OK on success.
      */
     static status_t openInputChannelPair(const std::string& name,
-            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+                                         std::unique_ptr<InputChannel>& outServerChannel,
+                                         std::unique_ptr<InputChannel>& outClientChannel);
 
     inline std::string getName() const { return mName; }
-    inline int getFd() const { return mFd.get(); }
+    inline const android::base::unique_fd& getFd() const { return mFd; }
+    inline sp<IBinder> getToken() const { return mToken; }
 
     /* Send a message to the other endpoint.
      *
@@ -229,10 +244,12 @@
     status_t receiveMessage(InputMessage* msg);
 
     /* Return a new object that has a duplicate of this channel's fd. */
-    sp<InputChannel> dup() const;
+    std::unique_ptr<InputChannel> dup() const;
 
-    status_t write(Parcel& out) const;
-    static sp<InputChannel> read(const Parcel& from);
+    void copyTo(InputChannel& outChannel) const;
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
+    status_t writeToParcel(android::Parcel* parcel) const override;
 
     /**
      * The connection token is used to identify the input connection, i.e.
@@ -248,8 +265,22 @@
      */
     sp<IBinder> getConnectionToken() const;
 
+    bool operator==(const InputChannel& inputChannel) const {
+        struct stat lhs, rhs;
+        if (fstat(mFd.get(), &lhs) != 0) {
+            return false;
+        }
+        if (fstat(inputChannel.getFd(), &rhs) != 0) {
+            return false;
+        }
+        // If file descriptors are pointing to same inode they are duplicated fds.
+        return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken &&
+                lhs.st_ino == rhs.st_ino;
+    }
+
 private:
-    InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
+    base::unique_fd dupFd() const;
+
     std::string mName;
     android::base::unique_fd mFd;
 
@@ -262,13 +293,13 @@
 class InputPublisher {
 public:
     /* Creates a publisher associated with an input channel. */
-    explicit InputPublisher(const sp<InputChannel>& channel);
+    explicit InputPublisher(const std::shared_ptr<InputChannel>& channel);
 
     /* Destroys the publisher and releases its input channel. */
     ~InputPublisher();
 
     /* Gets the underlying input channel. */
-    inline sp<InputChannel> getChannel() { return mChannel; }
+    inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
 
     /* Publishes a key event to the input channel.
      *
@@ -295,11 +326,10 @@
                                 int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
                                 int32_t actionButton, int32_t flags, int32_t edgeFlags,
                                 int32_t metaState, int32_t buttonState,
-                                MotionClassification classification, float xScale, float yScale,
-                                float xOffset, float yOffset, float xPrecision, float yPrecision,
-                                float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-                                nsecs_t eventTime, uint32_t pointerCount,
-                                const PointerProperties* pointerProperties,
+                                MotionClassification classification, const ui::Transform& transform,
+                                float xPrecision, float yPrecision, float xCursorPosition,
+                                float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+                                uint32_t pointerCount, const PointerProperties* pointerProperties,
                                 const PointerCoords* pointerCoords);
 
     /* Publishes a focus event to the input channel.
@@ -325,8 +355,7 @@
     status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
 
 private:
-
-    sp<InputChannel> mChannel;
+    std::shared_ptr<InputChannel> mChannel;
 };
 
 /*
@@ -335,13 +364,13 @@
 class InputConsumer {
 public:
     /* Creates a consumer associated with an input channel. */
-    explicit InputConsumer(const sp<InputChannel>& channel);
+    explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
 
     /* Destroys the consumer and releases its input channel. */
     ~InputConsumer();
 
     /* Gets the underlying input channel. */
-    inline sp<InputChannel> getChannel() { return mChannel; }
+    inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
 
     /* Consumes an input event from the input channel and copies its contents into
      * an InputEvent object created using the specified factory.
@@ -411,12 +440,13 @@
      */
     int32_t getPendingBatchSource() const;
 
+    std::string dump() const;
+
 private:
     // True if touch resampling is enabled.
     const bool mResampleTouch;
 
-    // The input channel.
-    sp<InputChannel> mChannel;
+    std::shared_ptr<InputChannel> mChannel;
 
     // The current input message.
     InputMessage mMsg;
@@ -427,9 +457,9 @@
 
     // Batched motion events per device and source.
     struct Batch {
-        Vector<InputMessage> samples;
+        std::vector<InputMessage> samples;
     };
-    Vector<Batch> mBatches;
+    std::vector<Batch> mBatches;
 
     // Touch state per device and source, only for sources of class pointer.
     struct History {
@@ -516,7 +546,7 @@
             return false;
         }
     };
-    Vector<TouchState> mTouchStates;
+    std::vector<TouchState> mTouchStates;
 
     // Chain of batched sequence numbers.  When multiple input messages are combined into
     // a batch, we append a record here that associates the last sequence number in the
@@ -526,7 +556,7 @@
         uint32_t seq;   // sequence number of batched input message
         uint32_t chain; // sequence number of previous batched input message
     };
-    Vector<SeqChain> mSeqChains;
+    std::vector<SeqChain> mSeqChains;
 
     status_t consumeBatch(InputEventFactoryInterface* factory,
             nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 6740855..36097d6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,112 +17,117 @@
 #ifndef _UI_INPUT_WINDOW_H
 #define _UI_INPUT_WINDOW_H
 
+#include <android/os/TouchOcclusionMode.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <input/Flags.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/Transform.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
 #include "InputApplication.h"
 
+using android::os::TouchOcclusionMode;
+
 namespace android {
-class Parcel;
 
 /*
  * Describes the properties of a window that can receive input.
  */
-struct InputWindowInfo {
+struct InputWindowInfo : public Parcelable {
     InputWindowInfo() = default;
-    InputWindowInfo(const Parcel& from);
 
     // Window flags from WindowManager.LayoutParams
-    enum : uint32_t {
-        FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
-        FLAG_DIM_BEHIND = 0x00000002,
-        FLAG_BLUR_BEHIND = 0x00000004,
-        FLAG_NOT_FOCUSABLE = 0x00000008,
-        FLAG_NOT_TOUCHABLE = 0x00000010,
-        FLAG_NOT_TOUCH_MODAL = 0x00000020,
-        FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
-        FLAG_KEEP_SCREEN_ON = 0x00000080,
-        FLAG_LAYOUT_IN_SCREEN = 0x00000100,
-        FLAG_LAYOUT_NO_LIMITS = 0x00000200,
-        FLAG_FULLSCREEN = 0x00000400,
-        FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
-        FLAG_DITHER = 0x00001000,
-        FLAG_SECURE = 0x00002000,
-        FLAG_SCALED = 0x00004000,
-        FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
-        FLAG_LAYOUT_INSET_DECOR = 0x00010000,
-        FLAG_ALT_FOCUSABLE_IM = 0x00020000,
-        FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
-        FLAG_SHOW_WHEN_LOCKED = 0x00080000,
-        FLAG_SHOW_WALLPAPER = 0x00100000,
-        FLAG_TURN_SCREEN_ON = 0x00200000,
-        FLAG_DISMISS_KEYGUARD = 0x00400000,
-        FLAG_SPLIT_TOUCH = 0x00800000,
-        FLAG_HARDWARE_ACCELERATED = 0x01000000,
-        FLAG_LAYOUT_IN_OVERSCAN = 0x02000000,
-        FLAG_TRANSLUCENT_STATUS = 0x04000000,
-        FLAG_TRANSLUCENT_NAVIGATION = 0x08000000,
-        FLAG_LOCAL_FOCUS_MODE = 0x10000000,
-        FLAG_SLIPPERY = 0x20000000,
-        FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
-        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
-    };
+    enum class Flag : uint32_t {
+        ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+        DIM_BEHIND = 0x00000002,
+        BLUR_BEHIND = 0x00000004,
+        NOT_FOCUSABLE = 0x00000008,
+        NOT_TOUCHABLE = 0x00000010,
+        NOT_TOUCH_MODAL = 0x00000020,
+        TOUCHABLE_WHEN_WAKING = 0x00000040,
+        KEEP_SCREEN_ON = 0x00000080,
+        LAYOUT_IN_SCREEN = 0x00000100,
+        LAYOUT_NO_LIMITS = 0x00000200,
+        FULLSCREEN = 0x00000400,
+        FORCE_NOT_FULLSCREEN = 0x00000800,
+        DITHER = 0x00001000,
+        SECURE = 0x00002000,
+        SCALED = 0x00004000,
+        IGNORE_CHEEK_PRESSES = 0x00008000,
+        LAYOUT_INSET_DECOR = 0x00010000,
+        ALT_FOCUSABLE_IM = 0x00020000,
+        WATCH_OUTSIDE_TOUCH = 0x00040000,
+        SHOW_WHEN_LOCKED = 0x00080000,
+        SHOW_WALLPAPER = 0x00100000,
+        TURN_SCREEN_ON = 0x00200000,
+        DISMISS_KEYGUARD = 0x00400000,
+        SPLIT_TOUCH = 0x00800000,
+        HARDWARE_ACCELERATED = 0x01000000,
+        LAYOUT_IN_OVERSCAN = 0x02000000,
+        TRANSLUCENT_STATUS = 0x04000000,
+        TRANSLUCENT_NAVIGATION = 0x08000000,
+        LOCAL_FOCUS_MODE = 0x10000000,
+        SLIPPERY = 0x20000000,
+        LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
+        DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
+    }; // Window types from WindowManager.LayoutParams
 
-    // Window types from WindowManager.LayoutParams
-    enum {
+    enum class Type : int32_t {
+        UNKNOWN = 0,
         FIRST_APPLICATION_WINDOW = 1,
-        TYPE_BASE_APPLICATION = 1,
-        TYPE_APPLICATION = 2,
-        TYPE_APPLICATION_STARTING = 3,
+        BASE_APPLICATION = 1,
+        APPLICATION = 2,
+        APPLICATION_STARTING = 3,
         LAST_APPLICATION_WINDOW = 99,
         FIRST_SUB_WINDOW = 1000,
-        TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
-        TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
-        TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
-        TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
-        TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
+        APPLICATION_PANEL = FIRST_SUB_WINDOW,
+        APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
+        APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
+        APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
+        APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
         LAST_SUB_WINDOW = 1999,
         FIRST_SYSTEM_WINDOW = 2000,
-        TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
-        TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
-        TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2,
-        TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
-        TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
-        TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5,
-        TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
-        TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
-        TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
-        TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
-        TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
-        TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
-        TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
-        TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
-        TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
-        TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
-        TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16,
-        TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
-        TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18,
-        TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
-        TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
-        TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
-        TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
-        TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
-        TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
-        TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
-        TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
-        TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
-        TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+        STATUS_BAR = FIRST_SYSTEM_WINDOW,
+        SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
+        PHONE = FIRST_SYSTEM_WINDOW + 2,
+        SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
+        KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
+        TOAST = FIRST_SYSTEM_WINDOW + 5,
+        SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
+        PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
+        SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
+        KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
+        SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
+        INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
+        INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
+        WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
+        STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
+        SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
+        DRAG = FIRST_SYSTEM_WINDOW + 16,
+        STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
+        POINTER = FIRST_SYSTEM_WINDOW + 18,
+        NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
+        VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
+        BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
+        INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
+        NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
+        MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
+        ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
+        DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+        ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
+        NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
         LAST_SYSTEM_WINDOW = 2999,
     };
 
-    enum {
-        INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
-        INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
-        INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
+    enum class Feature {
+        DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
+        NO_INPUT_CHANNEL = 0x00000002,
+        DISABLE_USER_ACTIVITY = 0x00000004,
     };
 
     /* These values are filled in by the WM and passed through SurfaceFlinger
@@ -134,9 +139,9 @@
     // This uniquely identifies the input window.
     int32_t id = -1;
     std::string name;
-    int32_t layoutParamsFlags = 0;
-    int32_t layoutParamsType = 0;
-    nsecs_t dispatchingTimeout = -1;
+    Flags<Flag> flags;
+    Type type = Type::UNKNOWN;
+    std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
 
     /* These values are filled in by SurfaceFlinger. */
     int32_t frameLeft = -1;
@@ -156,9 +161,12 @@
     // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
     float globalScaleFactor = 1.0f;
 
-    // Scaling factors applied to individual windows.
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
+    // The opacity of this window, from 0.0 to 1.0 (inclusive).
+    // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
+    float alpha;
+
+    // Transform applied to individual windows.
+    ui::Transform transform;
 
     /*
      * This is filled in by the WM relative to the frame and then translated
@@ -166,8 +174,7 @@
      */
     Region touchableRegion;
     bool visible = false;
-    bool canReceiveKeys = false;
-    bool hasFocus = false;
+    bool focusable = false;
     bool hasWallpaper = false;
     bool paused = false;
     /* This flag is set when the window is of a trusted type that is allowed to silently
@@ -176,9 +183,11 @@
      * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
      */
     bool trustedOverlay = false;
+    TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
     int32_t ownerPid = -1;
     int32_t ownerUid = -1;
-    int32_t inputFeatures = 0;
+    std::string packageName;
+    Flags<Feature> inputFeatures;
     int32_t displayId = ADISPLAY_ID_NONE;
     int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
@@ -195,13 +204,13 @@
 
     bool overlaps(const InputWindowInfo* other) const;
 
-    status_t write(Parcel& output) const;
+    bool operator==(const InputWindowInfo& inputChannel) const;
 
-    static InputWindowInfo read(const Parcel& from);
+    status_t writeToParcel(android::Parcel* parcel) const override;
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
 };
 
-std::string inputWindowFlagsToString(uint32_t flags);
-
 /*
  * Handle for a window that can receive input.
  *
@@ -210,26 +219,19 @@
  */
 class InputWindowHandle : public RefBase {
 public:
+    explicit InputWindowHandle();
+    InputWindowHandle(const InputWindowHandle& other);
+    InputWindowHandle(const InputWindowInfo& other);
 
-    inline const InputWindowInfo* getInfo() const {
-        return &mInfo;
-    }
+    inline const InputWindowInfo* getInfo() const { return &mInfo; }
 
     sp<IBinder> getToken() const;
 
     int32_t getId() const { return mInfo.id; }
 
-    sp<IBinder> getApplicationToken() {
-        return mInfo.applicationInfo.token;
-    }
+    sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; }
 
-    inline std::string getName() const {
-        return !mInfo.name.empty() ? mInfo.name : "<invalid>";
-    }
-
-    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
-        return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
-    }
+    inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; }
 
     inline std::chrono::nanoseconds getDispatchingTimeout(
             std::chrono::nanoseconds defaultValue) const {
@@ -239,13 +241,14 @@
     /**
      * Requests that the state of this object be updated to reflect
      * the most current available information about the application.
+     * As this class is created as RefBase object, no pure virtual function is allowed.
      *
      * This method should only be called from within the input dispatcher's
      * critical section.
      *
      * Returns true on success, or false if the handle is no longer valid.
      */
-    virtual bool updateInfo() = 0;
+    virtual bool updateInfo() { return false; }
 
     /**
      * Updates from another input window handle.
@@ -258,13 +261,15 @@
      */
     void releaseChannel();
 
+    // Not override since this class is not derrived from Parcelable.
+    status_t readFromParcel(const android::Parcel* parcel);
+    status_t writeToParcel(android::Parcel* parcel) const;
+
 protected:
-    explicit InputWindowHandle();
     virtual ~InputWindowHandle();
 
     InputWindowInfo mInfo;
 };
-
 } // namespace android
 
 #endif // _UI_INPUT_WINDOW_H
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index a1a32a6..23f8ddf 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,16 +19,16 @@
 
 #include <stdint.h>
 
-#ifdef __ANDROID__
+#ifdef __linux__
 #include <binder/IBinder.h>
 #endif
 
+#include <android-base/result.h>
 #include <input/Input.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
 #include <utils/Unicode.h>
-#include <utils/RefBase.h>
 
 // Maximum number of keys supported by KeyCharacterMaps
 #define MAX_KEYS 8192
@@ -42,29 +42,29 @@
  *
  * This object is immutable after it has been loaded.
  */
-class KeyCharacterMap : public RefBase {
+class KeyCharacterMap {
 public:
-    enum KeyboardType {
-        KEYBOARD_TYPE_UNKNOWN = 0,
-        KEYBOARD_TYPE_NUMERIC = 1,
-        KEYBOARD_TYPE_PREDICTIVE = 2,
-        KEYBOARD_TYPE_ALPHA = 3,
-        KEYBOARD_TYPE_FULL = 4,
+    enum class KeyboardType : int32_t {
+        UNKNOWN = 0,
+        NUMERIC = 1,
+        PREDICTIVE = 2,
+        ALPHA = 3,
+        FULL = 4,
         /**
          * Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead.
          */
-        KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
-        KEYBOARD_TYPE_OVERLAY = 6,
+        SPECIAL_FUNCTION = 5,
+        OVERLAY = 6,
     };
 
-    enum Format {
+    enum class Format {
         // Base keyboard layout, may contain device-specific options, such as "type" declaration.
-        FORMAT_BASE = 0,
+        BASE = 0,
         // Overlay keyboard layout, more restrictive, may be published by applications,
         // cannot override device-specific options.
-        FORMAT_OVERLAY = 1,
+        OVERLAY = 1,
         // Either base or overlay layout ok.
-        FORMAT_ANY = 2,
+        ANY = 2,
     };
 
     // Substitute key code and meta state for fallback action.
@@ -74,21 +74,21 @@
     };
 
     /* Loads a key character map from a file. */
-    static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
+    static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+                                                               Format format);
 
     /* Loads a key character map from its string contents. */
-    static status_t loadContents(const std::string& filename,
-            const char* contents, Format format, sp<KeyCharacterMap>* outMap);
+    static base::Result<std::shared_ptr<KeyCharacterMap>> loadContents(const std::string& filename,
+                                                                       const char* contents,
+                                                                       Format format);
 
-    /* Combines a base key character map and an overlay. */
-    static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
-            const sp<KeyCharacterMap>& overlay);
+    const std::string getLoadFileName() const;
 
-    /* Returns an empty key character map. */
-    static sp<KeyCharacterMap> empty();
+    /* Combines this key character map with an overlay. */
+    void combine(const KeyCharacterMap& overlay);
 
     /* Gets the keyboard type. */
-    int32_t getKeyboardType() const;
+    KeyboardType getKeyboardType() const;
 
     /* Gets the primary character for this key as in the label physically printed on it.
      * Returns 0 if none (eg. for non-printing keys). */
@@ -134,15 +134,16 @@
     void tryRemapKey(int32_t scanCode, int32_t metaState,
             int32_t* outKeyCode, int32_t* outMetaState) const;
 
-#ifdef __ANDROID__
+#ifdef __linux__
     /* Reads a key map from a parcel. */
-    static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
+    static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
 
     /* Writes a key map to a parcel. */
     void writeToParcel(Parcel* parcel) const;
 #endif
 
-protected:
+    KeyCharacterMap(const KeyCharacterMap& other);
+
     virtual ~KeyCharacterMap();
 
 private:
@@ -224,16 +225,14 @@
         status_t parseCharacterLiteral(char16_t* outCharacter);
     };
 
-    static sp<KeyCharacterMap> sEmpty;
-
     KeyedVector<int32_t, Key*> mKeys;
-    int mType;
+    KeyboardType mType;
+    std::string mLoadFileName;
 
     KeyedVector<int32_t, int32_t> mKeysByScanCode;
     KeyedVector<int32_t, int32_t> mKeysByUsageCode;
 
     KeyCharacterMap();
-    KeyCharacterMap(const KeyCharacterMap& other);
 
     bool getKey(int32_t keyCode, const Key** outKey) const;
     bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -242,7 +241,7 @@
 
     bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
 
-    static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
+    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);
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 26f3501..872dd45 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,11 +17,12 @@
 #ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
 #define _LIBINPUT_KEY_LAYOUT_MAP_H
 
+#include <android-base/result.h>
 #include <stdint.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
 #include <utils/RefBase.h>
+#include <utils/Tokenizer.h>
 
 namespace android {
 
@@ -60,9 +61,12 @@
  *
  * This object is immutable after it has been loaded.
  */
-class KeyLayoutMap : public RefBase {
+class KeyLayoutMap {
 public:
-    static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
+                                                                    const char* contents);
 
     status_t mapKey(int32_t scanCode, int32_t usageCode,
             int32_t* outKeyCode, uint32_t* outFlags) const;
@@ -71,8 +75,8 @@
     status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
 
     status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
+    const std::string getLoadFileName() const;
 
-protected:
     virtual ~KeyLayoutMap();
 
 private:
@@ -91,6 +95,7 @@
     KeyedVector<int32_t, AxisInfo> mAxes;
     KeyedVector<int32_t, Led> mLedsByScanCode;
     KeyedVector<int32_t, Led> mLedsByUsageCode;
+    std::string mLoadFileName;
 
     KeyLayoutMap();
 
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 92da10c..08ad8c6 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -20,8 +20,8 @@
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
+#include <input/PropertyMap.h>
 #include <utils/Errors.h>
-#include <utils/PropertyMap.h>
 
 namespace android {
 
@@ -34,10 +34,10 @@
 class KeyMap {
 public:
     std::string keyLayoutFile;
-    sp<KeyLayoutMap> keyLayoutMap;
+    std::shared_ptr<KeyLayoutMap> keyLayoutMap;
 
     std::string keyCharacterMapFile;
-    sp<KeyCharacterMap> keyCharacterMap;
+    std::shared_ptr<KeyCharacterMap> keyCharacterMap;
 
     KeyMap();
     ~KeyMap();
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
new file mode 100644
index 0000000..1d987fe
--- /dev/null
+++ b/include/input/NamedEnum.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+#ifndef __UI_INPUT_NAMEDENUM_H
+#define __UI_INPUT_NAMEDENUM_H
+
+namespace android {
+
+namespace details {
+template <typename E, E V>
+constexpr std::optional<std::string_view> enum_value_name() {
+    // Should look something like (but all on one line):
+    //   std::optional<std::string_view>
+    //   android::details::enum_value_name()
+    //   [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
+    std::string_view view = __PRETTY_FUNCTION__;
+    size_t templateStart = view.rfind("[");
+    size_t templateEnd = view.rfind("]");
+    if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+        return std::nullopt;
+    }
+
+    // Extract the template parameters without the enclosing braces.
+    // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
+    view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+    size_t valStart = view.rfind("V = ");
+    if (valStart == std::string::npos) {
+        return std::nullopt;
+    }
+
+    // Example (cont'd): V = android::test::TestEnums::ONE
+    view = view.substr(valStart);
+    size_t nameStart = view.rfind("::");
+    if (nameStart == std::string::npos) {
+        return std::nullopt;
+    }
+
+    // Chop off the initial "::"
+    nameStart += 2;
+    return view.substr(nameStart);
+}
+
+template <typename E, typename T, T... I>
+constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
+    constexpr size_t count = seq.size();
+
+    std::array<E, count> values{};
+    for (size_t i = 0, v = 0; v < count; ++i) {
+        values[v++] = static_cast<E>(T{0} + i);
+    }
+
+    return values;
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_values =
+        generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
+
+template <typename E, std::size_t N, std::size_t... I>
+constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
+    return std::array<std::optional<std::string_view>, sizeof...(I)>{
+            {enum_value_name<E, enum_values<E, N>[I]>()...}};
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
+
+} // namespace details
+
+class NamedEnum {
+public:
+    // By default allowed enum value range is 0 ~ 7.
+    template <typename E>
+    static constexpr size_t max = 8;
+
+    template <auto V>
+    static constexpr auto enum_name() {
+        using E = decltype(V);
+        return details::enum_value_name<E, V>();
+    }
+
+    template <typename E>
+    static constexpr std::optional<std::string_view> enum_name(E val) {
+        auto idx = static_cast<size_t>(val);
+        return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
+    }
+
+    // Helper function for parsing enum value to string.
+    // Example : enum class TestEnums { ZERO = 0x0 };
+    // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
+    // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
+    // it should be declared to specialized the maximum enum by below:
+    // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
+    // If the enum class definition is sparse and contains enum values starting from a large value,
+    // Do not specialize it to a large number to avoid performance issues.
+    // The recommended maximum enum number to specialize is 64.
+    template <typename E>
+    static const std::string string(E val, const char* fallbackFormat = "0x%08x") {
+        std::string result;
+        std::optional<std::string_view> enumString = enum_name(val);
+        result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
+        return result;
+    }
+};
+
+} // namespace android
+
+#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
new file mode 100644
index 0000000..451918b
--- /dev/null
+++ b/include/input/PropertyMap.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <android-base/result.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+    /* Creates an empty property map. */
+    PropertyMap();
+    ~PropertyMap();
+
+    /* Clears the property map. */
+    void clear();
+
+    /* Adds a property.
+     * Replaces the property with the same key if it is already present.
+     */
+    void addProperty(const String8& key, const String8& value);
+
+    /* Returns true if the property map contains the specified key. */
+    bool hasProperty(const String8& key) const;
+
+    /* Gets the value of a property and parses it.
+     * Returns true and sets outValue if the key was found and its value was parsed successfully.
+     * Otherwise returns false and does not modify outValue.  (Also logs a warning.)
+     */
+    bool tryGetProperty(const String8& key, String8& outValue) const;
+    bool tryGetProperty(const String8& key, bool& outValue) const;
+    bool tryGetProperty(const String8& key, int32_t& outValue) const;
+    bool tryGetProperty(const String8& key, float& outValue) const;
+
+    /* Adds all values from the specified property map. */
+    void addAll(const PropertyMap* map);
+
+    /* Gets the underlying property map. */
+    inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
+
+    /* Loads a property map from a file. */
+    static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
+
+private:
+    class Parser {
+        PropertyMap* mMap;
+        Tokenizer* mTokenizer;
+
+    public:
+        Parser(PropertyMap* map, Tokenizer* tokenizer);
+        ~Parser();
+        status_t parse();
+
+    private:
+        status_t parseType();
+        status_t parseKey();
+        status_t parseKeyProperty();
+        status_t parseModifier(const String8& token, int32_t* outMetaState);
+        status_t parseCharacterLiteral(char16_t* outCharacter);
+    };
+
+    KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index ee010a3..886f1f7 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -96,7 +96,7 @@
     // are included in the movement.
     // The positions array contains position information for each pointer in order by
     // increasing id.  Its size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
@@ -149,7 +149,7 @@
     virtual void clear() = 0;
     virtual void clearPointers(BitSet32 idBits) = 0;
     virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions) = 0;
+                             const std::vector<VelocityTracker::Position>& positions) = 0;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
 };
 
@@ -180,8 +180,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -223,8 +223,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -257,8 +257,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -292,8 +292,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 44b8915..dd34c0a 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -21,58 +21,59 @@
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
-
 #include <powermanager/PowerHalWrapper.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
-using android::hardware::power::V1_0::Feature;
-using android::hardware::power::V1_0::PowerHint;
-
 namespace android {
 
+namespace power {
+
 // -------------------------------------------------------------------------------------------------
 
 // Connects to underlying Power HAL handles.
-class PowerHalConnector {
+class HalConnector {
 public:
-    PowerHalConnector() = default;
-    virtual ~PowerHalConnector() = default;
+    HalConnector() = default;
+    virtual ~HalConnector() = default;
 
-    virtual std::unique_ptr<PowerHalWrapper> connect();
+    virtual std::unique_ptr<HalWrapper> connect();
     virtual void reset();
 };
 
 // -------------------------------------------------------------------------------------------------
 
 // Controller for Power HAL handle.
-// This relies on PowerHalConnector to connect to the underlying Power HAL service and reconnects to
-// it after each failed api call. This also ensures connecting to the service is thread-safe.
-class PowerHalController : public PowerHalWrapper {
+// This relies on HalConnector to connect to the underlying Power HAL
+// service and reconnects to it after each failed api call. This also ensures
+// connecting to the service is thread-safe.
+class PowerHalController : public HalWrapper {
 public:
-    PowerHalController() : PowerHalController(std::make_unique<PowerHalConnector>()) {}
-    explicit PowerHalController(std::unique_ptr<PowerHalConnector> connector)
-        : mHalConnector(std::move(connector)) {}
+    PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {}
+    explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+          : mHalConnector(std::move(connector)) {}
+    virtual ~PowerHalController() = default;
 
     void init();
 
-    PowerHalResult setBoost(Boost boost, int32_t durationMs) override;
-    PowerHalResult setMode(Mode mode, bool enabled) override;
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
 
 private:
     std::mutex mConnectedHalMutex;
-    std::unique_ptr<PowerHalConnector> mHalConnector;
+    std::unique_ptr<HalConnector> mHalConnector;
 
-    // Shared pointers to keep global pointer and allow local copies to be used in different threads
-    std::shared_ptr<PowerHalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
-    const std::shared_ptr<PowerHalWrapper> mDefaultHal = std::make_shared<EmptyPowerHalWrapper>();
+    // Shared pointers to keep global pointer and allow local copies to be used in
+    // different threads
+    std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+    const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
 
-    std::shared_ptr<PowerHalWrapper> initHal();
-    PowerHalResult processHalResult(PowerHalResult result, const char* functionName);
+    std::shared_ptr<HalWrapper> initHal();
+    HalResult processHalResult(HalResult result, const char* functionName);
 };
 
 // -------------------------------------------------------------------------------------------------
 
+}; // namespace power
+
 }; // namespace android
 
 #endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index 487b95b..ed6f6f3 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -18,35 +18,36 @@
 #define ANDROID_POWERHALLOADER_H
 
 #include <android-base/thread_annotations.h>
-
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/IPower.h>
 
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerAidl = android::hardware::power::IPower;
-
 namespace android {
 
+namespace power {
+
 // Loads available Power HAL services.
 class PowerHalLoader {
 public:
     static void unloadAll();
-    static sp<IPowerAidl> loadAidl();
-    static sp<IPowerV1_0> loadHidlV1_0();
-    static sp<IPowerV1_1> loadHidlV1_1();
+    static sp<hardware::power::IPower> loadAidl();
+    static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
+    static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
 
 private:
     static std::mutex gHalMutex;
-    static sp<IPowerAidl> gHalAidl GUARDED_BY(gHalMutex);
-    static sp<IPowerV1_0> gHalHidlV1_0 GUARDED_BY(gHalMutex);
-    static sp<IPowerV1_1> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+    static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+    static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+    static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
 
-    static sp<IPowerV1_0> loadHidlV1_0Locked() EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+    static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+            EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
 
-    PowerHalLoader() = default;
+    PowerHalLoader() = delete;
+    ~PowerHalLoader() = delete;
 };
 
+}; // namespace power
+
 } // namespace android
 
 #endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 6d8a6eb..c3e7601 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -18,111 +18,111 @@
 #define ANDROID_POWERHALWRAPPER_H
 
 #include <android-base/thread_annotations.h>
-
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
-using android::hardware::power::V1_0::Feature;
-using android::hardware::power::V1_0::PowerHint;
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
-using IPowerAidl = android::hardware::power::IPower;
-
 namespace android {
 
+namespace power {
+
 // State of Power HAL support for individual apis.
-enum class PowerHalSupport {
+enum class HalSupport {
     UNKNOWN = 0,
     ON = 1,
     OFF = 2,
 };
 
 // State of the Power HAL api call result.
-enum class PowerHalResult {
+enum class HalResult {
     SUCCESSFUL = 0,
     FAILED = 1,
     UNSUPPORTED = 2,
 };
 
 // Wrapper for Power HAL handlers.
-class PowerHalWrapper {
+class HalWrapper {
 public:
-    virtual ~PowerHalWrapper() = default;
+    virtual ~HalWrapper() = default;
 
-    virtual PowerHalResult setBoost(Boost boost, int32_t durationMs) = 0;
-    virtual PowerHalResult setMode(Mode mode, bool enabled) = 0;
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
 };
 
 // Empty Power HAL wrapper that ignores all api calls.
-class EmptyPowerHalWrapper : public PowerHalWrapper {
+class EmptyHalWrapper : public HalWrapper {
 public:
-    EmptyPowerHalWrapper() = default;
-    ~EmptyPowerHalWrapper() = default;
+    EmptyHalWrapper() = default;
+    ~EmptyHalWrapper() = default;
 
-    PowerHalResult setBoost(Boost boost, int32_t durationMs) override;
-    PowerHalResult setMode(Mode mode, bool enabled) override;
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
 };
 
 // Wrapper for the HIDL Power HAL v1.0.
-class HidlPowerHalWrapperV1_0 : public PowerHalWrapper {
+class HidlHalWrapperV1_0 : public HalWrapper {
 public:
-    explicit HidlPowerHalWrapperV1_0(sp<IPowerV1_0> powerHal) : handleV1_0(std::move(powerHal)) {}
-    virtual ~HidlPowerHalWrapperV1_0() = default;
+    explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+          : mHandleV1_0(std::move(Hal)) {}
+    virtual ~HidlHalWrapperV1_0() = default;
 
-    PowerHalResult setBoost(Boost boost, int32_t durationMs) override;
-    PowerHalResult setMode(Mode mode, bool enabled) override;
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
 
 protected:
-    virtual PowerHalResult sendPowerHint(PowerHint hintId, uint32_t data);
+    virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
 
 private:
-    sp<IPowerV1_0> handleV1_0;
-    PowerHalResult setInteractive(bool enabled);
-    PowerHalResult setFeature(Feature feature, bool enabled);
+    sp<hardware::power::V1_0::IPower> mHandleV1_0;
+    HalResult setInteractive(bool enabled);
+    HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
 };
 
 // Wrapper for the HIDL Power HAL v1.1.
-class HidlPowerHalWrapperV1_1 : public HidlPowerHalWrapperV1_0 {
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
 public:
-    HidlPowerHalWrapperV1_1(sp<IPowerV1_0> powerHalV1_0, sp<IPowerV1_1> powerHalV1_1)
-        : HidlPowerHalWrapperV1_0(powerHalV1_0), handleV1_1(std::move(powerHalV1_1)) {}
-    ~HidlPowerHalWrapperV1_1() = default;
+    HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
+                       sp<hardware::power::V1_1::IPower> handleV1_1)
+          : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+    virtual ~HidlHalWrapperV1_1() = default;
 
 protected:
-    virtual PowerHalResult sendPowerHint(PowerHint hintId, uint32_t data) override;
+    virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+                                    uint32_t data) override;
 
 private:
-    sp<IPowerV1_1> handleV1_1;
+    sp<hardware::power::V1_1::IPower> mHandleV1_1;
 };
 
 // Wrapper for the AIDL Power HAL.
-class AidlPowerHalWrapper : public PowerHalWrapper {
+class AidlHalWrapper : public HalWrapper {
 public:
-    explicit AidlPowerHalWrapper(sp<IPowerAidl> powerHal) : handle(std::move(powerHal)) {}
-    ~AidlPowerHalWrapper() = default;
+    explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+    virtual ~AidlHalWrapper() = default;
 
-    PowerHalResult setBoost(Boost boost, int32_t durationMs) override;
-    PowerHalResult setMode(Mode mode, bool enabled) override;
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
 
 private:
     // Control access to the boost and mode supported arrays.
     std::mutex mBoostMutex;
     std::mutex mModeMutex;
-    sp<IPowerAidl> handle;
+    sp<hardware::power::IPower> mHandle;
     // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
     // Need to increase the array size if more boost supported.
-    std::array<std::atomic<PowerHalSupport>, static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT)+1>
-        boostSupportedArray GUARDED_BY(mBoostMutex) = {PowerHalSupport::UNKNOWN};
+    std::array<std::atomic<HalSupport>,
+               static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+            mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
     // Android framework only sends mode upto DISPLAY_INACTIVE.
     // Need to increase the array if more mode supported.
-    std::array<std::atomic<PowerHalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE)+1>
-        modeSupportedArray GUARDED_BY(mModeMutex) = {PowerHalSupport::UNKNOWN};
+    std::array<std::atomic<HalSupport>,
+               static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+            mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
 };
 
+}; // namespace power
+
 }; // namespace android
 
 #endif // ANDROID_POWERHALWRAPPER_H
diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h
new file mode 120000
index 0000000..095d2ce
--- /dev/null
+++ b/include/ui/Rotation.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rotation.h
\ No newline at end of file
diff --git a/include/ui/Transform.h b/include/ui/Transform.h
new file mode 120000
index 0000000..323f1fd
--- /dev/null
+++ b/include/ui/Transform.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 5a0d3f6..dae6eeb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -83,28 +83,28 @@
         InitFrameworkHandlers();
         epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
         if (epoll_fd_ == -1) {
-            PLOG(FATAL) << "failed to create epoll fd";
+            PLOG(FATAL) << "adbd_auth: failed to create epoll fd";
         }
 
         event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
         if (event_fd_ == -1) {
-            PLOG(FATAL) << "failed to create eventfd";
+            PLOG(FATAL) << "adbd_auth: failed to create eventfd";
         }
 
         sock_fd_.reset(android_get_control_socket("adbd"));
         if (sock_fd_ == -1) {
-            PLOG(ERROR) << "failed to get adbd authentication socket";
+            PLOG(ERROR) << "adbd_auth: failed to get adbd authentication socket";
         } else {
             if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
-                PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+                PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket cloexec";
             }
 
             if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
-                PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+                PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket nonblocking";
             }
 
             if (listen(sock_fd_.get(), 4) != 0) {
-                PLOG(FATAL) << "failed to listen on adbd authentication socket";
+                PLOG(FATAL) << "adbd_auth: failed to listen on adbd authentication socket";
             }
         }
     }
@@ -146,7 +146,7 @@
             struct epoll_event event;
             event.events = EPOLLIN;
             if (!output_queue_.empty()) {
-                LOG(INFO) << "marking framework writable";
+                LOG(INFO) << "adbd_auth: marking framework writable";
                 event.events |= EPOLLOUT;
             }
             event.data.u64 = kEpollConstFramework;
@@ -155,7 +155,7 @@
     }
 
     void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
-        LOG(INFO) << "received new framework fd " << new_fd.get()
+        LOG(INFO) << "adbd_auth: received new framework fd " << new_fd.get()
                   << " (current = " << framework_fd_.get() << ")";
 
         // If we already had a framework fd, clean up after ourselves.
@@ -170,7 +170,7 @@
             struct epoll_event event;
             event.events = EPOLLIN;
             if (!output_queue_.empty()) {
-                LOG(INFO) << "marking framework writable";
+                LOG(INFO) << "adbd_auth: marking framework writable";
                 event.events |= EPOLLOUT;
             }
             event.data.u64 = kEpollConstFramework;
@@ -180,10 +180,10 @@
     }
 
     void HandlePacket(std::string_view packet) EXCLUDES(mutex_) {
-        LOG(INFO) << "received packet: " << packet;
+        LOG(INFO) << "adbd_auth: received packet: " << packet;
 
         if (packet.size() < 2) {
-            LOG(ERROR) << "received packet of invalid length";
+            LOG(ERROR) << "adbd_auth: received packet of invalid length";
             std::lock_guard<std::mutex> lock(mutex_);
             ReplaceFrameworkFd(unique_fd());
         }
@@ -197,7 +197,7 @@
             }
         }
         if (!handled_packet) {
-            LOG(ERROR) << "unhandled packet: " << packet;
+            LOG(ERROR) << "adbd_auth: unhandled packet: " << packet;
             std::lock_guard<std::mutex> lock(mutex_);
             ReplaceFrameworkFd(unique_fd());
         }
@@ -206,12 +206,18 @@
     void AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) {
         std::lock_guard<std::mutex> lock(mutex_);
         CHECK(buf.empty());
-        CHECK(dispatched_prompt_.has_value());
-        auto& [id, key, arg] = *dispatched_prompt_;
-        keys_.emplace(id, std::move(key));
 
-        callbacks_.key_authorized(arg, id);
-        dispatched_prompt_ = std::nullopt;
+        if (dispatched_prompt_.has_value()) {
+            // It's possible for the framework to send us a response without our having sent a
+            // request to it: e.g. if adbd restarts while we have a pending request.
+            auto& [id, key, arg] = *dispatched_prompt_;
+            keys_.emplace(id, std::move(key));
+
+            callbacks_.key_authorized(arg, id);
+            dispatched_prompt_ = std::nullopt;
+        } else {
+            LOG(WARNING) << "adbd_auth: received authorization for unknown prompt, ignoring";
+        }
 
         // We need to dispatch pending prompts here upon success as well,
         // since we might have multiple queued prompts.
@@ -273,14 +279,14 @@
             iovs[2].iov_base = p->public_key.data();
             iovs[2].iov_len = p->public_key.size();
         } else {
-            LOG(FATAL) << "unhandled packet type?";
+            LOG(FATAL) << "adbd_auth: unhandled packet type?";
         }
 
         output_queue_.pop_front();
 
         ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
         if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
-            PLOG(ERROR) << "failed to write to framework fd";
+            PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
             ReplaceFrameworkFd(unique_fd());
             return false;
         }
@@ -290,7 +296,7 @@
 
     void Run() {
         if (sock_fd_ == -1) {
-            LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+            LOG(ERROR) << "adbd_auth: socket unavailable, disabling user prompts";
         } else {
             struct epoll_event event;
             event.events = EPOLLIN;
@@ -309,9 +315,9 @@
             struct epoll_event events[3];
             int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
             if (rc == -1) {
-                PLOG(FATAL) << "epoll_wait failed";
+                PLOG(FATAL) << "adbd_auth: epoll_wait failed";
             } else if (rc == 0) {
-                LOG(FATAL) << "epoll_wait returned 0";
+                LOG(FATAL) << "adbd_auth: epoll_wait returned 0";
             }
 
             bool restart = false;
@@ -326,7 +332,7 @@
                         unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
                                                            SOCK_CLOEXEC | SOCK_NONBLOCK));
                         if (new_framework_fd == -1) {
-                            PLOG(FATAL) << "failed to accept framework fd";
+                            PLOG(FATAL) << "adbd_auth: failed to accept framework fd";
                         }
 
                         LOG(INFO) << "adbd_auth: received a new framework connection";
@@ -344,7 +350,8 @@
                         uint64_t dummy;
                         int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
                         if (rc != 8) {
-                            PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+                            PLOG(FATAL)
+                                    << "adbd_auth: failed to read from eventfd (rc = " << rc << ")";
                         }
 
                         std::lock_guard<std::mutex> lock(mutex_);
@@ -357,9 +364,9 @@
                         if (event.events & EPOLLIN) {
                             int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
                             if (rc == -1) {
-                                LOG(FATAL) << "failed to read from framework fd";
+                                LOG(FATAL) << "adbd_auth: failed to read from framework fd";
                             } else if (rc == 0) {
-                                LOG(INFO) << "hit EOF on framework fd";
+                                LOG(INFO) << "adbd_auth: hit EOF on framework fd";
                                 std::lock_guard<std::mutex> lock(mutex_);
                                 ReplaceFrameworkFd(unique_fd());
                             } else {
@@ -386,10 +393,10 @@
     void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) {
         for (const auto& path : key_paths) {
             if (access(path, R_OK) == 0) {
-                LOG(INFO) << "Loading keys from " << path;
+                LOG(INFO) << "adbd_auth: loading keys from " << path;
                 std::string content;
                 if (!android::base::ReadFileToString(path, &content)) {
-                    PLOG(ERROR) << "Couldn't read " << path;
+                    PLOG(ERROR) << "adbd_auth: couldn't read " << path;
                     continue;
                 }
                 for (const auto& line : android::base::Split(content, "\n")) {
@@ -405,6 +412,7 @@
         uint64_t id = NextId();
 
         std::lock_guard<std::mutex> lock(mutex_);
+        LOG(INFO) << "adbd_auth: sending prompt with id " << id;
         pending_prompts_.emplace_back(id, public_key, arg);
         DispatchPendingPrompt();
         return id;
@@ -423,7 +431,7 @@
         std::lock_guard<std::mutex> lock(mutex_);
         auto it = keys_.find(id);
         if (it == keys_.end()) {
-            LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+            LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection, skipping";
             return;
         }
         output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
@@ -446,7 +454,8 @@
         std::lock_guard<std::mutex> lock(mutex_);
         auto it = keys_.find(id);
         if (it == keys_.end()) {
-            LOG(DEBUG) << "couldn't find public key to notify disconnection of tls device, skipping";
+            LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection of tls "
+                          "device, skipping";
             return;
         }
         output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{
@@ -461,9 +470,9 @@
         uint64_t value = 1;
         ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
         if (rc == -1) {
-            PLOG(FATAL) << "write to eventfd failed";
+            PLOG(FATAL) << "adbd_auth: write to eventfd failed";
         } else if (rc != sizeof(value)) {
-            LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+            LOG(FATAL) << "adbd_auth: write to eventfd returned short (" << rc << ")";
         }
     }
 
@@ -516,8 +525,9 @@
     if (callbacks->version == 1) {
         return new AdbdAuthContext(reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks));
     } else {
-      LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
-      return nullptr;
+        LOG(ERROR) << "adbd_auth: received unknown AdbdAuthCallbacks version "
+                   << callbacks->version;
+        return nullptr;
     }
 }
 
@@ -545,7 +555,12 @@
 
 void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
                            void* opaque) {
-    ctx->PromptUser(std::string_view(public_key, len), opaque);
+    adbd_auth_prompt_user_with_id(ctx, public_key, len, opaque);
+}
+
+uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx, const char* public_key, size_t len,
+                                       void* opaque) {
+    return ctx->PromptUser(std::string_view(public_key, len), opaque);
 }
 
 uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx,
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
index 6ee3166..8f834df 100644
--- a/libs/adbd_auth/include/adbd_auth.h
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -122,9 +122,23 @@
  * @param len the length of the public_key argument
  * @param arg an opaque userdata argument
  */
-void adbd_auth_prompt_user(AdbdAuthContext* ctx,
-                           const char* public_key,
-                           size_t len, void* opaque) __INTRODUCED_IN(30);
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* opaque)
+        __INTRODUCED_IN(30);
+
+/**
+ * Prompt the user to authorize a public key.
+ *
+ * When this happens, a callback will be run on the auth thread with the result.
+ *
+ * @param ctx the AdbdAuthContext
+ * @param public_key the RSA public key to prompt user with
+ * @param len the length of the public_key argument
+ * @param arg an opaque userdata argument
+ * @return a unique id which will be returned via callback
+ */
+__attribute__((weak)) uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx,
+                                                             const char* public_key, size_t len,
+                                                             void* opaque) __INTRODUCED_IN(30);
 
 /**
  * Let system_server know that a TLS device has connected.
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
index 5857ecb..7584ca3 100644
--- a/libs/adbd_auth/libadbd_auth.map.txt
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -7,6 +7,7 @@
     adbd_auth_notify_auth; # apex introduced=30
     adbd_auth_notify_disconnect; # apex introduced=30
     adbd_auth_prompt_user; # apex introduced=30
+    adbd_auth_prompt_user_with_id; # apex introduced=30
     adbd_auth_tls_device_connected; # apex introduced=30
     adbd_auth_tls_device_disconnected; # apex introduced=30
     adbd_auth_get_max_version; # apex introduced=30
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 09a5f39..cdd7764 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -35,6 +35,11 @@
     vendor_available: true,
     double_loadable: true,
     host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
 
     cflags: [
         "-Wall",
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index f66673f..80aa891 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -22,6 +22,8 @@
 
 cc_library_headers {
     name: "libarect_headers",
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
 }
 
@@ -29,6 +31,8 @@
     name: "libarect",
     host_supported: true,
     vendor_available: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
         windows: {
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
new file mode 100644
index 0000000..b85aecd
--- /dev/null
+++ b/libs/attestation/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+cc_library_static {
+    name: "libattestation",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "HmacKeyManager.cpp"
+    ],
+
+    clang: true,
+
+    shared_libs: [
+        "liblog",
+        "libcrypto",
+    ],
+}
\ No newline at end of file
diff --git a/libs/attestation/HmacKeyManager.cpp b/libs/attestation/HmacKeyManager.cpp
new file mode 100644
index 0000000..b15f143
--- /dev/null
+++ b/libs/attestation/HmacKeyManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <attestation/HmacKeyManager.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
+namespace android {
+
+static std::array<uint8_t, 128> getRandomKey() {
+    std::array<uint8_t, 128> key;
+    if (RAND_bytes(key.data(), key.size()) != 1) {
+        LOG_ALWAYS_FATAL("Can't generate HMAC key");
+    }
+    return key;
+}
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
+    // SHA256 always generates 32-bytes result
+    std::array<uint8_t, 32> hash;
+    unsigned int hashLen = 0;
+    uint8_t* result =
+            HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
+    if (result == nullptr) {
+        ALOGE("Could not sign the data using HMAC");
+        return INVALID_HMAC;
+    }
+
+    if (hashLen != hash.size()) {
+        ALOGE("HMAC-SHA256 has unexpected length");
+        return INVALID_HMAC;
+    }
+
+    return hash;
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
new file mode 100644
index 0000000..4dbb0ea
--- /dev/null
+++ b/libs/attestation/OWNERS
@@ -0,0 +1,2 @@
+chaviw@google.com
+svv@google.com
\ No newline at end of file
diff --git a/libs/attestation/TEST_MAPPING b/libs/attestation/TEST_MAPPING
new file mode 100644
index 0000000..43be638
--- /dev/null
+++ b/libs/attestation/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libattestation_tests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
new file mode 100644
index 0000000..6ce5ea1
--- /dev/null
+++ b/libs/attestation/tests/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "libattestation_tests",
+    test_suites: ["device-tests"],
+    srcs: [
+        "HmacKeyManager_test.cpp",
+    ],
+    static_libs: [
+        "libattestation",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcrypto",
+    ],
+}
diff --git a/libs/attestation/tests/HmacKeyManager_test.cpp b/libs/attestation/tests/HmacKeyManager_test.cpp
new file mode 100644
index 0000000..7f7a408
--- /dev/null
+++ b/libs/attestation/tests/HmacKeyManager_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <attestation/HmacKeyManager.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+    HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+    std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+
+    std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data));
+    std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data));
+    ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in the hmac verification data produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+    std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+    std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data));
+
+    data[2] = 2;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index d005058..727ea60 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -17,6 +17,7 @@
 #include <mutex>
 #include <unistd.h>
 
+#include <android/permission_manager.h>
 #include <binder/ActivityManager.h>
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
@@ -98,13 +99,16 @@
     return PROCESS_STATE_UNKNOWN;
 }
 
-bool ActivityManager::setSchedPolicyCgroup(const int32_t tid, const int32_t group)
-{
+status_t ActivityManager::checkPermission(const String16& permission,
+                                     const pid_t pid,
+                                     const uid_t uid,
+                                     int32_t* outResult) {
     sp<IActivityManager> service = getService();
     if (service != nullptr) {
-        return service->setSchedPolicyCgroup(tid, group);
+        return service->checkPermission(permission, pid, uid, outResult);
     }
-    return false;
+    // ActivityManagerService appears dead. Return usual error code for dead service.
+    return DEAD_OBJECT;
 }
 
 status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index db4aba8..26b997e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -17,6 +17,8 @@
     export_include_dirs: ["include"],
     vendor_available: true,
     host_supported: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
 
     header_libs: [
         "libbase_headers",
@@ -29,6 +31,11 @@
         "libutils_headers",
     ],
     min_sdk_version: "29",
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 // These interfaces are android-specific implementation unrelated to binder
@@ -62,6 +69,8 @@
     },
     double_loadable: true,
     host_supported: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
 
     // TODO(b/31559095): get headers from bionic on host
     include_dirs: [
@@ -74,7 +83,7 @@
     // or dessert updates. Instead, apex users should use libbinder_ndk.
     apex_available: [
         "//apex_available:platform",
-        // TODO(b/139016109) remove these three
+        // TODO(b/166468760) remove these three
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
     ],
@@ -95,6 +104,7 @@
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
         "Parcel.cpp",
+        "ParcelableHolder.cpp",
         "ParcelFileDescriptor.cpp",
         "PersistableBundle.cpp",
         "ProcessState.cpp",
@@ -102,7 +112,9 @@
         "Stability.cpp",
         "Status.cpp",
         "TextOutput.cpp",
+        "Utils.cpp",
         ":libbinder_aidl",
+        ":activity_manager_procstate_aidl",
     ],
 
     target: {
@@ -117,12 +129,19 @@
         vendor: {
             exclude_srcs: libbinder_device_interface_sources,
         },
+        darwin: {
+            enabled: false,
+        },
     },
 
     aidl: {
         export_aidl_headers: true,
     },
 
+    // TODO(b/142684679): for com.android.media which is compiled
+    // as vendor and used as system code.
+    use_apex_name_macro: true,
+
     cflags: [
         "-Wall",
         "-Wextra",
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 1c6b491..de42f36 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -22,6 +22,7 @@
 #include <utils/SystemClock.h>
 
 #include <sys/types.h>
+#include <private/android_filesystem_config.h>
 
 #ifdef LOG_TAG
 #undef LOG_TAG
@@ -100,7 +101,7 @@
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
             ? service->noteOperation(op, uid, callingPackage, attributionTag,
-                    shouldCollectNotes(op), message)
+                    shouldCollectNotes(op), message, uid == AID_SYSTEM)
             : AppOpsManager::MODE_IGNORED;
 
     return mode;
@@ -118,7 +119,8 @@
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
             ? service->startOperation(getClientId(), op, uid, callingPackage,
-                    attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+                    attributionTag, startIfModeDefault, shouldCollectNotes(op), message,
+                    uid == AID_SYSTEM)
             : AppOpsManager::MODE_IGNORED;
 
     return mode;
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e0fb543..f2d223d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -24,6 +24,7 @@
 #include <binder/IShellCallback.h>
 #include <binder/Parcel.h>
 
+#include <linux/sched.h>
 #include <stdio.h>
 
 namespace android {
@@ -133,6 +134,8 @@
     // unlocked objects
     bool mRequestingSid = false;
     sp<IBinder> mExtension;
+    int mPolicy = SCHED_NORMAL;
+    int mPriority = 0;
 
     // for below objects
     Mutex mLock;
@@ -170,6 +173,10 @@
 {
     data.setDataPosition(0);
 
+    if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
+        reply->markSensitive();
+    }
+
     status_t err = NO_ERROR;
     switch (code) {
         case PING_TRANSACTION:
@@ -279,6 +286,47 @@
     return e->mExtension;
 }
 
+void BBinder::setMinSchedulerPolicy(int policy, int priority) {
+    switch (policy) {
+    case SCHED_NORMAL:
+      LOG_ALWAYS_FATAL_IF(priority < -20 || priority > 19, "Invalid priority for SCHED_NORMAL: %d", priority);
+      break;
+    case SCHED_RR:
+    case SCHED_FIFO:
+      LOG_ALWAYS_FATAL_IF(priority < 1 || priority > 99, "Invalid priority for sched %d: %d", policy, priority);
+      break;
+    default:
+      LOG_ALWAYS_FATAL("Unrecognized scheduling policy: %d", policy);
+    }
+
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    if (e == nullptr) {
+        // Avoid allocations if called with default.
+        if (policy == SCHED_NORMAL && priority == 0) {
+            return;
+        }
+
+        e = getOrCreateExtras();
+        if (!e) return; // out of memory
+    }
+
+    e->mPolicy = policy;
+    e->mPriority = priority;
+}
+
+int BBinder::getMinSchedulerPolicy() {
+    Extras* e = mExtras.load(std::memory_order_acquire);
+    if (e == nullptr) return SCHED_NORMAL;
+    return e->mPolicy;
+}
+
+int BBinder::getMinSchedulerPriority() {
+    Extras* e = mExtras.load(std::memory_order_acquire);
+    if (e == nullptr) return 0;
+    return e->mPriority;
+}
+
 pid_t BBinder::getDebugPid() {
     return getpid();
 }
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index d2b9b8f..c183d29 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -224,7 +224,7 @@
             using android::internal::Stability;
 
             auto stability = Stability::get(this);
-            auto required = privateVendor ? Stability::VENDOR : Stability::kLocalStability;
+            auto required = privateVendor ? Stability::VENDOR : Stability::getLocalStability();
 
             if (CC_UNLIKELY(!Stability::check(stability, required))) {
                 ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index a3021122..e9f5aae 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,9 +17,11 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include <android/permission_manager.h>
 #include <binder/ActivityManager.h>
 #include <binder/IActivityManager.h>
 #include <binder/Parcel.h>
+#include <utils/Errors.h>
 
 namespace android {
 
@@ -105,15 +107,21 @@
         return reply.readInt32();
     }
 
-    virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group)
-    {
-         Parcel data, reply;
-         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
-         data.writeInt32(tid);
-         data.writeInt32(group);
-         remote()->transact(SET_SCHED_POLICY_CGROUP_TRANSACTION, data, &reply);
-         if (reply.readExceptionCode() != 0) return false;
-         return reply.readBool();
+    virtual status_t checkPermission(const String16& permission,
+                                    const pid_t pid,
+                                    const uid_t uid,
+                                    int32_t* outResult) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeString16(permission);
+        data.writeInt32(pid);
+        data.writeInt32(uid);
+        status_t err = remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
+        if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+            return err;
+        }
+        *outResult = reply.readInt32();
+        return NO_ERROR;
     }
 };
 
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index cd78866..ee0cd62 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -50,15 +50,16 @@
 
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
                 const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
-                const String16& message) {
+                const String16& message, bool shouldCollectMessage) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
         data.writeString16(attributionTag);
-        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeBool(shouldCollectAsyncNotedOp);
         data.writeString16(message);
+        data.writeBool(shouldCollectMessage);
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -67,7 +68,8 @@
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
                 const String16& packageName, const std::optional<String16>& attributionTag,
-                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
+                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+                bool shouldCollectMessage) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
@@ -75,9 +77,10 @@
         data.writeInt32(uid);
         data.writeString16(packageName);
         data.writeString16(attributionTag);
-        data.writeInt32(startIfModeDefault ? 1 : 0);
-        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeBool(startIfModeDefault);
+        data.writeBool(shouldCollectAsyncNotedOp);
         data.writeString16(message);
+        data.writeBool(shouldCollectMessage);
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -186,10 +189,11 @@
             String16 packageName = data.readString16();
             std::optional<String16> attributionTag;
             data.readString16(&attributionTag);
-            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            bool shouldCollectAsyncNotedOp = data.readBool();
             String16 message = data.readString16();
+            bool shouldCollectMessage = data.readBool();
             int32_t res = noteOperation(code, uid, packageName, attributionTag,
-                    shouldCollectAsyncNotedOp, message);
+                    shouldCollectAsyncNotedOp, message, shouldCollectMessage);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -202,11 +206,12 @@
             String16 packageName = data.readString16();
             std::optional<String16> attributionTag;
             data.readString16(&attributionTag);
-            bool startIfModeDefault = data.readInt32() == 1;
-            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            bool startIfModeDefault = data.readBool();
+            bool shouldCollectAsyncNotedOp = data.readBool();
             String16 message = data.readString16();
+            bool shouldCollectMessage = data.readBool();
             int32_t res = startOperation(token, code, uid, packageName, attributionTag,
-                    startIfModeDefault, shouldCollectAsyncNotedOp, message);
+                    startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index d67ce15..28ce935 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -448,6 +448,14 @@
     return mLastTransactionBinderFlags;
 }
 
+void IPCThreadState::setCallRestriction(ProcessState::CallRestriction restriction) {
+    mCallRestriction = restriction;
+}
+
+ProcessState::CallRestriction IPCThreadState::getCallRestriction() const {
+    return mCallRestriction;
+}
+
 void IPCThreadState::restoreCallingIdentity(int64_t token)
 {
     mCallingUid = (int)(token>>32);
@@ -614,7 +622,7 @@
     talkWithDriver(false);
 }
 
-int IPCThreadState::setupPolling(int* fd)
+status_t IPCThreadState::setupPolling(int* fd)
 {
     if (mProcess->mDriverFD < 0) {
         return -EBADF;
@@ -679,7 +687,7 @@
                 CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
                     ANDROID_LOG_ERROR);
             } else /* FATAL_IF_NOT_ONEWAY */ {
-                LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code);
+                LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code);
             }
         }
 
@@ -860,6 +868,10 @@
             err = FAILED_TRANSACTION;
             goto finish;
 
+        case BR_FROZEN_REPLY:
+            err = FAILED_TRANSACTION;
+            goto finish;
+
         case BR_ACQUIRE_RESULT:
             {
                 ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
@@ -883,21 +895,21 @@
                             tr.data_size,
                             reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                             tr.offsets_size/sizeof(binder_size_t),
-                            freeBuffer, this);
+                            freeBuffer);
                     } else {
                         err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                         freeBuffer(nullptr,
                             reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                             tr.data_size,
                             reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
-                            tr.offsets_size/sizeof(binder_size_t), this);
+                            tr.offsets_size/sizeof(binder_size_t));
                     }
                 } else {
                     freeBuffer(nullptr,
                         reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                         tr.data_size,
                         reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
-                        tr.offsets_size/sizeof(binder_size_t), this);
+                        tr.offsets_size/sizeof(binder_size_t));
                     continue;
                 }
             }
@@ -1171,7 +1183,7 @@
                 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                 tr.data_size,
                 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
-                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
+                tr.offsets_size/sizeof(binder_size_t), freeBuffer);
 
             const void* origServingStackPointer = mServingStackPointer;
             mServingStackPointer = &origServingStackPointer; // anything on the stack
@@ -1232,7 +1244,9 @@
             if ((tr.flags & TF_ONE_WAY) == 0) {
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                 if (error < NO_ERROR) reply.setError(error);
-                sendReply(reply, 0);
+
+                constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
+                sendReply(reply, (tr.flags & kForwardReplyFlags));
             } else {
                 if (error != OK || reply.dataSize() != 0) {
                     alog << "oneway function results will be dropped but finished with status "
@@ -1316,11 +1330,47 @@
         }
 }
 
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
+{
+    int ret = 0;
+    binder_frozen_status_info info;
+    info.pid = pid;
+
+#if defined(__ANDROID__)
+    if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+        ret = -errno;
+#endif
+    *sync_received = info.sync_recv;
+    *async_received = info.async_recv;
+
+    return ret;
+}
+
+status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
+    struct binder_freeze_info info;
+    int ret = 0;
+
+    info.pid = pid;
+    info.enable = enable;
+    info.timeout_ms = timeout_ms;
+
+
+#if defined(__ANDROID__)
+    if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)
+        ret = -errno;
+#endif
+
+    //
+    // ret==-EAGAIN indicates that transactions have not drained.
+    // Call again to poll for completion.
+    //
+    return ret;
+}
 
 void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
                                 size_t /*dataSize*/,
                                 const binder_size_t* /*objects*/,
-                                size_t /*objectsSize*/, void* /*cookie*/)
+                                size_t /*objectsSize*/)
 {
     //ALOGI("Freeing parcel %p", &parcel);
     IF_LOG_COMMANDS() {
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 05f43e3..6d728dc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -18,6 +18,9 @@
 
 #include <binder/IServiceManager.h>
 
+#include <inttypes.h>
+#include <unistd.h>
+
 #include <android/os/BnServiceCallback.h>
 #include <android/os/IServiceManager.h>
 #include <binder/IPCThreadState.h>
@@ -36,8 +39,6 @@
 
 #include "Static.h"
 
-#include <unistd.h>
-
 namespace android {
 
 using AidlServiceManager = android::os::IServiceManager;
@@ -73,6 +74,7 @@
     Vector<String16> listServices(int dumpsysPriority) override;
     sp<IBinder> waitForService(const String16& name16) override;
     bool isDeclared(const String16& name) override;
+    Vector<String16> getDeclaredInstances(const String16& interface) override;
 
     // for legacy ABI
     const String16& getInterfaceDescriptor() const override {
@@ -219,7 +221,8 @@
 
     const bool isVendorService =
         strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
-    const long timeout = uptimeMillis() + 5000;
+    const long timeout = 5000;
+    int64_t startTime = uptimeMillis();
     // Vendor code can't access system properties
     if (!gSystemBootCompleted && !isVendorService) {
 #ifdef __ANDROID__
@@ -233,15 +236,21 @@
     // retry interval in millisecond; note that vendor services stay at 100ms
     const long sleepTime = gSystemBootCompleted ? 1000 : 100;
 
+    ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
+          ProcessState::self()->getDriverName().c_str());
+
     int n = 0;
-    while (uptimeMillis() < timeout) {
+    while (uptimeMillis() - startTime < timeout) {
         n++;
-        ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
-            ProcessState::self()->getDriverName().c_str());
         usleep(1000*sleepTime);
 
         sp<IBinder> svc = checkService(name);
-        if (svc != nullptr) return svc;
+        if (svc != nullptr) {
+            ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms",
+                  String8(name).string(), ProcessState::self()->getDriverName().c_str(),
+                  uptimeMillis() - startTime);
+            return svc;
+        }
     }
     ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
     return nullptr;
@@ -326,6 +335,11 @@
 
     while(true) {
         {
+            // It would be really nice if we could read binder commands on this
+            // thread instead of needing a threadpool to be started, but for
+            // instance, if we call getAndExecuteCommand, it might be the case
+            // that another thread serves the callback, and we never get a
+            // command, so we hang indefinitely.
             std::unique_lock<std::mutex> lock(waiter->mMutex);
             using std::literals::chrono_literals::operator""s;
             waiter->mCv.wait_for(lock, 1s, [&] {
@@ -334,6 +348,8 @@
             if (waiter->mBinder != nullptr) return waiter->mBinder;
         }
 
+        ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str());
+
         // Handle race condition for lazy services. Here is what can happen:
         // - the service dies (not processed by init yet).
         // - sm processes death notification.
@@ -347,8 +363,6 @@
             return nullptr;
         }
         if (out != nullptr) return out;
-
-        ALOGW("Waited one second for %s", name.c_str());
     }
 }
 
@@ -360,4 +374,18 @@
     return declared;
 }
 
+Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+    std::vector<std::string> out;
+    if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+        return {};
+    }
+
+    Vector<String16> res;
+    res.setCapacity(out.size());
+    for (const std::string& instance : out) {
+        res.push(String16(instance.c_str()));
+    }
+    return res;
+}
+
 } // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 325e204..2e15e50 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -29,16 +29,12 @@
 
 using AidlServiceManager = android::os::IServiceManager;
 
-class ClientCounterCallback : public ::android::os::BnClientCallback {
+class ClientCounterCallbackImpl : public ::android::os::BnClientCallback {
 public:
-    ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
+    ClientCounterCallbackImpl() : mNumConnectedServices(0), mForcePersist(false) {}
 
     bool registerService(const sp<IBinder>& service, const std::string& name,
                          bool allowIsolated, int dumpFlags);
-
-    /**
-     * Set a flag to prevent services from automatically shutting down
-     */
     void forcePersist(bool persist);
 
 protected:
@@ -75,7 +71,23 @@
     bool mForcePersist;
 };
 
-bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+class ClientCounterCallback {
+public:
+    ClientCounterCallback();
+
+    bool registerService(const sp<IBinder>& service, const std::string& name,
+                                            bool allowIsolated, int dumpFlags);
+
+    /**
+     * Set a flag to prevent services from automatically shutting down
+     */
+    void forcePersist(bool persist);
+
+private:
+    sp<ClientCounterCallbackImpl> mImpl;
+};
+
+bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
                                             bool allowIsolated, int dumpFlags) {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
@@ -89,7 +101,7 @@
     }
 
     if (!reRegister) {
-        if (!manager->registerClientCallback(name, service, this).isOk()) {
+        if(!manager->registerClientCallback(name, service, this).isOk()) {
             ALOGE("Failed to add client callback for service %s", name.c_str());
             return false;
         }
@@ -105,7 +117,7 @@
     return true;
 }
 
-std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+std::map<std::string, ClientCounterCallbackImpl::Service>::iterator ClientCounterCallbackImpl::assertRegisteredService(const sp<IBinder>& service) {
     LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
     for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
         auto const& [name, registered] = *it;
@@ -117,7 +129,7 @@
     __builtin_unreachable();
 }
 
-void ClientCounterCallback::forcePersist(bool persist) {
+void ClientCounterCallbackImpl::forcePersist(bool persist) {
     mForcePersist = persist;
     if(!mForcePersist) {
         // Attempt a shutdown in case the number of clients hit 0 while the flag was on
@@ -129,7 +141,7 @@
  * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
  * invocations could occur on different threads however.
  */
-Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
     auto & [name, registered] = *assertRegisteredService(service);
     if (registered.clients == clients) {
         LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -154,7 +166,7 @@
     return Status::ok();
 }
 
-void ClientCounterCallback::tryShutdown() {
+void ClientCounterCallbackImpl::tryShutdown() {
     if(mNumConnectedServices > 0) {
         // Should only shut down if there are no clients
         return;
@@ -175,7 +187,6 @@
 
         bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
 
-
         if (!success) {
             ALOGI("Failed to unregister service %s", entry.first.c_str());
             break;
@@ -200,6 +211,19 @@
     }
 }
 
+ClientCounterCallback::ClientCounterCallback() {
+      mImpl = sp<ClientCounterCallbackImpl>::make();
+}
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+                                            bool allowIsolated, int dumpFlags) {
+    return mImpl->registerService(service, name, allowIsolated, dumpFlags);
+}
+
+void ClientCounterCallback::forcePersist(bool persist) {
+    mImpl->forcePersist(persist);
+}
+
 }  // namespace internal
 
 LazyServiceRegistrar::LazyServiceRegistrar() {
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index ebf91f9..b46b3e8 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -387,7 +387,7 @@
     while (cur) {
         if (cur->start == start) {
             LOG_FATAL_IF(cur->free,
-                "block at offset 0x%08lX of size 0x%08lX already freed",
+                "block at offset 0x%08lX of size 0x%08X already freed",
                 cur->start*kMemoryAlign, cur->size*kMemoryAlign);
 
             // merge freed blocks together
@@ -411,7 +411,7 @@
                 }
             #endif
             LOG_FATAL_IF(!freed->free,
-                "freed block at offset 0x%08lX of size 0x%08lX is not free!",
+                "freed block at offset 0x%08lX of size 0x%08X is not free!",
                 freed->start * kMemoryAlign, freed->size * kMemoryAlign);
 
             return freed;
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index e4ea60f..e1cbc19 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -49,7 +49,7 @@
     int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size);
     ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
     if (fd >= 0) {
-        if (mapfd(fd, size) == NO_ERROR) {
+        if (mapfd(fd, true, size) == NO_ERROR) {
             if (flags & READ_ONLY) {
                 ashmem_set_prot_region(fd, PROT_READ);
             }
@@ -70,7 +70,7 @@
     if (fd >= 0) {
         const size_t pagesize = getpagesize();
         size = ((size + pagesize-1) & ~(pagesize-1));
-        if (mapfd(fd, size) == NO_ERROR) {
+        if (mapfd(fd, false, size) == NO_ERROR) {
             mDevice = device;
         }
     }
@@ -82,7 +82,7 @@
 {
     const size_t pagesize = getpagesize();
     size = ((size + pagesize-1) & ~(pagesize-1));
-    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
+    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset);
 }
 
 status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
@@ -98,7 +98,7 @@
     return NO_ERROR;
 }
 
-status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
+status_t MemoryHeapBase::mapfd(int fd, bool writeableByCaller, size_t size, off_t offset)
 {
     if (size == 0) {
         // try to figure out the size automatically
@@ -116,8 +116,12 @@
     }
 
     if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+        int prot = PROT_READ;
+        if (writeableByCaller || (mFlags & READ_ONLY) == 0) {
+            prot |= PROT_WRITE;
+        }
         void* base = (uint8_t*)mmap(nullptr, size,
-                PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+                prot, MAP_SHARED, fd, offset);
         if (base == MAP_FAILED) {
             ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
                     fd, size, strerror(errno));
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 166c1d8..c72b2ad 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/sched.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -40,7 +41,6 @@
 #include <binder/TextOutput.h>
 
 #include <cutils/ashmem.h>
-#include <utils/Debug.h>
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
@@ -49,6 +49,7 @@
 
 #include <private/binder/binder_module.h>
 #include "Static.h"
+#include "Utils.h"
 
 #define LOG_REFS(...)
 //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -77,9 +78,8 @@
 // many things compile this into prebuilts on the stack
 static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
 
-static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
-static size_t gParcelGlobalAllocSize = 0;
-static size_t gParcelGlobalAllocCount = 0;
+static std::atomic<size_t> gParcelGlobalAllocCount;
+static std::atomic<size_t> gParcelGlobalAllocSize;
 
 static size_t gMaxFds = 0;
 
@@ -188,16 +188,18 @@
     return OK;
 }
 
+static constexpr inline int schedPolicyMask(int policy, int priority) {
+    return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
+}
+
 status_t Parcel::flattenBinder(const sp<IBinder>& binder)
 {
     flat_binder_object obj;
+    obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
 
-    if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
-        /* minimum priority for all nodes is nice 0 */
-        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
-    } else {
-        /* minimum priority for all nodes is MAX_NICE(19) */
-        obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+    int schedBits = 0;
+    if (!IPCThreadState::self()->backgroundSchedulingDisabled()) {
+        schedBits = schedPolicyMask(SCHED_NORMAL, 19);
     }
 
     if (binder != nullptr) {
@@ -213,6 +215,13 @@
             obj.handle = handle;
             obj.cookie = 0;
         } else {
+            int policy = local->getMinSchedulerPolicy();
+            int priority = local->getMinSchedulerPriority();
+
+            if (policy != 0 || priority != 0) {
+                // override value, since it is set explicitly
+                schedBits = schedPolicyMask(policy, priority);
+            }
             if (local->isRequestingSid()) {
                 obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
             }
@@ -226,6 +235,8 @@
         obj.cookie = 0;
     }
 
+    obj.flags |= schedBits;
+
     return finishFlattenBinder(binder, obj);
 }
 
@@ -264,17 +275,11 @@
 }
 
 size_t Parcel::getGlobalAllocSize() {
-    pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
-    size_t size = gParcelGlobalAllocSize;
-    pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
-    return size;
+    return gParcelGlobalAllocSize.load();
 }
 
 size_t Parcel::getGlobalAllocCount() {
-    pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
-    size_t count = gParcelGlobalAllocCount;
-    pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
-    return count;
+    return gParcelGlobalAllocCount.load();
 }
 
 const uint8_t* Parcel::data() const
@@ -498,6 +503,11 @@
     return mHasFds;
 }
 
+void Parcel::markSensitive() const
+{
+    mDeallocZero = true;
+}
+
 void Parcel::updateWorkSourceRequestHeaderPosition() const {
     // Only update the request headers once. We only want to point
     // to the first headers read/written.
@@ -516,14 +526,19 @@
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
+    return writeInterfaceToken(interface.string(), interface.size());
+}
+
+status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
     const IPCThreadState* threadState = IPCThreadState::self();
     writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
     updateWorkSourceRequestHeaderPosition();
     writeInt32(threadState->shouldPropagateWorkSource() ?
             threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
     writeInt32(kHeader);
+
     // currently the interface identification token is just its name as a string
-    return writeString16(interface);
+    return writeString16(str, len);
 }
 
 bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
@@ -1059,6 +1074,7 @@
 {
     if (str == nullptr) return writeInt32(-1);
 
+    // NOTE: Keep this logic in sync with android_os_Parcel.cpp
     status_t err = writeInt32(len);
     if (err == NO_ERROR) {
         uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
@@ -1099,6 +1115,7 @@
 {
     if (str == nullptr) return writeInt32(-1);
 
+    // NOTE: Keep this logic in sync with android_os_Parcel.cpp
     status_t err = writeInt32(len);
     if (err == NO_ERROR) {
         len *= sizeof(char16_t);
@@ -1514,7 +1531,7 @@
 
 template<class T>
 status_t Parcel::readAligned(T *pArg) const {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+    static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
 
     if ((mDataPos+sizeof(T)) <= mDataSize) {
         if (mObjectsSize > 0) {
@@ -1547,7 +1564,7 @@
 
 template<class T>
 status_t Parcel::writeAligned(T val) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+    static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
 
     if ((mDataPos+sizeof(val)) <= mDataCapacity) {
 restart_write:
@@ -1897,17 +1914,6 @@
 
 #endif
 
-status_t Parcel::readIntPtr(intptr_t *pArg) const
-{
-    return readAligned(pArg);
-}
-
-
-intptr_t Parcel::readIntPtr() const
-{
-    return readAligned<intptr_t>();
-}
-
 status_t Parcel::readBool(bool *pArg) const
 {
     int32_t tmp = 0;
@@ -2492,7 +2498,7 @@
 }
 
 void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
-    const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
+    const binder_size_t* objects, size_t objectsCount, release_func relFunc)
 {
     binder_size_t minOffset = 0;
     freeDataNoInit();
@@ -2507,7 +2513,6 @@
     mNextObjectHint = 0;
     mObjectsSorted = false;
     mOwner = relFunc;
-    mOwnerCookie = relCookie;
     for (size_t i = 0; i < mObjectsSize; i++) {
         binder_size_t offset = mObjects[i];
         if (offset < minOffset) {
@@ -2608,22 +2613,17 @@
     if (mOwner) {
         LOG_ALLOC("Parcel %p: freeing other owner data", this);
         //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
-        mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+        mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
     } else {
         LOG_ALLOC("Parcel %p: freeing allocated data", this);
         releaseObjects();
         if (mData) {
             LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
-            pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
-            if (mDataCapacity <= gParcelGlobalAllocSize) {
-              gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
-            } else {
-              gParcelGlobalAllocSize = 0;
+            gParcelGlobalAllocSize -= mDataCapacity;
+            gParcelGlobalAllocCount--;
+            if (mDeallocZero) {
+                zeroMemory(mData, mDataSize);
             }
-            if (gParcelGlobalAllocCount > 0) {
-              gParcelGlobalAllocCount--;
-            }
-            pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
             free(mData);
         }
         if (mObjects) free(mObjects);
@@ -2646,6 +2646,21 @@
             : continueWrite(std::max(newSize, (size_t) 128));
 }
 
+static uint8_t* reallocZeroFree(uint8_t* data, size_t oldCapacity, size_t newCapacity, bool zero) {
+    if (!zero) {
+        return (uint8_t*)realloc(data, newCapacity);
+    }
+    uint8_t* newData = (uint8_t*)malloc(newCapacity);
+    if (!newData) {
+        return nullptr;
+    }
+
+    memcpy(newData, data, std::min(oldCapacity, newCapacity));
+    zeroMemory(data, oldCapacity);
+    free(data);
+    return newData;
+}
+
 status_t Parcel::restartWrite(size_t desired)
 {
     if (desired > INT32_MAX) {
@@ -2659,7 +2674,7 @@
         return continueWrite(desired);
     }
 
-    uint8_t* data = (uint8_t*)realloc(mData, desired);
+    uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
     if (!data && desired > mDataCapacity) {
         mError = NO_MEMORY;
         return NO_MEMORY;
@@ -2667,15 +2682,17 @@
 
     releaseObjects();
 
-    if (data) {
+    if (data || desired == 0) {
         LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
-        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
-        gParcelGlobalAllocSize += desired;
-        gParcelGlobalAllocSize -= mDataCapacity;
+        if (mDataCapacity > desired) {
+            gParcelGlobalAllocSize -= (mDataCapacity - desired);
+        } else {
+            gParcelGlobalAllocSize += (desired - mDataCapacity);
+        }
+
         if (!mData) {
             gParcelGlobalAllocCount++;
         }
-        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
         mData = data;
         mDataCapacity = desired;
     }
@@ -2759,14 +2776,12 @@
             memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
         }
         //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
-        mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+        mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
         mOwner = nullptr;
 
         LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
-        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
         gParcelGlobalAllocSize += desired;
         gParcelGlobalAllocCount++;
-        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
 
         mData = data;
         mObjects = objects;
@@ -2810,14 +2825,12 @@
 
         // We own the data, so we can just do a realloc().
         if (desired > mDataCapacity) {
-            uint8_t* data = (uint8_t*)realloc(mData, desired);
+            uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
             if (data) {
                 LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
                         desired);
-                pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
                 gParcelGlobalAllocSize += desired;
                 gParcelGlobalAllocSize -= mDataCapacity;
-                pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
                 mData = data;
                 mDataCapacity = desired;
             } else {
@@ -2849,10 +2862,8 @@
         }
 
         LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
-        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
         gParcelGlobalAllocSize += desired;
         gParcelGlobalAllocCount++;
-        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
 
         mData = data;
         mDataSize = mDataPos = 0;
@@ -2882,6 +2893,7 @@
     mHasFds = false;
     mFdsKnown = true;
     mAllowFds = true;
+    mDeallocZero = false;
     mOwner = nullptr;
     mOpenAshmemSize = 0;
     mWorkSourceRequestHeaderPosition = 0;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
new file mode 100644
index 0000000..b2b8671
--- /dev/null
+++ b/libs/binder/ParcelableHolder.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ParcelableHolder.h>
+
+#define RETURN_ON_FAILURE(expr)                     \
+    do {                                            \
+        android::status_t _status = (expr);         \
+        if (_status != android::OK) return _status; \
+    } while (false)
+
+namespace android {
+namespace os {
+status_t ParcelableHolder::writeToParcel(Parcel* p) const {
+    RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
+    if (this->mParcelPtr) {
+        RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
+        RETURN_ON_FAILURE(p->appendFrom(this->mParcelPtr.get(), 0, this->mParcelPtr->dataSize()));
+        return OK;
+    }
+    if (this->mParcelable) {
+        size_t sizePos = p->dataPosition();
+        RETURN_ON_FAILURE(p->writeInt32(0));
+        size_t dataStartPos = p->dataPosition();
+        RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+        this->mParcelable->writeToParcel(p);
+        size_t dataSize = p->dataPosition() - dataStartPos;
+
+        p->setDataPosition(sizePos);
+        RETURN_ON_FAILURE(p->writeInt32(dataSize));
+        p->setDataPosition(p->dataPosition() + dataSize);
+        return OK;
+    }
+
+    RETURN_ON_FAILURE(p->writeInt32(0));
+    return OK;
+}
+
+status_t ParcelableHolder::readFromParcel(const Parcel* p) {
+    this->mStability = static_cast<Stability>(p->readInt32());
+    this->mParcelable = nullptr;
+    this->mParcelableName = std::nullopt;
+    int32_t rawDataSize;
+
+    status_t status = p->readInt32(&rawDataSize);
+    if (status != android::OK || rawDataSize < 0) {
+        this->mParcelPtr = nullptr;
+        return status != android::OK ? status : BAD_VALUE;
+    }
+    if (rawDataSize == 0) {
+        if (this->mParcelPtr) {
+            this->mParcelPtr = nullptr;
+        }
+        return OK;
+    }
+
+    size_t dataSize = rawDataSize;
+
+    size_t dataStartPos = p->dataPosition();
+
+    if (dataStartPos > SIZE_MAX - dataSize) {
+        this->mParcelPtr = nullptr;
+        return BAD_VALUE;
+    }
+
+    if (!this->mParcelPtr) {
+        this->mParcelPtr = std::make_unique<Parcel>();
+    }
+    this->mParcelPtr->freeData();
+
+    status = this->mParcelPtr->appendFrom(p, dataStartPos, dataSize);
+    if (status != android::OK) {
+        this->mParcelPtr = nullptr;
+        return status;
+    }
+    p->setDataPosition(dataStartPos + dataSize);
+    return OK;
+}
+} // namespace os
+} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f3861bb..83ca687 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -32,6 +32,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <mutex>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -73,38 +74,49 @@
 
 sp<ProcessState> ProcessState::self()
 {
-    Mutex::Autolock _l(gProcessMutex);
-    if (gProcess != nullptr) {
-        return gProcess;
-    }
-    gProcess = new ProcessState(kDefaultDriver);
-    return gProcess;
+    return init(kDefaultDriver, false /*requireDefault*/);
 }
 
 sp<ProcessState> ProcessState::initWithDriver(const char* driver)
 {
-    Mutex::Autolock _l(gProcessMutex);
-    if (gProcess != nullptr) {
-        // Allow for initWithDriver to be called repeatedly with the same
-        // driver.
-        if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
-            return gProcess;
-        }
-        LOG_ALWAYS_FATAL("ProcessState was already initialized.");
-    }
-
-    if (access(driver, R_OK) == -1) {
-        ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
-        driver = "/dev/binder";
-    }
-
-    gProcess = new ProcessState(driver);
-    return gProcess;
+    return init(driver, true /*requireDefault*/);
 }
 
 sp<ProcessState> ProcessState::selfOrNull()
 {
-    Mutex::Autolock _l(gProcessMutex);
+    return init(nullptr, false /*requireDefault*/);
+}
+
+sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
+{
+    [[clang::no_destroy]] static sp<ProcessState> gProcess;
+    [[clang::no_destroy]] static std::mutex gProcessMutex;
+
+    if (driver == nullptr) {
+        std::lock_guard<std::mutex> l(gProcessMutex);
+        return gProcess;
+    }
+
+    [[clang::no_destroy]] static std::once_flag gProcessOnce;
+    std::call_once(gProcessOnce, [&](){
+        if (access(driver, R_OK) == -1) {
+            ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
+            driver = "/dev/binder";
+        }
+
+        std::lock_guard<std::mutex> l(gProcessMutex);
+        gProcess = new ProcessState(driver);
+    });
+
+    if (requireDefault) {
+        // Detect if we are trying to initialize with a different driver, and
+        // consider that an error. ProcessState will only be initialized once above.
+        LOG_ALWAYS_FATAL_IF(gProcess->getDriverName() != driver,
+                            "ProcessState was already initialized with %s,"
+                            " can't initialize with %s.",
+                            gProcess->getDriverName().c_str(), driver);
+    }
+
     return gProcess;
 }
 
@@ -132,11 +144,9 @@
     }
 }
 
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+bool ProcessState::becomeContextManager()
 {
     AutoMutex _l(mLock);
-    mBinderContextCheckFunc = checkFunc;
-    mBinderContextUserData = userData;
 
     flat_binder_object obj {
         .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -148,13 +158,11 @@
     if (result != 0) {
         android_errorWriteLog(0x534e4554, "121035042");
 
-        int dummy = 0;
-        result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        int unused = 0;
+        result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
     }
 
     if (result == -1) {
-        mBinderContextCheckFunc = nullptr;
-        mBinderContextUserData = nullptr;
         ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
     }
 
@@ -274,9 +282,17 @@
                 // a driver API to get a handle to the context manager with
                 // proper reference counting.
 
+                IPCThreadState* ipc = IPCThreadState::self();
+
+                CallRestriction originalCallRestriction = ipc->getCallRestriction();
+                ipc->setCallRestriction(CallRestriction::NONE);
+
                 Parcel data;
-                status_t status = IPCThreadState::self()->transact(
+                status_t status = ipc->transact(
                         0, IBinder::PING_TRANSACTION, data, nullptr, 0);
+
+                ipc->setCallRestriction(originalCallRestriction);
+
                 if (status == DEAD_OBJECT)
                    return nullptr;
             }
@@ -385,14 +401,12 @@
     , mExecutingThreadsCount(0)
     , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
     , mStarvationStartTimeMs(0)
-    , mBinderContextCheckFunc(nullptr)
-    , mBinderContextUserData(nullptr)
     , mThreadPoolStarted(false)
     , mThreadPoolSeq(1)
     , mCallRestriction(CallRestriction::NONE)
 {
 
-// TODO(b/139016109): enforce in build system
+// TODO(b/166468760): enforce in build system
 #if defined(__ANDROID_APEX__)
     LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
 #endif
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index e1565fa..6115aec 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -22,7 +22,7 @@
 namespace internal {
 
 void Stability::markCompilationUnit(IBinder* binder) {
-    status_t result = set(binder, kLocalStability, true /*log*/);
+    status_t result = set(binder, getLocalStability(), true /*log*/);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
@@ -45,7 +45,26 @@
 }
 
 void Stability::tryMarkCompilationUnit(IBinder* binder) {
-    (void) set(binder, kLocalStability, false /*log*/);
+    (void) set(binder, getLocalStability(), false /*log*/);
+}
+
+Stability::Level Stability::getLocalStability() {
+#ifdef __ANDROID_VNDK__
+    #ifdef __ANDROID_APEX__
+        // TODO(b/142684679) avoid use_vendor on system APEXes
+        #if !defined(__ANDROID_APEX_COM_ANDROID_MEDIA_SWCODEC__) \
+            && !defined(__ANDROID_APEX_TEST_COM_ANDROID_MEDIA_SWCODEC__)
+        #error VNDK + APEX only defined for com.android.media.swcodec
+        #endif
+        // TODO(b/142684679) avoid use_vendor on system APEXes
+        return Level::SYSTEM;
+    #else
+        return Level::VENDOR;
+    #endif
+#else
+    // TODO(b/139325195): split up stability levels for system/APEX.
+    return Level::SYSTEM;
+#endif
 }
 
 status_t Stability::set(IBinder* binder, int32_t stability, bool log) {
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index 779ed41..db0f1c7 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -68,9 +68,4 @@
 TextOutput& aout(*new FdTextOutput(STDOUT_FILENO));
 TextOutput& aerr(*new FdTextOutput(STDERR_FILENO));
 
-// ------------ ProcessState.cpp
-
-Mutex& gProcessMutex = *new Mutex;
-sp<ProcessState> gProcess;
-
 }   // namespace android
diff --git a/libs/binder/Static.h b/libs/binder/Static.h
index f8e0ee5..83524e8 100644
--- a/libs/binder/Static.h
+++ b/libs/binder/Static.h
@@ -27,8 +27,4 @@
 // For TextStream.cpp
 extern Vector<int32_t> gTextBuffers;
 
-// For ProcessState.cpp
-extern Mutex& gProcessMutex;
-extern sp<ProcessState> gProcess;
-
 }   // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c232283..61a611d 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,6 +16,13 @@
       "name": "binderTextOutputTest"
     },
     {
+      "name": "binderParcelTest"
+    },
+    {
+      "name": "binderParcelTest",
+      "host": true
+    },
+    {
       "name": "binderLibTest"
     },
     {
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/Utils.cpp
similarity index 72%
copy from libs/binder/fuzzer/binder.h
copy to libs/binder/Utils.cpp
index b224ef4..90a4502 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/Utils.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-#include <binder/Parcel.h>
-#include <vector>
+#include "Utils.h"
 
-#include "parcel_fuzzer.h"
+#include <string.h>
 
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+
+void zeroMemory(uint8_t* data, size_t size) {
+    memset(data, 0, size);
+}
+
+}   // namespace android
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/Utils.h
similarity index 72%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/binder/Utils.h
index 10cf17c..f94b158 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/Utils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,5 +14,12 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+#include <cstdint>
+#include <stddef.h>
+
+namespace android {
+
+// avoid optimizations
+void zeroMemory(uint8_t* data, size_t size);
+
+}   // namespace android
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ff15460..2b1e492 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -99,6 +99,14 @@
     boolean isDeclared(@utf8InCpp String name);
 
     /**
+     * Returns all declared instances for a particular interface.
+     *
+     * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+     * passed here, then ["foo"] would be returned.
+     */
+    @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
+
+    /**
      * Request a callback when the number of clients of the service changes.
      * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
      */
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
deleted file mode 100644
index 6657edb..0000000
--- a/libs/binder/fuzzer/main.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define FUZZ_LOG_TAG "main"
-
-#include "binder.h"
-#include "binder_ndk.h"
-#include "hwbinder.h"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <cstdlib>
-#include <ctime>
-
-template <typename P>
-void doFuzz(
-        const std::vector<ParcelRead<P>>& reads,
-        const std::vector<uint8_t>& input,
-        const std::vector<uint8_t>& instructions) {
-
-    P p;
-    p.setData(input.data(), input.size());
-
-    // since we are only using a byte to index
-    CHECK(reads.size() <= 255) << reads.size();
-
-    for (size_t i = 0; i < instructions.size() - 1; i += 2) {
-        uint8_t a = instructions[i];
-        uint8_t readIdx = a % reads.size();
-
-        uint8_t b = instructions[i + 1];
-
-        FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
-                   << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
-                   << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
-                   << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
-                   << " cap: " << p.dataCapacity();
-
-        reads[readIdx](p, b);
-    }
-}
-
-void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
-    uint8_t parcelType = options & 0x3;
-
-    switch (parcelType) {
-        case 0x0:
-            doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
-                                                instructions);
-            break;
-        case 0x1:
-            doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
-            break;
-        case 0x2:
-            doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
-            break;
-        case 0x3:
-            /*reserved for future use*/
-            break;
-        default:
-            LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
-    }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    if (size <= 1) return 0;  // no use
-
-    // avoid timeouts, see b/142617274, b/142473153
-    if (size > 50000) return 0;
-
-    uint8_t options = *data;
-    data++;
-    size--;
-
-    // TODO: generate 'objects' data
-
-    // data to fill out parcel
-    size_t inputLen = size / 2;
-    std::vector<uint8_t> input(data, data + inputLen);
-    data += inputLen;
-    size -= inputLen;
-
-    // data to use to determine what to do
-    size_t instructionLen = size;
-    std::vector<uint8_t> instructions(data, data + instructionLen);
-    data += instructionLen;
-    size -= instructionLen;
-
-    CHECK(size == 0) << "size: " << size;
-
-    FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
-    FUZZ_LOG() << "input: " << hexString(input);
-    FUZZ_LOG() << "instructions: " << hexString(instructions);
-
-    fuzz(options, input, instructions);
-    return 0;
-}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 7043b17..0240858 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -14,18 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_ACTIVITY_MANAGER_H
-#define ANDROID_ACTIVITY_MANAGER_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
 #include <binder/IActivityManager.h>
+#include <android/app/ProcessStateEnum.h>
 
 #include <utils/threads.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
 
+#define DECLARE_PROCESS_STATE(name) \
+    PROCESS_STATE_##name = (int32_t) app::ProcessStateEnum::name
+
 class ActivityManager
 {
 public:
@@ -41,30 +44,31 @@
         UID_OBSERVER_ACTIVE = 1<<3
     };
 
+    // PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
+    // This is to make sure that Java side uses the same values as native.
     enum {
-        PROCESS_STATE_UNKNOWN = -1,
-        PROCESS_STATE_PERSISTENT = 0,
-        PROCESS_STATE_PERSISTENT_UI = 1,
-        PROCESS_STATE_TOP = 2,
-        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
-        PROCESS_STATE_BOUND_TOP = 4,
-        PROCESS_STATE_FOREGROUND_SERVICE = 5,
-        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
-        PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
-        PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
-        PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
-        PROCESS_STATE_BACKUP = 10,
-        PROCESS_STATE_SERVICE = 11,
-        PROCESS_STATE_RECEIVER = 12,
-        PROCESS_STATE_TOP_SLEEPING = 13,
-        PROCESS_STATE_HEAVY_WEIGHT = 14,
-        PROCESS_STATE_HOME = 15,
-        PROCESS_STATE_LAST_ACTIVITY = 16,
-        PROCESS_STATE_CACHED_ACTIVITY = 17,
-        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
-        PROCESS_STATE_CACHED_RECENT = 19,
-        PROCESS_STATE_CACHED_EMPTY = 20,
-        PROCESS_STATE_NONEXISTENT = 21,
+        DECLARE_PROCESS_STATE(UNKNOWN),
+        DECLARE_PROCESS_STATE(PERSISTENT),
+        DECLARE_PROCESS_STATE(PERSISTENT_UI),
+        DECLARE_PROCESS_STATE(TOP),
+        DECLARE_PROCESS_STATE(BOUND_TOP),
+        DECLARE_PROCESS_STATE(FOREGROUND_SERVICE),
+        DECLARE_PROCESS_STATE(BOUND_FOREGROUND_SERVICE),
+        DECLARE_PROCESS_STATE(IMPORTANT_FOREGROUND),
+        DECLARE_PROCESS_STATE(IMPORTANT_BACKGROUND),
+        DECLARE_PROCESS_STATE(TRANSIENT_BACKGROUND),
+        DECLARE_PROCESS_STATE(BACKUP),
+        DECLARE_PROCESS_STATE(SERVICE),
+        DECLARE_PROCESS_STATE(RECEIVER),
+        DECLARE_PROCESS_STATE(TOP_SLEEPING),
+        DECLARE_PROCESS_STATE(HEAVY_WEIGHT),
+        DECLARE_PROCESS_STATE(HOME),
+        DECLARE_PROCESS_STATE(LAST_ACTIVITY),
+        DECLARE_PROCESS_STATE(CACHED_ACTIVITY),
+        DECLARE_PROCESS_STATE(CACHED_ACTIVITY_CLIENT),
+        DECLARE_PROCESS_STATE(CACHED_RECENT),
+        DECLARE_PROCESS_STATE(CACHED_EMPTY),
+        DECLARE_PROCESS_STATE(NONEXISTENT),
     };
 
     ActivityManager();
@@ -77,9 +81,9 @@
     void unregisterUidObserver(const sp<IUidObserver>& observer);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
     int getUidProcessState(const uid_t uid, const String16& callingPackage);
-    bool setSchedPolicyCgroup(const int32_t tid, const int32_t group);
+    status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
 
-  status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+    status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
     status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
 
 private:
@@ -94,5 +98,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_ACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6d04f13..35c697e 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_APP_OPS_MANAGER_H
-#define ANDROID_APP_OPS_MANAGER_H
+#pragma once
 
 #include <binder/IAppOpsService.h>
 
@@ -133,7 +132,11 @@
         OP_DEPRECATED_1 = 96,
         OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
         OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
-        _NUM_OP = 99
+        OP_NO_ISOLATED_STORAGE = 99,
+        OP_PHONE_CALL_MICROPHONE = 100,
+        OP_PHONE_CALL_CAMERA = 101,
+        OP_RECORD_AUDIO_HOTWORD = 102,
+        _NUM_OP = 103
     };
 
     AppOpsManager();
@@ -175,5 +178,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 74e52db..d6da397 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BINDER_H
-#define ANDROID_BINDER_H
+#pragma once
 
 #include <atomic>
 #include <stdint.h>
@@ -72,6 +71,22 @@
     // This must be called before the object is sent to another process. Not thread safe.
     void                setExtension(const sp<IBinder>& extension);
 
+    // This must be called before the object is sent to another process. Not thread safe.
+    //
+    // This function will abort if improper parameters are set. This is like
+    // sched_setscheduler. However, it sets the minimum scheduling policy
+    // only for the duration that this specific binder object is handling the
+    // call in a threadpool. By default, this API is set to SCHED_NORMAL/0. In
+    // this case, the scheduling priority will not actually be modified from
+    // binder defaults. See also IPCThreadState::disableBackgroundScheduling.
+    //
+    // Appropriate values are:
+    // SCHED_NORMAL: -20 <= priority <= 19
+    // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99
+    void                setMinSchedulerPolicy(int policy, int priority);
+    int                 getMinSchedulerPolicy();
+    int                 getMinSchedulerPriority();
+
     pid_t               getDebugPid();
 
 protected:
@@ -126,5 +141,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_BINDER_H
diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
index c17ae6f..5776f3c 100644
--- a/libs/binder/include/binder/BinderService.h
+++ b/libs/binder/include/binder/BinderService.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BINDER_SERVICE_H
-#define ANDROID_BINDER_SERVICE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -64,4 +63,3 @@
 
 } // namespace android
 // ---------------------------------------------------------------------------
-#endif // ANDROID_BINDER_SERVICE_H
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 378a911..64d0657 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BPBINDER_H
-#define ANDROID_BPBINDER_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
@@ -153,5 +152,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_BPBINDER_H
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
index 324e5c1..ac71e00 100644
--- a/libs/binder/include/binder/Debug.h
+++ b/libs/binder/include/binder/Debug.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BINDER_DEBUG_H
-#define ANDROID_BINDER_DEBUG_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -45,5 +44,3 @@
 
 // ---------------------------------------------------------------------------
 } // namespace android
-
-#endif // ANDROID_BINDER_DEBUG_H
diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h
index aec6f70..c6803bd 100644
--- a/libs/binder/include/binder/Enums.h
+++ b/libs/binder/include/binder/Enums.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
 #include <iterator>
@@ -38,4 +39,4 @@
     constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index fe58a41..e2081ff 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IACTIVITY_MANAGER_H
-#define ANDROID_IACTIVITY_MANAGER_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -39,7 +38,10 @@
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
     virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
-    virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) = 0;
+    virtual status_t checkPermission(const String16& permission,
+                                    const pid_t pid,
+                                    const uid_t uid,
+                                    int32_t* outResult) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -47,7 +49,7 @@
         UNREGISTER_UID_OBSERVER_TRANSACTION,
         IS_UID_ACTIVE_TRANSACTION,
         GET_UID_PROCESS_STATE_TRANSACTION,
-        SET_SCHED_POLICY_CGROUP_TRANSACTION
+        CHECK_PERMISSION_TRANSACTION,
     };
 };
 
@@ -58,5 +60,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
index 7664260..eb76f57 100644
--- a/libs/binder/include/binder/IAppOpsCallback.h
+++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IAPP_OPS_CALLBACK_H
-#define ANDROID_IAPP_OPS_CALLBACK_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -57,6 +55,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IAPP_OPS_CALLBACK_H
-
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index a4a20c8..22f056b 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IAPP_OPS_SERVICE_H
-#define ANDROID_IAPP_OPS_SERVICE_H
+#pragma once
 
 #include <binder/IAppOpsCallback.h>
 #include <binder/IInterface.h>
@@ -39,10 +37,11 @@
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
             const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
-            const String16& message) = 0;
+            const String16& message, bool shouldCollectMessage) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
             const String16& packageName, const std::optional<String16>& attributionTag,
-            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
+            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+            bool shouldCollectMessage) = 0;
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
             const String16& packageName, const std::optional<String16>& attributionTag) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
@@ -89,5 +88,3 @@
 // ----------------------------------------------------------------------
 
 } // namespace android
-
-#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
index b786f89..6defc7f 100644
--- a/libs/binder/include/binder/IBatteryStats.h
+++ b/libs/binder/include/binder/IBatteryStats.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IBATTERYSTATS_H
-#define ANDROID_IBATTERYSTATS_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -82,5 +81,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IBATTERYSTATS_H
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index eea0e89..c8fb448 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IBINDER_H
-#define ANDROID_IBINDER_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -64,6 +63,10 @@
         // Corresponds to TF_ONE_WAY -- an asynchronous call.
         FLAG_ONEWAY             = 0x00000001,
 
+        // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
+        // is made
+        FLAG_CLEAR_BUF          = 0x00000020,
+
         // Private userspace flag for transaction which is being requested from
         // a vendor context.
         FLAG_PRIVATE_VENDOR     = 0x10000000,
@@ -253,5 +256,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_IBINDER_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7116154..988508e 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IINTERFACE_H
-#define ANDROID_IINTERFACE_H
+#pragma once
 
 #include <binder/Binder.h>
 
@@ -234,6 +232,7 @@
   "android.gui.IGraphicBufferConsumer",
   "android.gui.IRegionSamplingListener",
   "android.gui.ITransactionComposerListener",
+  "android.gui.IScreenCaptureListener",
   "android.gui.SensorEventConnection",
   "android.gui.SensorServer",
   "android.hardware.ICamera",
@@ -247,8 +246,6 @@
   "android.hardware.ISoundTriggerHwService",
   "android.hardware.IStreamListener",
   "android.hardware.IStreamSource",
-  "android.input.IInputFlinger",
-  "android.input.ISetInputWindowsListener",
   "android.media.IAudioFlinger",
   "android.media.IAudioFlingerClient",
   "android.media.IAudioPolicyService",
@@ -257,8 +254,6 @@
   "android.media.IAudioTrack",
   "android.media.IDataSource",
   "android.media.IDrmClient",
-  "android.media.IEffect",
-  "android.media.IEffectClient",
   "android.media.IMediaCodecList",
   "android.media.IMediaDrmService",
   "android.media.IMediaExtractor",
@@ -282,7 +277,6 @@
   "android.os.IComplexTypeInterface",
   "android.os.IPermissionController",
   "android.os.IPingResponder",
-  "android.os.IPowerManager",
   "android.os.IProcessInfoService",
   "android.os.ISchedulingPolicyService",
   "android.os.IStringConstants",
@@ -335,5 +329,3 @@
 
 } // namespace internal
 } // namespace android
-
-#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
index da2b7cf..ac4b4ca 100644
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -57,5 +56,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 1a36eb0..d8b7ec1 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IMEMORY_H
-#define ANDROID_IMEMORY_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -124,5 +123,3 @@
 // ----------------------------------------------------------------------------
 
 } // namespace android
-
-#endif // ANDROID_IMEMORY_H
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index b4e4a42..418ac35 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IPC_THREAD_STATE_H
-#define ANDROID_IPC_THREAD_STATE_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <binder/Parcel.h>
@@ -32,9 +31,28 @@
 class IPCThreadState
 {
 public:
+    using CallRestriction = ProcessState::CallRestriction;
+
     static  IPCThreadState*     self();
     static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
-    
+
+    // Freeze or unfreeze the binder interface to a specific process. When freezing, this method
+    // will block up to timeout_ms to process pending transactions directed to pid. Unfreeze
+    // is immediate. Transactions to processes frozen via this method won't be delivered and the
+    // driver will return BR_FROZEN_REPLY to the client sending them. After unfreeze,
+    // transactions will be delivered normally.
+    //
+    // pid: id for the process for which the binder interface is to be frozen
+    // enable: freeze (true) or unfreeze (false)
+    // timeout_ms: maximum time this function is allowed to block the caller waiting for pending
+    // binder transactions to be processed.
+    //
+    // returns: 0 in case of success, a value < 0 in case of error
+    static  status_t            freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
+
+    // Provide information about the state of a frozen process
+    static  status_t            getProcessFreezeInfo(pid_t pid, bool *sync_received,
+                                                    bool *async_received);
             sp<ProcessState>    process();
             
             status_t            clearLastError();
@@ -50,7 +68,7 @@
              * Returns the SELinux security identifier of the process which has
              * made the current binder call. If not in a binder call this will
              * return nullptr. If this isn't requested with
-             * IBinder::setRequestingSid, it will also return nullptr.
+             * Binder::setRequestingSid, it will also return nullptr.
              *
              * This can't be restored once it's cleared, and it does not return the
              * context of the current process when not in a binder call.
@@ -82,11 +100,14 @@
             void                setLastTransactionBinderFlags(int32_t flags);
             int32_t             getLastTransactionBinderFlags() const;
 
+            void                setCallRestriction(CallRestriction restriction);
+            CallRestriction     getCallRestriction() const;
+
             int64_t             clearCallingIdentity();
             // Restores PID/UID (not SID)
             void                restoreCallingIdentity(int64_t token);
-            
-            int                 setupPolling(int* fd);
+
+            status_t            setupPolling(int* fd);
             status_t            handlePolledCommands();
             void                flushCommands();
 
@@ -140,7 +161,6 @@
             // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
             // side.
             static const int32_t kUnsetWorkSource = -1;
-
 private:
                                 IPCThreadState();
                                 ~IPCThreadState();
@@ -165,9 +185,8 @@
     static  void                threadDestructor(void *st);
     static  void                freeBuffer(Parcel* parcel,
                                            const uint8_t* data, size_t dataSize,
-                                           const binder_size_t* objects, size_t objectsSize,
-                                           void* cookie);
-    
+                                           const binder_size_t* objects, size_t objectsSize);
+
     const   sp<ProcessState>    mProcess;
             Vector<BBinder*>    mPendingStrongDerefs;
             Vector<RefBase::weakref_type*> mPendingWeakDerefs;
@@ -187,12 +206,9 @@
             bool                mPropagateWorkSource;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
-
-            ProcessState::CallRestriction mCallRestriction;
+            CallRestriction     mCallRestriction;
 };
 
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
index 4b66df8..a4f93d9 100644
--- a/libs/binder/include/binder/IPermissionController.h
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IPERMISSION_CONTROLLER_H
-#define ANDROID_IPERMISSION_CONTROLLER_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -70,6 +68,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IPERMISSION_CONTROLLER_H
-
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
index ca30ad3..622f231 100644
--- a/libs/binder/include/binder/IProcessInfoService.h
+++ b/libs/binder/include/binder/IProcessInfoService.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_I_PROCESS_INFO_SERVICE_H
-#define ANDROID_I_PROCESS_INFO_SERVICE_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -51,5 +50,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
index 70e99e7..5434445 100644
--- a/libs/binder/include/binder/IResultReceiver.h
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IRESULT_RECEIVER_H
-#define ANDROID_IRESULT_RECEIVER_H
+#pragma once
 
 #include <binder/IInterface.h>
 
@@ -51,6 +49,3 @@
 // ----------------------------------------------------------------------
 
 } // namespace android
-
-#endif // ANDROID_IRESULT_RECEIVER_H
-
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 1d520c1..5f0d056 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_ISERVICE_MANAGER_H
-#define ANDROID_ISERVICE_MANAGER_H
+#pragma once
 
 #include <binder/IInterface.h>
 #include <utils/Vector.h>
@@ -96,6 +94,11 @@
      * service.
      */
     virtual bool isDeclared(const String16& name) = 0;
+
+    /**
+     * Get all instances of a service as declared in the VINTF manifest
+     */
+    virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
 };
 
 sp<IServiceManager> defaultServiceManager();
@@ -159,6 +162,3 @@
 bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
 
 } // namespace android
-
-#endif // ANDROID_ISERVICE_MANAGER_H
-
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
index b7ab6ea..17e34db 100644
--- a/libs/binder/include/binder/IShellCallback.h
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_ISHELL_CALLBACK_H
-#define ANDROID_ISHELL_CALLBACK_H
+#pragma once
 
 #include <binder/IInterface.h>
 
@@ -52,6 +50,3 @@
 // ----------------------------------------------------------------------
 
 } // namespace android
-
-#endif // ANDROID_ISHELL_CALLBACK_H
-
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index d070390..9291c0b 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-//
-#ifndef ANDROID_IUID_OBSERVER_H
-#define ANDROID_IUID_OBSERVER_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -64,5 +62,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IUID_OBSERVER_H
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
index c7e7a50..c1cd3c2 100644
--- a/libs/binder/include/binder/IpPrefix.h
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_IP_PREFIX_H
-#define ANDROID_IP_PREFIX_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -90,5 +89,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif  // ANDROID_IP_PREFIX_H
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 6d711bc..d18c88e 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -26,7 +26,19 @@
 class ClientCounterCallback;
 }  // namespace internal
 
-/** Exits when all services registered through this object have 0 clients */
+/**
+ * Exits when all services registered through this object have 0 clients
+ *
+ * In order to use this class, it's expected that your service:
+ * - registers all services in the process with this API
+ * - configures services as oneshot in init .rc files
+ * - configures services as disabled in init.rc files, unless a client is
+ *   guaranteed early in boot, in which case, forcePersist should also be used
+ *   to avoid races.
+ * - uses 'interface' declarations in init .rc files
+ *
+ * For more information on init .rc configuration, see system/core/init/README.md
+ **/
 class LazyServiceRegistrar {
    public:
      static LazyServiceRegistrar& getInstance();
@@ -47,4 +59,4 @@
 };
 
 }  // namespace binder
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/libs/binder/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
index 4dd3638..61a029c 100644
--- a/libs/binder/include/binder/MemoryBase.h
+++ b/libs/binder/include/binder/MemoryBase.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEMORY_BASE_H
-#define ANDROID_MEMORY_BASE_H
+#pragma once
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -47,5 +46,3 @@
 
 // ---------------------------------------------------------------------------
 } // namespace android
-
-#endif // ANDROID_MEMORY_BASE_H
diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
index 6c1c412..e727772 100644
--- a/libs/binder/include/binder/MemoryDealer.h
+++ b/libs/binder/include/binder/MemoryDealer.h
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEMORY_DEALER_H
-#define ANDROID_MEMORY_DEALER_H
-
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -60,5 +58,3 @@
 
 // ----------------------------------------------------------------------------
 } // namespace android
-
-#endif // ANDROID_MEMORY_DEALER_H
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index edada3d..dd76943 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEMORY_HEAP_BASE_H
-#define ANDROID_MEMORY_HEAP_BASE_H
+#pragma once
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -51,6 +50,8 @@
 
     /*
      * maps memory from ashmem, with the given name for debugging
+     * if the READ_ONLY flag is set, the memory will be writeable by the calling process,
+     * but not by others. this is NOT the case with the other ctors.
      */
     explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr);
 
@@ -71,14 +72,6 @@
     /* this closes this heap -- use carefully */
     void dispose();
 
-    /* this is only needed as a workaround, use only if you know
-     * what you are doing */
-    status_t setDevice(const char* device) {
-        if (mDevice == nullptr)
-            mDevice = device;
-        return mDevice ? NO_ERROR : ALREADY_EXISTS;
-    }
-
 protected:
             MemoryHeapBase();
     // init() takes ownership of fd
@@ -86,7 +79,7 @@
             int flags = 0, const char* device = nullptr);
 
 private:
-    status_t mapfd(int fd, size_t size, off_t offset = 0);
+    status_t mapfd(int fd, bool writeableByCaller, size_t size, off_t offset = 0);
 
     int         mFD;
     size_t      mSize;
@@ -99,5 +92,3 @@
 
 // ---------------------------------------------------------------------------
 } // namespace android
-
-#endif // ANDROID_MEMORY_HEAP_BASE_H
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
deleted file mode 100644
index a98583d..0000000
--- a/libs/binder/include/binder/Nullable.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <optional>
-#include <utility>
-
-namespace android {
-
-namespace aidl {
-
-// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
-// usage:
-//     nullable<Foo> a;
-//     nullable<Foo> b = make_nullable<Foo>(...);
-//     auto c = make_nullable<Foo>(...);
-//     c.reset();
-//     c = make_nullable<Foo>(...);
-//     c = std::move(a);
-
-template <typename T>
-using nullable = std::optional<T>;
-
-template <typename T, typename... Args>
-inline nullable<T> make_nullable(Args&&... args) {
-    return std::make_optional<T>(std::forward<Args>(args)...);
-}
-
-} // namespace aidl
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b6cfb8e..a123b25 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PARCEL_H
-#define ANDROID_PARCEL_H
+#pragma once
 
 #include <map> // for legacy reasons
 #include <string>
@@ -84,8 +83,16 @@
 
     bool                hasFileDescriptors() const;
 
+    // Zeros data when reallocating. Other mitigations may be added
+    // in the future.
+    //
+    // WARNING: some read methods may make additional copies of data.
+    // In order to verify this, heap dumps should be used.
+    void                markSensitive() const;
+
     // Writes the RPC header.
     status_t            writeInterfaceToken(const String16& interface);
+    status_t            writeInterfaceToken(const char16_t* str, size_t len);
 
     // Parses the RPC header, returning true if the interface name
     // in the header matches the expected interface from the caller.
@@ -296,8 +303,6 @@
     status_t            readFloat(float *pArg) const;
     double              readDouble() const;
     status_t            readDouble(double *pArg) const;
-    intptr_t            readIntPtr() const;
-    status_t            readIntPtr(intptr_t *pArg) const;
     bool                readBool() const;
     status_t            readBool(bool *pArg) const;
     char16_t            readChar() const;
@@ -480,24 +485,21 @@
     // uid.
     uid_t               readCallingWorkSourceUid() const;
 
+    void                print(TextOutput& to, uint32_t flags = 0) const;
+
 private:
     typedef void        (*release_func)(Parcel* parcel,
                                         const uint8_t* data, size_t dataSize,
-                                        const binder_size_t* objects, size_t objectsSize,
-                                        void* cookie);
-                        
+                                        const binder_size_t* objects, size_t objectsSize);
+
     uintptr_t           ipcData() const;
     size_t              ipcDataSize() const;
     uintptr_t           ipcObjects() const;
     size_t              ipcObjectsCount() const;
     void                ipcSetDataReference(const uint8_t* data, size_t dataSize,
                                             const binder_size_t* objects, size_t objectsCount,
-                                            release_func relFunc, void* relCookie);
-    
-public:
-    void                print(TextOutput& to, uint32_t flags = 0) const;
+                                            release_func relFunc);
 
-private:
                         Parcel(const Parcel& o);
     Parcel&             operator=(const Parcel& o);
     
@@ -599,8 +601,14 @@
     mutable bool        mHasFds;
     bool                mAllowFds;
 
+    // if this parcelable is involved in a secure transaction, force the
+    // data to be overridden with zero when deallocated
+    mutable bool        mDeallocZero;
+
     release_func        mOwner;
-    void*               mOwnerCookie;
+
+    // TODO(167966510): reserved for binder/version/stability
+    void*               mReserved = reinterpret_cast<void*>(0xAAAAAAAA);
 
     class Blob {
     public:
@@ -1327,5 +1335,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_PARCEL_H
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 71e1d3c..4ba6ba8 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PARCEL_FILE_DESCRIPTOR_H_
-#define ANDROID_PARCEL_FILE_DESCRIPTOR_H_
+#pragma once
 
 #include <android-base/unique_fd.h>
 #include <binder/Parcel.h>
@@ -67,5 +66,3 @@
 
 } // namespace os
 } // namespace android
-
-#endif // ANDROID_OS_PARCEL_FILE_DESCRIPTOR_H_
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index c113279..2c652be 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PARCELABLE_H
-#define ANDROID_PARCELABLE_H
+#pragma once
 
 #include <vector>
 
@@ -53,11 +52,20 @@
     // Returns android::OK on success and an appropriate error otherwise.
     virtual status_t readFromParcel(const Parcel* parcel) = 0;
 
-    // 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+    // WARNING: for use by auto-generated code only (AIDL). Should not be used
+    // manually, or there is a risk of breaking CTS, GTS, VTS, or CTS-on-GSI
+    // tests.
+    enum class Stability : int32_t {
+        STABILITY_LOCAL,
+        STABILITY_VINTF, // corresponds to @VintfStability
+    };
+
+    // 'Stable' means this parcelable is guaranteed to be stable for multiple
+    // years.
     // It must be guaranteed by setting stability field in aidl_interface.
-    // WARNING: isStable() is only expected to be overridden by auto-generated code.
-    // Returns true if this parcelable is stable.
-    virtual bool isStable() const { return false; }
+    // WARNING: getStability() is only expected to be overridden by auto-generated
+    // code. Returns true if this parcelable is stable.
+    virtual Stability getStability() const { return Stability::STABILITY_LOCAL; }
 };  // class Parcelable
 
 #if defined(__clang__)
@@ -65,5 +73,3 @@
 #endif
 
 }  // namespace android
-
-#endif // ANDROID_PARCELABLE_H
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
new file mode 100644
index 0000000..4ea3dd3
--- /dev/null
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <mutex>
+#include <optional>
+#include <tuple>
+
+namespace android {
+namespace os {
+/*
+ * C++ implementation of the Java class android.os.ParcelableHolder
+ */
+class ParcelableHolder : public android::Parcelable {
+public:
+    ParcelableHolder() = delete;
+    explicit ParcelableHolder(Stability stability) : mStability(stability){};
+    virtual ~ParcelableHolder() = default;
+    ParcelableHolder(const ParcelableHolder& other) {
+        mParcelable = other.mParcelable;
+        mParcelableName = other.mParcelableName;
+        if (other.mParcelPtr) {
+            mParcelPtr = std::make_unique<Parcel>();
+            mParcelPtr->appendFrom(other.mParcelPtr.get(), 0, other.mParcelPtr->dataSize());
+        }
+        mStability = other.mStability;
+    };
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    void reset() {
+        this->mParcelable = nullptr;
+        this->mParcelableName = std::nullopt;
+        this->mParcelPtr = nullptr;
+    }
+
+    template <typename T>
+    bool setParcelable(T&& p) {
+        using Tt = typename std::decay<T>::type;
+        return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
+    }
+
+    template <typename T>
+    bool setParcelable(std::shared_ptr<T> p) {
+        static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+        if (p && this->getStability() > p->getStability()) {
+            return false;
+        }
+        this->mParcelable = p;
+        this->mParcelableName = T::getParcelableDescriptor();
+        this->mParcelPtr = nullptr;
+        return true;
+    }
+
+    template <typename T>
+    std::shared_ptr<T> getParcelable() const {
+        static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+        const std::string& parcelableDesc = T::getParcelableDescriptor();
+        if (!this->mParcelPtr) {
+            if (!this->mParcelable || !this->mParcelableName) {
+                ALOGD("empty ParcelableHolder");
+                return nullptr;
+            } else if (parcelableDesc != *mParcelableName) {
+                ALOGD("extension class name mismatch expected:%s actual:%s",
+                      mParcelableName->c_str(), parcelableDesc.c_str());
+                return nullptr;
+            }
+            return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+        }
+        this->mParcelPtr->setDataPosition(0);
+        status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+        if (status != android::OK || parcelableDesc != this->mParcelableName) {
+            this->mParcelableName = std::nullopt;
+            return nullptr;
+        }
+        this->mParcelable = std::make_shared<T>();
+        status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
+        if (status != android::OK) {
+            this->mParcelableName = std::nullopt;
+            this->mParcelable = nullptr;
+            return nullptr;
+        }
+        this->mParcelPtr = nullptr;
+        return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+    }
+
+    Stability getStability() const override { return mStability; }
+
+    inline bool operator!=(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) !=
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+    inline bool operator<(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) <
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+    inline bool operator<=(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) <=
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+    inline bool operator==(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) ==
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+    inline bool operator>(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) >
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+    inline bool operator>=(const ParcelableHolder& rhs) const {
+        return std::tie(mParcelable, mParcelPtr, mStability) >=
+                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+    }
+
+private:
+    mutable std::shared_ptr<Parcelable> mParcelable;
+    mutable std::optional<std::string> mParcelableName;
+    mutable std::unique_ptr<Parcel> mParcelPtr;
+    Stability mStability;
+};
+} // namespace os
+} // namespace android
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index c258215..835a3a8 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef BINDER_PERMISSION_H
-#define BINDER_PERMISSION_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -82,5 +81,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif /* BINDER_PERMISSION_H */
diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h
index 4db522a..e658574 100644
--- a/libs/binder/include/binder/PermissionController.h
+++ b/libs/binder/include/binder/PermissionController.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PERMISSION_CONTROLLER_H
-#define ANDROID_PERMISSION_CONTROLLER_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -65,5 +64,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_PERMISSION_CONTROLLER_H
diff --git a/libs/binder/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
index 322fef9..4517cf2 100644
--- a/libs/binder/include/binder/PersistableBundle.h
+++ b/libs/binder/include/binder/PersistableBundle.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PERSISTABLE_BUNDLE_H
-#define ANDROID_PERSISTABLE_BUNDLE_H
+#pragma once
 
 #include <map>
 #include <set>
@@ -128,5 +127,3 @@
 }  // namespace os
 
 }  // namespace android
-
-#endif  // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
index 6bfd1bc..6b3b5ce 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PROCESS_INFO_SERVICE_H
-#define ANDROID_PROCESS_INFO_SERVICE_H
+#pragma once
 
 #ifndef __ANDROID_VNDK__
 
@@ -83,6 +82,3 @@
 #else // __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif // __ANDROID_VNDK__
-
-#endif // ANDROID_PROCESS_INFO_SERVICE_H
-
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index e57ff1c..46457cd 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PROCESS_STATE_H
-#define ANDROID_PROCESS_STATE_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
@@ -42,20 +41,16 @@
      * any call to ProcessState::self(). The default is /dev/vndbinder
      * for processes built with the VNDK and /dev/binder for those
      * which are not.
+     *
+     * If this is called with nullptr, the behavior is the same as selfOrNull.
      */
     static  sp<ProcessState>    initWithDriver(const char *driver);
 
             sp<IBinder>         getContextObject(const sp<IBinder>& caller);
 
             void                startThreadPool();
-                        
-    typedef bool (*context_check_func)(const String16& name,
-                                       const sp<IBinder>& caller,
-                                       void* userData);
 
-            bool                becomeContextManager(
-                                    context_check_func checkFunc,
-                                    void* userData);
+            bool                becomeContextManager();
 
             sp<IBinder>         getStrongProxyForHandle(int32_t handle);
             void                expungeHandle(int32_t handle, IBinder* binder);
@@ -90,6 +85,8 @@
             void setCallRestriction(CallRestriction restriction);
 
 private:
+    static  sp<ProcessState>    init(const char *defaultDriver, bool requireDefault);
+
     friend class IPCThreadState;
     
             explicit            ProcessState(const char* driver);
@@ -124,9 +121,6 @@
 
             Vector<handle_entry>mHandleToObject;
 
-            context_check_func  mBinderContextCheckFunc;
-            void*               mBinderContextUserData;
-
             String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
@@ -137,5 +131,3 @@
 } // namespace android
 
 // ---------------------------------------------------------------------------
-
-#endif // ANDROID_PROCESS_STATE_H
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 2894482..6566285 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,11 +81,8 @@
         VINTF = 0b111111,
     };
 
-#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
-    static constexpr Level kLocalStability = Level::VENDOR;
-#else
-    static constexpr Level kLocalStability = Level::SYSTEM;
-#endif
+    // returns the stability according to how this was built
+    static Level getLocalStability();
 
     // applies stability to binder if stability level is known
     __attribute__((warn_unused_result))
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index f66406f..bf9c92b 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_TEXTOUTPUT_H
-#define ANDROID_TEXTOUTPUT_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
@@ -50,12 +49,18 @@
 
 // ---------------------------------------------------------------------------
 
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
 // Text output stream for printing to the log (via utils/Log.h).
 extern TextOutput& alog;
 
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
 // Text output stream for printing to stdout.
 extern TextOutput& aout;
 
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
 // Text output stream for printing to stderr.
 extern TextOutput& aerr;
 
@@ -200,5 +205,3 @@
 
 // ---------------------------------------------------------------------------
 } // namespace android
-
-#endif // ANDROID_TEXTOUTPUT_H
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index c22be9f..0fe7f5b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -36,6 +36,62 @@
 #include <sys/ioctl.h>
 #include <linux/android/binder.h>
 
+#ifndef BR_FROZEN_REPLY
+// Temporary definition of BR_FROZEN_REPLY. For production
+// this will come from UAPI binder.h
+#define BR_FROZEN_REPLY _IO('r', 18)
+#endif //BR_FROZEN_REPLY
+
+#ifndef BINDER_FREEZE
+/*
+ * Temporary definitions for freeze support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
+
+struct binder_freeze_info {
+    //
+    // Group-leader PID of process to be frozen
+    //
+    uint32_t            pid;
+    //
+    // Enable(1) / Disable(0) freeze for given PID
+    //
+    uint32_t            enable;
+    //
+    // Timeout to wait for transactions to drain.
+    // 0: don't wait (ioctl will return EAGAIN if not drained)
+    // N: number of ms to wait
+    uint32_t            timeout_ms;
+};
+#endif //BINDER_FREEZE
+
+#ifndef BINDER_GET_FROZEN_INFO
+
+#define BINDER_GET_FROZEN_INFO          _IOWR('b', 15, struct binder_frozen_status_info)
+
+struct binder_frozen_status_info {
+    //
+    // Group-leader PID of process to be queried
+    //
+    __u32            pid;
+    //
+    // Indicates whether the process has received any sync calls since last
+    // freeze (cleared at freeze/unfreeze)
+    //
+    __u32            sync_recv;
+    //
+    // Indicates whether the process has received any async calls since last
+    // freeze (cleared at freeze/unfreeze)
+    //
+    __u32            async_recv;
+};
+#endif //BINDER_GET_FROZEN_INFO
+
+enum transaction_flags_ext {
+    TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
+};
+
 #ifdef __cplusplus
 }   // namespace android
 #endif
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index b37db43..cecc759 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -26,6 +26,9 @@
                 "-D__ANDROID_API__=10000",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
     },
 }
 
@@ -35,7 +38,10 @@
     defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
 
+    llndk_stubs: "libbinder_ndk.llndk",
+
     export_include_dirs: [
+        "include_cpp",
         "include_ndk",
         "include_platform",
     ],
@@ -81,6 +87,9 @@
         linux: {
             version_script: "libbinder_ndk.map.txt",
         },
+        darwin: {
+            enabled: false,
+        },
     },
     stubs: {
         symbol_file: "libbinder_ndk.map.txt",
@@ -101,6 +110,17 @@
     license: "NOTICE",
 }
 
+// TODO(b/160624671): package with the aidl compiler
+ndk_headers {
+    name: "libbinder_ndk_helper_headers",
+    from: "include_cpp/android",
+    to: "android",
+    srcs: [
+        "include_cpp/android/*.h",
+    ],
+    license: "NOTICE",
+}
+
 ndk_library {
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
@@ -108,9 +128,10 @@
 }
 
 llndk_library {
-    name: "libbinder_ndk",
+    name: "libbinder_ndk.llndk",
     symbol_file: "libbinder_ndk.map.txt",
     export_include_dirs: [
+        "include_cpp",
         "include_ndk",
         "include_platform",
     ],
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 649faa1..b927f6f 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
 #include "ibinder_internal.h"
 
 #include <android/binder_stability.h>
@@ -71,14 +73,13 @@
 AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
 AIBinder::~AIBinder() {}
 
-bool AIBinder::associateClass(const AIBinder_Class* clazz) {
-    if (clazz == nullptr) return false;
+std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
+                                                     const String16& newDescriptor, bool set) {
+    std::lock_guard<std::mutex> lock(mClazzMutex);
     if (mClazz == clazz) return true;
 
-    String8 newDescriptor(clazz->getInterfaceDescriptor());
-
     if (mClazz != nullptr) {
-        String8 currentDescriptor(mClazz->getInterfaceDescriptor());
+        const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
         if (newDescriptor == currentDescriptor) {
             LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
                        << "' match during associateClass, but they are different class objects. "
@@ -87,27 +88,46 @@
             LOG(ERROR) << __func__
                        << ": Class cannot be associated on object which already has a class. "
                           "Trying to associate to '"
-                       << newDescriptor.c_str() << "' but already set to '"
-                       << currentDescriptor.c_str() << "'.";
+                       << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
         }
 
         // always a failure because we know mClazz != clazz
         return false;
     }
 
+    if (set) {
+        // if this is a local object, it's not one known to libbinder_ndk
+        mClazz = clazz;
+        return true;
+    }
+
+    return {};
+}
+
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
+    if (clazz == nullptr) return false;
+
+    const String16& newDescriptor = clazz->getInterfaceDescriptor();
+
+    auto result = associateClassInternal(clazz, newDescriptor, false);
+    if (result.has_value()) return *result;
+
     CHECK(asABpBinder() != nullptr);  // ABBinder always has a descriptor
 
-    String8 descriptor(getBinder()->getInterfaceDescriptor());
+    const String16& descriptor = getBinder()->getInterfaceDescriptor();
     if (descriptor != newDescriptor) {
-        LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
-                   << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+        if (getBinder()->isBinderAlive()) {
+            LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
+                       << "' but descriptor is actually '" << descriptor << "'.";
+        } else {
+            // b/155793159
+            LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
+                       << "' to dead binder.";
+        }
         return false;
     }
 
-    // if this is a local object, it's not one known to libbinder_ndk
-    mClazz = clazz;
-
-    return true;
+    return associateClassInternal(clazz, newDescriptor, true).value();
 }
 
 ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
@@ -161,7 +181,7 @@
 
         binder_status_t status = getClass()->onTransact(this, code, &in, &out);
         return PruneStatusT(status);
-    } else if (code == SHELL_COMMAND_TRANSACTION) {
+    } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
         int in = data.readFileDescriptor();
         int out = data.readFileDescriptor();
         int err = data.readFileDescriptor();
@@ -591,7 +611,7 @@
         return STATUS_UNKNOWN_TRANSACTION;
     }
 
-    constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY;
+    constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
     if ((flags & ~kAllFlags) != 0) {
         LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
         return STATUS_BAD_VALUE;
@@ -676,3 +696,29 @@
     rawBinder->setExtension(ext->getBinder());
     return STATUS_OK;
 }
+
+// platform methods follow
+
+void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) {
+    ABBinder* localBinder = binder->asABBinder();
+    if (localBinder == nullptr) {
+        LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder";
+    }
+
+    localBinder->setRequestingSid(requestingSid);
+}
+
+const char* AIBinder_getCallingSid() {
+    return ::android::IPCThreadState::self()->getCallingSid();
+}
+
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) {
+    if (binder == nullptr) return nullptr;
+    return binder->getBinder();
+}
+
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) {
+    sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder);
+    AIBinder_incStrong(ndkBinder.get());
+    return ndkBinder.get();
+}
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5779427..0fa47c6 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -22,6 +22,7 @@
 
 #include <atomic>
 #include <mutex>
+#include <optional>
 #include <vector>
 
 #include <binder/Binder.h>
@@ -52,10 +53,14 @@
     }
 
    private:
+    std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
+                                               const ::android::String16& newDescriptor, bool set);
+
     // AIBinder instance is instance of this class for a local object. In order to transact on a
     // remote object, this also must be set for simplicity (although right now, only the
     // interfaceDescriptor from it is used).
     const AIBinder_Class* mClazz;
+    std::mutex mClazzMutex;
 };
 
 // This is a local AIBinder object with a known class.
@@ -110,13 +115,13 @@
     const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
 
     // required to be non-null, implemented for every class
-    const AIBinder_Class_onCreate onCreate;
-    const AIBinder_Class_onDestroy onDestroy;
-    const AIBinder_Class_onTransact onTransact;
+    const AIBinder_Class_onCreate onCreate = nullptr;
+    const AIBinder_Class_onDestroy onDestroy = nullptr;
+    const AIBinder_Class_onTransact onTransact = nullptr;
 
     // optional methods for a class
-    AIBinder_onDump onDump;
-    AIBinder_handleShellCommand handleShellCommand;
+    AIBinder_onDump onDump = nullptr;
+    AIBinder_handleShellCommand handleShellCommand = nullptr;
 
    private:
     // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
similarity index 92%
rename from libs/binder/ndk/include_ndk/android/binder_auto_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 2b61cf1..8d60226 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -44,9 +44,14 @@
 class SpAIBinder {
    public:
     /**
+     * Default constructor.
+     */
+    SpAIBinder() : mBinder(nullptr) {}
+
+    /**
      * Takes ownership of one strong refcount of binder.
      */
-    explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {}
+    explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
 
     /**
      * Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not
@@ -189,6 +194,7 @@
     explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {}
     ~ScopedAParcel() {}
     ScopedAParcel(ScopedAParcel&&) = default;
+    ScopedAParcel& operator=(ScopedAParcel&&) = default;
 };
 
 /**
@@ -198,6 +204,9 @@
    public:
     /**
      * Takes ownership of a.
+     *
+     * WARNING: this constructor is only expected to be used when reading a
+     *     status value. Use `ScopedAStatus::ok()` instead.
      */
     explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
     ~ScopedAStatus() {}
@@ -273,6 +282,7 @@
         : ScopedAResource(a) {}
     ~ScopedAIBinder_DeathRecipient() {}
     ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default;
+    ScopedAIBinder_DeathRecipient& operator=(ScopedAIBinder_DeathRecipient&&) = default;
 };
 
 /**
@@ -287,6 +297,7 @@
     explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {}
     ~ScopedAIBinder_Weak() {}
     ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default;
+    ScopedAIBinder_Weak& operator=(ScopedAIBinder_Weak&&) = default;
 
     /**
      * See AIBinder_Weak_promote.
@@ -302,9 +313,11 @@
     /**
      * Takes ownership of a.
      */
-    explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+    ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+    explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
     ~ScopedFileDescriptor() {}
     ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
+    ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
 };
 
 }  // namespace ndk
diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_cpp/android/binder_enums.h
similarity index 100%
rename from libs/binder/ndk/include_ndk/android/binder_enums.h
rename to libs/binder/ndk/include_cpp/android/binder_enums.h
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
similarity index 96%
rename from libs/binder/ndk/include_ndk/android/binder_interface_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 33e4586..a4f4441 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -88,13 +88,21 @@
 
     static void operator delete(void* p) { std::free(p); }
 
+    // Once minSdkVersion is 30, we are guaranteed to be building with the
+    // Android 11 AIDL compiler which supports the SharedRefBase::make API.
+    //
+    // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
+    // ownership. Making this operator private to avoid double-ownership.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
+   private:
+#else
+    [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
+#endif
+    static void* operator new(size_t s) { return std::malloc(s); }
+
    private:
     std::once_flag mFlagThis;
     std::weak_ptr<SharedRefBase> mThis;
-
-    // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
-    // ownership. Making this operator private to avoid double-ownership.
-    static void* operator new(size_t s) { return std::malloc(s); }
 };
 
 /**
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
similarity index 94%
rename from libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 09949ea..054aebe 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -45,7 +45,7 @@
     std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
     if (static_cast<size_t>(length) > vec->max_size()) return false;
 
-    vec->resize(length);
+    vec->resize(static_cast<size_t>(length));
     *outBuffer = vec->data();
     return true;
 }
@@ -66,7 +66,7 @@
     *vec = std::optional<std::vector<T>>(std::vector<T>{});
 
     if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
-    (*vec)->resize(length);
+    (*vec)->resize(static_cast<size_t>(length));
 
     *outBuffer = (*vec)->data();
     return true;
@@ -90,7 +90,7 @@
     std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
     if (static_cast<size_t>(length) > vec->max_size()) return false;
 
-    vec->resize(length);
+    vec->resize(static_cast<size_t>(length));
     return true;
 }
 
@@ -117,7 +117,7 @@
     *vec = std::optional<std::vector<T>>(std::vector<T>{});
 
     if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
-    (*vec)->resize(length);
+    (*vec)->resize(static_cast<size_t>(length));
 
     return true;
 }
@@ -257,7 +257,7 @@
     if (length <= 0) return false;
 
     std::string* str = static_cast<std::string*>(stringData);
-    str->resize(length - 1);
+    str->resize(static_cast<size_t>(length) - 1);
     *buffer = &(*str)[0];
     return true;
 }
@@ -279,7 +279,7 @@
     }
 
     *str = std::optional<std::string>(std::string{});
-    (*str)->resize(length - 1);
+    (*str)->resize(static_cast<size_t>(length) - 1);
     *buffer = &(**str)[0];
     return true;
 }
@@ -303,7 +303,7 @@
     const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData);
     const std::string& element = vec->at(index);
 
-    *outLength = element.size();
+    *outLength = static_cast<int32_t>(element.size());
     return element.c_str();
 }
 
@@ -337,7 +337,7 @@
         return nullptr;
     }
 
-    *outLength = element->size();
+    *outLength = static_cast<int32_t>(element->size());
     return element->c_str();
 }
 
@@ -345,7 +345,7 @@
  * Convenience API for writing a std::string.
  */
 static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
-    return AParcel_writeString(parcel, str.c_str(), str.size());
+    return AParcel_writeString(parcel, str.c_str(), static_cast<int32_t>(str.size()));
 }
 
 /**
@@ -365,7 +365,7 @@
         return AParcel_writeString(parcel, nullptr, -1);
     }
 
-    return AParcel_writeString(parcel, str->c_str(), str->size());
+    return AParcel_writeString(parcel, str->c_str(), static_cast<int32_t>(str->size()));
 }
 
 /**
@@ -383,7 +383,7 @@
 static inline binder_status_t AParcel_writeVector(AParcel* parcel,
                                                   const std::vector<std::string>& vec) {
     const void* vectorData = static_cast<const void*>(&vec);
-    return AParcel_writeStringArray(parcel, vectorData, vec.size(),
+    return AParcel_writeStringArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
                                     AParcel_stdVectorStringElementGetter);
 }
 
@@ -404,7 +404,8 @@
 static inline binder_status_t AParcel_writeVector(
         AParcel* parcel, const std::optional<std::vector<std::optional<std::string>>>& vec) {
     const void* vectorData = static_cast<const void*>(&vec);
-    return AParcel_writeStringArray(parcel, vectorData, (vec ? vec->size() : -1),
+    return AParcel_writeStringArray(parcel, vectorData,
+                                    (vec ? static_cast<int32_t>(vec->size()) : -1),
                                     AParcel_nullableStdVectorStringElementGetter);
 }
 
@@ -545,7 +546,7 @@
 template <typename P>
 static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
     const void* vectorData = static_cast<const void*>(&vec);
-    return AParcel_writeParcelableArray(parcel, vectorData, vec.size(),
+    return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
                                         AParcel_writeStdVectorParcelableElement<P>);
 }
 
@@ -564,7 +565,7 @@
  * Writes a vector of int32_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int32_t>& vec) {
-    return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+    return AParcel_writeInt32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -597,7 +598,7 @@
  * Writes a vector of uint32_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint32_t>& vec) {
-    return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+    return AParcel_writeUint32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -631,7 +632,7 @@
  * Writes a vector of int64_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int64_t>& vec) {
-    return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+    return AParcel_writeInt64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -664,7 +665,7 @@
  * Writes a vector of uint64_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint64_t>& vec) {
-    return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+    return AParcel_writeUint64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -698,7 +699,7 @@
  * Writes a vector of float to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<float>& vec) {
-    return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+    return AParcel_writeFloatArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -731,7 +732,7 @@
  * Writes a vector of double to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<double>& vec) {
-    return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+    return AParcel_writeDoubleArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -764,8 +765,8 @@
  * Writes a vector of bool to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<bool>& vec) {
-    return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec), vec.size(),
-                                  AParcel_stdVectorGetter<bool>);
+    return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+                                  static_cast<int32_t>(vec.size()), AParcel_stdVectorGetter<bool>);
 }
 
 /**
@@ -801,7 +802,7 @@
  * Writes a vector of char16_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<char16_t>& vec) {
-    return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+    return AParcel_writeCharArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
 }
 
 /**
@@ -834,7 +835,8 @@
  * Writes a vector of uint8_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) {
-    return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size());
+    return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+                                  static_cast<int32_t>(vec.size()));
 }
 
 /**
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
new file mode 100644
index 0000000..dfcf4dc
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcelable_utils.h
+ * @brief Helper for parcelable.
+ */
+
+#pragma once
+#include <android/binder_parcel_utils.h>
+#include <optional>
+
+namespace ndk {
+// Also see Parcelable.h in libbinder.
+typedef int32_t parcelable_stability_t;
+enum {
+    STABILITY_LOCAL,
+    STABILITY_VINTF,  // corresponds to @VintfStability
+};
+#define RETURN_ON_FAILURE(expr)                   \
+    do {                                          \
+        binder_status_t _status = (expr);         \
+        if (_status != STATUS_OK) return _status; \
+    } while (false)
+
+class AParcelableHolder {
+   public:
+    AParcelableHolder() = delete;
+    explicit AParcelableHolder(parcelable_stability_t stability)
+        : mParcel(AParcel_create()), mStability(stability) {}
+
+    virtual ~AParcelableHolder() = default;
+
+    binder_status_t writeToParcel(AParcel* parcel) const {
+        RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+        RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
+        RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
+                                             AParcel_getDataSize(this->mParcel.get())));
+        return STATUS_OK;
+    }
+
+    binder_status_t readFromParcel(const AParcel* parcel) {
+        AParcel_reset(mParcel.get());
+
+        RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
+        int32_t dataSize;
+        binder_status_t status = AParcel_readInt32(parcel, &dataSize);
+
+        if (status != STATUS_OK || dataSize < 0) {
+            return status != STATUS_OK ? status : STATUS_BAD_VALUE;
+        }
+
+        int32_t dataStartPos = AParcel_getDataPosition(parcel);
+
+        if (dataStartPos > INT32_MAX - dataSize) {
+            return STATUS_BAD_VALUE;
+        }
+
+        status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+        if (status != STATUS_OK) {
+            return status;
+        }
+        return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
+    }
+
+    template <typename T>
+    bool setParcelable(T* p) {
+        if (p && this->mStability > T::_aidl_stability) {
+            return false;
+        }
+        AParcel_reset(mParcel.get());
+        AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
+        p->writeToParcel(mParcel.get());
+        return true;
+    }
+
+    template <typename T>
+    std::unique_ptr<T> getParcelable() const {
+        const std::string parcelableDesc(T::descriptor);
+        AParcel_setDataPosition(mParcel.get(), 0);
+        if (AParcel_getDataSize(mParcel.get()) == 0) {
+            return nullptr;
+        }
+        std::string parcelableDescInParcel;
+        binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
+        if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
+            return nullptr;
+        }
+        std::unique_ptr<T> ret = std::make_unique<T>();
+        status = ret->readFromParcel(this->mParcel.get());
+        if (status != STATUS_OK) {
+            return nullptr;
+        }
+        return std::move(ret);
+    }
+
+   private:
+    mutable ndk::ScopedAParcel mParcel;
+    parcelable_stability_t mStability;
+};
+
+#undef RETURN_ON_FAILURE
+}  // namespace ndk
+
+/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4560f22..ce3d1db 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
@@ -42,7 +43,6 @@
 
 #if __ANDROID_API__ >= 29
 
-// Also see TF_* in kernel's binder.h
 typedef uint32_t binder_flags_t;
 enum {
     /**
@@ -407,6 +407,8 @@
  * This returns true if the class association succeeds. If it fails, no change is made to the
  * binder object.
  *
+ * Warning: this may fail if the binder is dead.
+ *
  * Available since API level 29.
  *
  * \param binder the object to attach the class to.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 86b75b8..93c3f32 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
 
@@ -1119,6 +1120,53 @@
 // @END-PRIMITIVE-READ-WRITE
 
 #endif  //__ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 31
+/**
+ * Reset the parcel to the initial status.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to be reset.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_reset(AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Gets the size of the parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to get the size.
+ *
+ * \return The size of the parcel.
+ */
+int32_t AParcel_getDataSize(const AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Copy the data of a parcel to other parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param from The source
+ * \param to The detination
+ * \param start The position where the copied data starts.
+ * \param size The amount of data which will be copied.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size)
+        __INTRODUCED_IN(31);
+
+/**
+ * Creates a parcel.
+ *
+ * Available since API level 31.
+ *
+ * \return A parcel which is not related to any IBinder objects.
+ */
+AParcel* AParcel_create() __INTRODUCED_IN(31);
+#endif  //__ANDROID_API__ >= 31
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index ab9a144..3a55f94 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -26,6 +26,7 @@
 #pragma once
 
 #include <errno.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
 
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
new file mode 100644
index 0000000..e315c79
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+// platform values for binder_flags_t
+enum {
+    /**
+     * The transaction and reply will be cleared by the kernel in read-only
+     * binder buffers storing transactions.
+     *
+     * Introduced in API level 31.
+     */
+    FLAG_CLEAR_BUF = 0x20,
+};
+
+/**
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
+ *
+ * \param binder local server binder to request security contexts on
+ */
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+        __INTRODUCED_IN(31);
+
+/**
+ * Returns the selinux context of the callee.
+ *
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
+ *
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
+ */
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
new file mode 100644
index 0000000..f0c00e8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+#include <android/binder_ibinder.h>
+#include <binder/IBinder.h>
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 055c79b..2784aa8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,6 +18,7 @@
 
 #include <android/binder_ibinder.h>
 #include <android/binder_status.h>
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -28,9 +29,9 @@
  * \param binder object to register globally with the service manager.
  * \param instance identifier of the service. This will be used to lookup the service.
  *
- * \return STATUS_OK on success.
+ * \return EX_NONE on success.
  */
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance);
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance);
 
 /**
  * Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -50,4 +51,47 @@
  */
 __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
 
+/**
+ * Registers a lazy service with the default service manager under the 'instance' name.
+ * Does not take ownership of binder.
+ * The service must be configured statically with init so it can be restarted with
+ * ctl.interface.* messages from servicemanager.
+ * AServiceManager_registerLazyService cannot safely be used with AServiceManager_addService
+ * in the same process. If one service is registered with AServiceManager_registerLazyService,
+ * the entire process will have its lifetime controlled by servicemanager.
+ * Instead, all services in the process should be registered using
+ * AServiceManager_registerLazyService.
+ *
+ * \param binder object to register globally with the service manager.
+ * \param instance identifier of the service. This will be used to lookup the service.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance)
+        __INTRODUCED_IN(31);
+
+/**
+ * Gets a binder object with this specific instance name. Efficiently waits for the service.
+ * If the service is not declared, it will wait indefinitely. Requires the threadpool
+ * to be started in the service.
+ * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
+ * for calling AIBinder_decStrong).
+ *
+ * \param instance identifier of the service used to lookup the service.
+ *
+ * \return service if registered, null if not.
+ */
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(const char* instance)
+        __INTRODUCED_IN(31);
+
+/**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * \param instance identifier of the service.
+ *
+ * \return true on success, meaning AServiceManager_waitForService should always
+ *    be able to return the service.
+ */
+bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
+
 __END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index ac46cb8..d54c1a1 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -20,6 +20,10 @@
 
 __BEGIN_DECLS
 
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error this is only for platform code
+#endif
+
 /**
  * Gets whether or not FDs are allowed by this AParcel
  *
@@ -29,4 +33,15 @@
  */
 bool AParcel_getAllowFds(const AParcel*);
 
-__END_DECLS
\ No newline at end of file
+/**
+ * Data written to the parcel will be zero'd before being deleted or realloced.
+ *
+ * The main use of this is marking a parcel that will be used in a transaction
+ * with FLAG_CLEAR_BUF. When FLAG_CLEAR_BUF is used, the reply parcel will
+ * automatically be marked as sensitive when it is created.
+ *
+ * \param parcel The parcel to clear associated data from.
+ */
+void AParcel_markSensitive(const AParcel* parcel);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index fdefbb4..f408fad 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -19,10 +19,15 @@
 #include <stdint.h>
 #include <sys/cdefs.h>
 
+#include <android/binder_status.h>
+
 __BEGIN_DECLS
 
 /**
  * This creates a threadpool for incoming binder transactions if it has not already been created.
+ *
+ * When using this, it is expected that ABinderProcess_setupPolling and
+ * ABinderProcess_handlePolledCommands are not used.
  */
 void ABinderProcess_startThreadPool();
 /**
@@ -37,4 +42,27 @@
  */
 void ABinderProcess_joinThreadPool();
 
+/**
+ * This gives you an fd to wait on. Whenever data is available on the fd,
+ * ABinderProcess_handlePolledCommands can be called to handle binder queries.
+ * This is expected to be used in a single threaded process which waits on
+ * events from multiple different fds.
+ *
+ * When using this, it is expected ABinderProcess_startThreadPool and
+ * ABinderProcess_joinThreadPool are not used.
+ *
+ * \param fd out param corresponding to the binder domain opened in this
+ * process.
+ * \return STATUS_OK on success
+ */
+__attribute__((weak)) binder_status_t ABinderProcess_setupPolling(int* fd) __INTRODUCED_IN(31);
+
+/**
+ * This will handle all queued binder commands in this process and then return.
+ * It is expected to be called whenever there is data on the fd.
+ *
+ * \return STATUS_OK on success
+ */
+__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31);
+
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index a9eba47..6962f86 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -95,8 +95,6 @@
     AServiceManager_addService; # apex llndk
     AServiceManager_checkService; # apex llndk
     AServiceManager_getService; # apex llndk
-  local:
-    *;
 };
 
 LIBBINDER_NDK30 { # introduced=30
@@ -111,11 +109,32 @@
     AIBinder_markVendorStability; # llndk
     AIBinder_markVintfStability; # apex llndk
     AIBinder_Class_setHandleShellCommand; # apex llndk
-  local:
-    *;
+};
+
+LIBBINDER_NDK31 { # introduced=31
+  global:
+    ABinderProcess_handlePolledCommands; # apex
+    ABinderProcess_setupPolling; # apex
+    AIBinder_getCallingSid; # apex
+    AIBinder_setRequestingSid; # apex
+    AServiceManager_isDeclared; # apex llndk
+    AServiceManager_registerLazyService; # llndk
+    AServiceManager_waitForService; # apex llndk
+
+    AParcel_appendFrom;
+    AParcel_create;
+    AParcel_getDataSize;
+    AParcel_reset;
 };
 
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;
+    AParcel_markSensitive;
+    extern "C++" {
+        AIBinder_fromPlatformBinder*;
+        AIBinder_toPlatformBinder*;
+    };
+  local:
+    *;
 };
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c33c44f..3e3eda1 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -226,6 +226,10 @@
     return parcel->get()->dataPosition();
 }
 
+void AParcel_markSensitive(const AParcel* parcel) {
+    return parcel->get()->markSensitive();
+}
+
 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
     sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
     return parcel->get()->writeStrongBinder(writeBinder);
@@ -272,7 +276,7 @@
 }
 
 binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) {
-    return PruneStatusT(status->get()->writeToParcel(parcel->get()));
+    return PruneStatusT(status->get().writeToParcel(parcel->get()));
 }
 binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) {
     ::android::binder::Status bstatus;
@@ -647,4 +651,22 @@
     return parcel->get()->allowFds();
 }
 
+binder_status_t AParcel_reset(AParcel* parcel) {
+    parcel->get()->freeData();
+    return STATUS_OK;
+}
+
+int32_t AParcel_getDataSize(const AParcel* parcel) {
+    return parcel->get()->dataSize();
+}
+
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size) {
+    status_t status = to->get()->appendFrom(from->get(), start, size);
+    return PruneStatusT(status);
+}
+
+AParcel* AParcel_create() {
+    return new AParcel(nullptr);
+}
+
 // @END
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index c89caaf..ac582a4 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -34,3 +34,11 @@
 void ABinderProcess_joinThreadPool() {
     IPCThreadState::self()->joinThreadPool();
 }
+
+binder_status_t ABinderProcess_setupPolling(int* fd) {
+    return IPCThreadState::self()->setupPolling(fd);
+}
+
+binder_status_t ABinderProcess_handlePolledCommands() {
+    return IPCThreadState::self()->handlePolledCommands();
+}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d0b166d..c782d47 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -20,6 +20,7 @@
 #include "status_internal.h"
 
 #include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
 
 using ::android::defaultServiceManager;
 using ::android::IBinder;
@@ -28,14 +29,14 @@
 using ::android::status_t;
 using ::android::String16;
 
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) {
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
     if (binder == nullptr || instance == nullptr) {
-        return STATUS_UNEXPECTED_NULL;
+        return EX_ILLEGAL_ARGUMENT;
     }
 
     sp<IServiceManager> sm = defaultServiceManager();
-    status_t status = sm->addService(String16(instance), binder->getBinder());
-    return PruneStatusT(status);
+    status_t exception = sm->addService(String16(instance), binder->getBinder());
+    return PruneException(exception);
 }
 AIBinder* AServiceManager_checkService(const char* instance) {
     if (instance == nullptr) {
@@ -61,3 +62,33 @@
     AIBinder_incStrong(ret.get());
     return ret.get();
 }
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) {
+    if (binder == nullptr || instance == nullptr) {
+        return STATUS_UNEXPECTED_NULL;
+    }
+
+    auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
+    status_t status = serviceRegistrar.registerService(binder->getBinder(), instance);
+
+    return PruneStatusT(status);
+}
+AIBinder* AServiceManager_waitForService(const char* instance) {
+    if (instance == nullptr) {
+        return nullptr;
+    }
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->waitForService(String16(instance));
+
+    sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+    AIBinder_incStrong(ret.get());
+    return ret.get();
+}
+bool AServiceManager_isDeclared(const char* instance) {
+    if (instance == nullptr) {
+        return false;
+    }
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    return sm->isDeclared(String16(instance));
+}
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 87e1341..a8ae441 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -23,7 +23,8 @@
 using ::android::binder::Status;
 
 AStatus* AStatus_newOk() {
-    return new AStatus();
+    static AStatus status = AStatus();
+    return &status;
 }
 
 AStatus* AStatus_fromExceptionCode(binder_exception_t exception) {
@@ -47,27 +48,27 @@
 }
 
 bool AStatus_isOk(const AStatus* status) {
-    return status->get()->isOk();
+    return status->get().isOk();
 }
 
 binder_exception_t AStatus_getExceptionCode(const AStatus* status) {
-    return PruneException(status->get()->exceptionCode());
+    return PruneException(status->get().exceptionCode());
 }
 
 int32_t AStatus_getServiceSpecificError(const AStatus* status) {
-    return status->get()->serviceSpecificErrorCode();
+    return status->get().serviceSpecificErrorCode();
 }
 
 binder_status_t AStatus_getStatus(const AStatus* status) {
-    return PruneStatusT(status->get()->transactionError());
+    return PruneStatusT(status->get().transactionError());
 }
 
 const char* AStatus_getMessage(const AStatus* status) {
-    return status->get()->exceptionMessage().c_str();
+    return status->get().exceptionMessage().c_str();
 }
 
 const char* AStatus_getDescription(const AStatus* status) {
-    android::String8 description = status->get()->toString8();
+    android::String8 description = status->get().toString8();
     char* cStr = new char[description.size() + 1];
     memcpy(cStr, description.c_str(), description.size() + 1);
     return cStr;
@@ -78,7 +79,9 @@
 }
 
 void AStatus_delete(AStatus* status) {
-    delete status;
+    if (status != AStatus_newOk()) {
+        delete status;
+    }
 }
 
 binder_status_t PruneStatusT(status_t status) {
@@ -123,8 +126,8 @@
             return STATUS_UNKNOWN_ERROR;
 
         default:
-            LOG(WARNING) << __func__
-                         << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status;
+            LOG(WARNING) << __func__ << ": Unknown status_t (" << status
+                         << ") pruned into STATUS_UNKNOWN_ERROR";
             return STATUS_UNKNOWN_ERROR;
     }
 }
@@ -155,8 +158,8 @@
             return EX_TRANSACTION_FAILED;
 
         default:
-            LOG(WARNING) << __func__
-                         << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception;
+            LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
+                         << ") pruned into EX_TRANSACTION_FAILED";
             return EX_TRANSACTION_FAILED;
     }
 }
diff --git a/libs/binder/ndk/status_internal.h b/libs/binder/ndk/status_internal.h
index f6227f7..cb96e48 100644
--- a/libs/binder/ndk/status_internal.h
+++ b/libs/binder/ndk/status_internal.h
@@ -25,8 +25,7 @@
     AStatus() {}  // ok
     explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {}
 
-    ::android::binder::Status* get() { return &mStatus; }
-    const ::android::binder::Status* get() const { return &mStatus; }
+    const ::android::binder::Status& get() const { return mStatus; }
 
    private:
     ::android::binder::Status mStatus;
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 5f5265c..46e6270 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -40,6 +40,7 @@
 cc_defaults {
     name: "test_libbinder_ndk_test_defaults",
     defaults: ["test_libbinder_ndk_defaults"],
+    // critical that libbinder/libbinder_ndk are shared for VTS
     shared_libs: [
         "libandroid_runtime_lazy",
         "libbase",
@@ -63,11 +64,8 @@
         "IBinderNdkUnitTest-cpp",
         "IBinderNdkUnitTest-ndk_platform",
     ],
-    test_suites: ["general-tests"],
+    test_suites: ["general-tests", "vts"],
     require_root: true,
-
-    // force since binderVendorDoubleLoadTest has its own
-    auto_gen_config: true,
 }
 
 cc_test {
@@ -81,13 +79,15 @@
         "IBinderVendorDoubleLoadTest-ndk_platform",
         "libbinder_aidl_test_stub-ndk_platform",
     ],
+    // critical that libbinder/libbinder_ndk are shared for VTS
     shared_libs: [
         "libbase",
         "libbinder",
         "libbinder_ndk",
         "libutils",
     ],
-    test_suites: ["general-tests"],
+    test_suites: ["general-tests", "vts"],
+    require_root: true,
 }
 
 aidl_interface {
diff --git a/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
index 6e8e463..dc77467d 100644
--- a/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
+++ b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
@@ -22,6 +22,10 @@
 import IEmpty;
 
 interface IBinderNdkUnitTest {
+    int repeatInt(int a);
+
     void takeInterface(IEmpty test);
     void forceFlushCommands();
+
+    boolean getsRequestedSid();
 }
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 64832f3..a588985 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -118,7 +118,7 @@
     AIBinder_Weak_delete(mWeakBinder);
 }
 
-binder_status_t IFoo::addService(const char* instance) {
+AIBinder* IFoo::getBinder() {
     AIBinder* binder = nullptr;
 
     if (mWeakBinder != nullptr) {
@@ -132,8 +132,18 @@
             AIBinder_Weak_delete(mWeakBinder);
         }
         mWeakBinder = AIBinder_Weak_new(binder);
+
+        // WARNING: it is important that this class does not implement debug or
+        // shell functions because it does not use special C++ wrapper
+        // functions, and so this is how we test those functions.
     }
 
+    return binder;
+}
+
+binder_status_t IFoo::addService(const char* instance) {
+    AIBinder* binder = getBinder();
+
     binder_status_t status = AServiceManager_addService(binder, instance);
     // Strong references we care about kept by remote process
     AIBinder_decStrong(binder);
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index cdf5493..d9dd64b 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -30,6 +30,9 @@
 
     static AIBinder_Class* kClass;
 
+    // binder representing this interface with one reference count
+    AIBinder* getBinder();
+
     // Takes ownership of IFoo
     binder_status_t addService(const char* instance);
     static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index fd30d87..f84d9d3 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -19,10 +19,13 @@
 #include <aidl/BnEmpty.h>
 #include <android-base/logging.h>
 #include <android/binder_ibinder_jni.h>
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <gtest/gtest.h>
 #include <iface/iface.h>
+#include <utils/Looper.h>
 
 // warning: this is assuming that libbinder_ndk is using the same copy
 // of libbinder that we are.
@@ -34,14 +37,20 @@
 #include <sys/prctl.h>
 #include <chrono>
 #include <condition_variable>
+#include <iostream>
 #include <mutex>
 
 using namespace android;
 
 constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
 constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
 
 class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+    ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
+        *out = in;
+        return ndk::ScopedAStatus::ok();
+    }
     ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
         (void)empty;
         return ndk::ScopedAStatus::ok();
@@ -52,6 +61,12 @@
         android::IPCThreadState::self()->flushCommands();
         return ndk::ScopedAStatus::ok();
     }
+    ndk::ScopedAStatus getsRequestedSid(bool* out) {
+        const char* sid = AIBinder_getCallingSid();
+        std::cout << "Got security context: " << (sid ?: "null") << std::endl;
+        *out = sid != nullptr;
+        return ndk::ScopedAStatus::ok();
+    }
     binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args,
                                        uint32_t numArgs) override {
         for (uint32_t i = 0; i < numArgs; i++) {
@@ -66,11 +81,15 @@
     ABinderProcess_setThreadPoolMaxThreadCount(0);
 
     auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
-    binder_status_t status =
-            AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+    auto binder = service->asBinder();
 
-    if (status != STATUS_OK) {
-        LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+    AIBinder_setRequestingSid(binder.get(), true);
+
+    binder_exception_t exception =
+            AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
+
+    if (exception != EX_NONE) {
+        LOG(FATAL) << "Could not register: " << exception << " " << kBinderNdkUnitTestService;
     }
 
     ABinderProcess_joinThreadPool();
@@ -92,12 +111,53 @@
     }
 };
 
-int manualService(const char* instance) {
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-
+void manualService(const char* instance) {
     // Strong reference to MyFoo kept by service manager.
-    binder_status_t status = (new MyFoo)->addService(instance);
+    binder_exception_t exception = (new MyFoo)->addService(instance);
 
+    if (exception != EX_NONE) {
+        LOG(FATAL) << "Could not register: " << exception << " " << instance;
+    }
+}
+int manualPollingService(const char* instance) {
+    int fd;
+    CHECK(STATUS_OK == ABinderProcess_setupPolling(&fd));
+    manualService(instance);
+
+    class Handler : public LooperCallback {
+        int handleEvent(int /*fd*/, int /*events*/, void* /*data*/) override {
+            ABinderProcess_handlePolledCommands();
+            return 1;  // Continue receiving callbacks.
+        }
+    };
+
+    sp<Looper> looper = Looper::prepare(0 /* opts */);
+    looper->addFd(fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, new Handler(), nullptr /*data*/);
+    // normally, would add additional fds
+    while (true) {
+        looper->pollAll(-1 /* timeoutMillis */);
+    }
+    return 1;  // should not reach
+}
+int manualThreadPoolService(const char* instance) {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    manualService(instance);
+    ABinderProcess_joinThreadPool();
+    return 1;
+}
+
+int lazyService(const char* instance) {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    // Wait to register this service to make sure the main test process will
+    // actually wait for the service to be available. Tested with sleep(60),
+    // and reduced for sake of time.
+    sleep(1);
+    // Strong reference to MyBinderNdkUnitTest kept by service manager.
+    // This is just for testing, it has no corresponding init behavior.
+    auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+    auto binder = service->asBinder();
+
+    binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance);
     if (status != STATUS_OK) {
         LOG(FATAL) << "Could not register: " << status << " " << instance;
     }
@@ -107,11 +167,10 @@
     return 1;  // should not return
 }
 
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-//     sp<IFoo> foo = IFoo::getService("asdfghkl;");
-//     EXPECT_EQ(nullptr, foo.get());
-// }
+TEST(NdkBinder, GetServiceThatDoesntExist) {
+    sp<IFoo> foo = IFoo::getService("asdfghkl;");
+    EXPECT_EQ(nullptr, foo.get());
+}
 
 TEST(NdkBinder, CheckServiceThatDoesntExist) {
     AIBinder* binder = AServiceManager_checkService("asdfghkl;");
@@ -126,6 +185,26 @@
     AIBinder_decStrong(binder);
 }
 
+TEST(NdkBinder, UnimplementedDump) {
+    sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+    ASSERT_NE(foo, nullptr);
+    AIBinder* binder = foo->getBinder();
+    EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0));
+    AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, UnimplementedShell) {
+    // libbinder_ndk doesn't support calling shell, so we are calling from the
+    // libbinder across processes to the NDK service which doesn't implement
+    // shell
+    static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName));
+
+    Vector<String16> argsVec;
+    EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
+                                        argsVec, nullptr, nullptr));
+}
+
 TEST(NdkBinder, DoubleNumber) {
     sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
     ASSERT_NE(foo, nullptr);
@@ -135,6 +214,33 @@
     EXPECT_EQ(2, out);
 }
 
+TEST(NdkBinder, GetLazyService) {
+    // Not declared in the vintf manifest
+    ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
+    ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+    std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+            aidl::IBinderNdkUnitTest::fromBinder(binder);
+    ASSERT_NE(service, nullptr);
+
+    EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+// This is too slow
+TEST(NdkBinder, CheckLazyServiceShutDown) {
+    ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+    std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+            aidl::IBinderNdkUnitTest::fromBinder(binder);
+    ASSERT_NE(service, nullptr);
+
+    EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+    binder = nullptr;
+    service = nullptr;
+    IPCThreadState::self()->flushCommands();
+    // Make sure the service is dead after some time of no use
+    sleep(10);
+    ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService));
+}
+
 void LambdaOnDeath(void* cookie) {
     auto onDeath = static_cast<std::function<void(void)>*>(cookie);
     (*onDeath)();
@@ -218,11 +324,20 @@
     }
 };
 
+TEST(NdkBinder, AddNullService) {
+    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
+}
+
+TEST(NdkBinder, AddInvalidServiceName) {
+    sp<IFoo> foo = new MyTestFoo;
+    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&"));
+}
+
 TEST(NdkBinder, GetServiceInProcess) {
     static const char* kInstanceName = "test-get-service-in-process";
 
     sp<IFoo> foo = new MyTestFoo;
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName));
 
     sp<IFoo> getFoo = IFoo::getService(kInstanceName);
     EXPECT_EQ(foo.get(), getFoo.get());
@@ -269,11 +384,21 @@
     static const char* kInstanceName1 = "test-multi-1";
     static const char* kInstanceName2 = "test-multi-2";
     sp<IFoo> foo = new MyTestFoo;
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName1));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2));
     EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
 }
 
+TEST(NdkBinder, RequestedSidWorks) {
+    ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+    std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+            aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+    bool gotSid = false;
+    EXPECT_TRUE(service->getsRequestedSid(&gotSid).isOk());
+    EXPECT_TRUE(gotSid);
+}
+
 TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
     static volatile bool destroyed = false;
     static std::mutex dMutex;
@@ -308,6 +433,30 @@
     EXPECT_TRUE(destroyed);
 }
 
+TEST(NdkBinder, ConvertToPlatformBinder) {
+    for (const ndk::SpAIBinder& binder :
+         {// remote
+          ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+          // local
+          ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
+        // convert to platform binder
+        EXPECT_NE(binder.get(), nullptr);
+        sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get());
+        EXPECT_NE(platformBinder.get(), nullptr);
+        auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder);
+        EXPECT_NE(proxy, nullptr);
+
+        // use platform binder
+        int out;
+        EXPECT_TRUE(proxy->repeatInt(4, &out).isOk());
+        EXPECT_EQ(out, 4);
+
+        // convert back
+        ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder));
+        EXPECT_EQ(backBinder.get(), binder.get());
+    }
+}
+
 class MyResultReceiver : public BnResultReceiver {
    public:
     Mutex mMutex;
@@ -399,11 +548,15 @@
 
     if (fork() == 0) {
         prctl(PR_SET_PDEATHSIG, SIGHUP);
-        return manualService(IFoo::kInstanceNameToDieFor);
+        return manualThreadPoolService(IFoo::kInstanceNameToDieFor);
     }
     if (fork() == 0) {
         prctl(PR_SET_PDEATHSIG, SIGHUP);
-        return manualService(IFoo::kSomeInstanceName);
+        return manualPollingService(IFoo::kSomeInstanceName);
+    }
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+        return lazyService(kLazyBinderNdkUnitTestService);
     }
     if (fork() == 0) {
         prctl(PR_SET_PDEATHSIG, SIGHUP);
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
similarity index 67%
rename from libs/binder/fuzzer/Android.bp
rename to libs/binder/parcel_fuzzer/Android.bp
index d2b4d52..3e6fe99 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -12,10 +12,13 @@
         "binder_ndk.cpp",
         "hwbinder.cpp",
         "main.cpp",
+        "random_fd.cpp",
+        "random_parcel.cpp",
         "util.cpp",
     ],
     static_libs: [
         "libbase",
+        "libbinder_random_parcel",
         "libcgrouprc",
         "libcgrouprc_format",
         "libcutils",
@@ -45,3 +48,25 @@
     // produced, you may find uncommenting the below line very useful.
     // cflags: ["-DENABLE_LOG_FUZZ"],
 }
+
+cc_library_static {
+    name: "libbinder_random_parcel",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
+    srcs: [
+        "random_fd.cpp",
+        "random_parcel.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+    local_include_dirs: ["include_random_parcel"],
+    export_include_dirs: ["include_random_parcel"],
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
similarity index 94%
rename from libs/binder/fuzzer/binder.cpp
rename to libs/binder/parcel_fuzzer/binder.cpp
index 8c0495c..a94f06f 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -19,6 +19,8 @@
 #include "util.h"
 
 #include <android/os/IServiceManager.h>
+#include <binder/ParcelableHolder.h>
+#include <binder/PersistableBundle.h>
 
 using ::android::status_t;
 
@@ -128,7 +130,6 @@
     PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
     PARCEL_READ_OPT_STATUS(float, readFloat),
     PARCEL_READ_OPT_STATUS(double, readDouble),
-    PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr),
     PARCEL_READ_OPT_STATUS(bool, readBool),
     PARCEL_READ_OPT_STATUS(char16_t, readChar),
     PARCEL_READ_OPT_STATUS(int8_t, readByte),
@@ -271,5 +272,20 @@
     PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
     PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
     PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+
+    // additional parcelable objects defined in libbinder
+    [] (const ::android::Parcel& p, uint8_t data) {
+        using ::android::os::ParcelableHolder;
+        using ::android::Parcelable;
+        FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status";
+        Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL;
+        if ( (data & 1) == 1 ) {
+            stability = Parcelable::Stability::STABILITY_VINTF;
+        }
+        ParcelableHolder t = ParcelableHolder(stability);
+        status_t status = p.readParcelable(&t);
+        FUZZ_LOG() << "ParcelableHolder status: " << status;
+    },
+    PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
 };
 // clang-format on
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/binder.h
similarity index 98%
rename from libs/binder/fuzzer/binder.h
rename to libs/binder/parcel_fuzzer/binder.h
index b224ef4..0c51d68 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/binder.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #include <binder/Parcel.h>
 #include <vector>
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
similarity index 83%
rename from libs/binder/fuzzer/binder_ndk.cpp
rename to libs/binder/parcel_fuzzer/binder_ndk.cpp
index 29da8f7..008780c 100644
--- a/libs/binder/fuzzer/binder_ndk.cpp
+++ b/libs/binder/parcel_fuzzer/binder_ndk.cpp
@@ -18,6 +18,7 @@
 #include "binder_ndk.h"
 
 #include <android/binder_parcel_utils.h>
+#include <android/binder_parcelable_utils.h>
 
 #include "util.h"
 
@@ -54,6 +55,25 @@
             binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
             FUZZ_LOG() << "read status header: " << status;
         },
+        [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+            FUZZ_LOG() << "about to getDataSize the parcel";
+            AParcel_getDataSize(p.aParcel());
+            FUZZ_LOG() << "getDataSize done";
+        },
+        [](const NdkParcelAdapter& p, uint8_t data) {
+            FUZZ_LOG() << "about to read a ParcelableHolder";
+            ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
+            binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph);
+            FUZZ_LOG() << "read the ParcelableHolder: " << status;
+        },
+        [](const NdkParcelAdapter& p, uint8_t data) {
+            FUZZ_LOG() << "about to appendFrom";
+            AParcel* parcel = AParcel_create();
+            binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data);
+            AParcel_delete(parcel);
+            FUZZ_LOG() << "appendFrom: " << status;
+        },
+
         PARCEL_READ(int32_t, AParcel_readInt32),
         PARCEL_READ(uint32_t, AParcel_readUint32),
         PARCEL_READ(int64_t, AParcel_readInt64),
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/parcel_fuzzer/binder_ndk.h
similarity index 91%
rename from libs/binder/fuzzer/binder_ndk.h
rename to libs/binder/parcel_fuzzer/binder_ndk.h
index 622cafc..e69d9c1 100644
--- a/libs/binder/fuzzer/binder_ndk.h
+++ b/libs/binder/parcel_fuzzer/binder_ndk.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #include <android/binder_auto_utils.h>
 #include <vector>
@@ -31,6 +32,9 @@
     const AParcel* aParcel() const { return mParcel.get(); }
     AParcel* aParcel() { return mParcel.get(); }
 
+    android::Parcel* parcel() { return aParcel()->get(); }
+
+    const uint8_t* data() const { return aParcel()->get()->data(); }
     size_t dataSize() const { return aParcel()->get()->dataSize(); }
     size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
     size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/fuzzer/hwbinder.cpp
rename to libs/binder/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/parcel_fuzzer/hwbinder.h
similarity index 98%
rename from libs/binder/fuzzer/hwbinder.h
rename to libs/binder/parcel_fuzzer/hwbinder.h
index a6c66be..1fa56d4 100644
--- a/libs/binder/fuzzer/hwbinder.h
+++ b/libs/binder/parcel_fuzzer/hwbinder.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #include <hwbinder/Parcel.h>
 #include <vector>
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index f9bacc8..0a083d7 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/MockEventControlThread.h"
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+// ownership to callee, always valid or aborts
+// get a random FD for use in fuzzing, of a few different specific types
+int getRandomFd(FuzzedDataProvider* provider);
 
-} // namespace mock
 } // namespace android
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 72%
copy from libs/binder/fuzzer/binder.h
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index b224ef4..b92a6a9 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+#pragma once
+
 #include <binder/Parcel.h>
-#include <vector>
+#include <fuzzer/FuzzedDataProvider.h>
 
-#include "parcel_fuzzer.h"
-
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
new file mode 100644
index 0000000..386c70b
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <fuzzbinder/random_parcel.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using android::fillRandomParcel;
+
+void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
+    // TODO: functionality to create random parcels for libhwbinder parcels
+    std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
+    p->setData(input.data(), input.size());
+}
+static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) {
+    // fill underlying parcel using functions to fill random libbinder parcel
+    fillRandomParcel(p->parcel(), std::move(provider));
+}
+
+template <typename P>
+void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+            FuzzedDataProvider&& provider) {
+    // Allow some majority of the bytes to be dedicated to telling us what to
+    // do. The fixed value added here represents that we want to test doing a
+    // lot of 'instructions' even on really short parcels.
+    size_t maxInstructions = 20 + (provider.remaining_bytes() * 2 / 3);
+    // but don't always use that many instructions. We want to allow the fuzzer
+    // to explore large parcels with few instructions if it wants to.
+    std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
+            provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
+
+    P p;
+    fillRandomParcel(&p, std::move(provider));
+
+    // since we are only using a byte to index
+    CHECK(reads.size() <= 255) << reads.size();
+
+    FUZZ_LOG() << "backend: " << backend;
+    FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
+    FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+    for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
+        uint8_t a = instructions[i];
+        uint8_t readIdx = a % reads.size();
+
+        uint8_t b = instructions[i + 1];
+
+        FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+                   << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+                   << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+                   << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+                   << " cap: " << p.dataCapacity();
+
+        reads[readIdx](p, b);
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size <= 1) return 0;  // no use
+
+    // avoid timeouts, see b/142617274, b/142473153
+    if (size > 50000) return 0;
+
+    FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+    const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+            [](FuzzedDataProvider&& provider) {
+                doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+                                                    std::move(provider));
+            },
+            [](FuzzedDataProvider&& provider) {
+                doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+                                          std::move(provider));
+            },
+            [](FuzzedDataProvider&& provider) {
+                doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+                                         std::move(provider));
+            },
+    };
+
+    provider.PickValueInArray(fuzzBackend)(std::move(provider));
+
+    return 0;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
similarity index 97%
rename from libs/binder/fuzzer/parcel_fuzzer.h
rename to libs/binder/parcel_fuzzer/parcel_fuzzer.h
index 10cf17c..b68a8a9 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 template <typename P>
 using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/parcel_fuzzer/random_fd.cpp
new file mode 100644
index 0000000..cef6adb
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_fd.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/random_fd.h>
+
+#include <fcntl.h>
+
+#include <android-base/logging.h>
+#include <cutils/ashmem.h>
+
+namespace android {
+
+int getRandomFd(FuzzedDataProvider* provider) {
+    int fd = provider->PickValueInArray<std::function<int()>>({
+            []() { return ashmem_create_region("binder test region", 1024); },
+            []() { return open("/dev/null", O_RDWR); },
+    })();
+    CHECK(fd >= 0);
+    return fd;
+}
+
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
new file mode 100644
index 0000000..9ca4c8a
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_parcel.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <fuzzbinder/random_fd.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class NamedBinder : public BBinder {
+public:
+    NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
+    const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+    String16 mDescriptor;
+};
+
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+    while (provider.remaining_bytes() > 0) {
+        auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
+                // write data
+                [&]() {
+                    size_t toWrite =
+                            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+                    std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(toWrite);
+                    CHECK(OK == p->write(data.data(), data.size()));
+                },
+                // write FD
+                [&]() {
+                    int fd = getRandomFd(&provider);
+                    CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/));
+                },
+                // write binder
+                [&]() {
+                    auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
+                            [&]() {
+                                // descriptor is the length of a class name, e.g.
+                                // "some.package.Foo"
+                                std::string str =
+                                        provider.ConsumeRandomLengthString(100 /*max length*/);
+                                return new NamedBinder(String16(str.c_str()));
+                            },
+                            []() {
+                                // this is the easiest remote binder to get ahold of, and it
+                                // should be able to handle anything thrown at it, and
+                                // essentially every process can talk to it, so it's a good
+                                // candidate for checking usage of an actual BpBinder
+                                return IInterface::asBinder(defaultServiceManager());
+                            },
+                            []() { return nullptr; },
+                    });
+                    sp<IBinder> binder = makeFunc();
+                    CHECK(OK == p->writeStrongBinder(binder));
+                },
+        });
+
+        fillFunc();
+    }
+}
+
+} // namespace android
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/fuzzer/util.cpp
rename to libs/binder/parcel_fuzzer/util.cpp
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/parcel_fuzzer/util.h
similarity index 98%
rename from libs/binder/fuzzer/util.h
rename to libs/binder/parcel_fuzzer/util.h
index aa504d2..45e8c57 100644
--- a/libs/binder/fuzzer/util.h
+++ b/libs/binder/parcel_fuzzer/util.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #include <iostream>
 #include <sstream>
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
new file mode 100644
index 0000000..fd5f2f5
--- /dev/null
+++ b/libs/binder/rust/Android.bp
@@ -0,0 +1,96 @@
+rust_library {
+    name: "libbinder_rs",
+    crate_name: "binder",
+    srcs: ["src/lib.rs"],
+    shared_libs: [
+        "libutils",
+    ],
+    rustlibs: [
+        "liblibc",
+        "libbinder_ndk_sys",
+    ],
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    }
+}
+
+rust_library {
+    name: "libbinder_ndk_sys",
+    crate_name: "binder_ndk_sys",
+    srcs: [
+        "sys/lib.rs",
+        ":libbinder_ndk_bindgen",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    }
+}
+
+rust_bindgen {
+    name: "libbinder_ndk_bindgen",
+    crate_name: "binder_ndk_bindgen",
+    wrapper_src: "sys/BinderBindings.hpp",
+    source_stem: "bindings",
+    bindgen_flags: [
+        // Unfortunately the only way to specify the rust_non_exhaustive enum
+        // style for a type is to make it the default
+        "--default-enum-style", "rust_non_exhaustive",
+        // and then specify constified enums for the enums we don't want
+        // rustified
+        "--constified-enum", "android::c_interface::consts::.*",
+
+        "--whitelist-type", "android::c_interface::.*",
+        "--whitelist-type", "AStatus",
+        "--whitelist-type", "AIBinder_Class",
+        "--whitelist-type", "AIBinder",
+        "--whitelist-type", "AIBinder_Weak",
+        "--whitelist-type", "AIBinder_DeathRecipient",
+        "--whitelist-type", "AParcel",
+        "--whitelist-type", "binder_status_t",
+        "--whitelist-function", ".*",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    host_supported: true,
+
+    // Currently necessary for host builds
+    // TODO(b/31559095): bionic on host should define this
+    target: {
+        host: {
+            cflags: [
+                "-D__INTRODUCED_IN(n)=",
+                "-D__assert(a,b,c)=",
+                // We want all the APIs to be available on the host.
+                "-D__ANDROID_API__=10000",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+rust_test {
+    name: "libbinder_rs-internal_test",
+    crate_name: "binder",
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    rustlibs: [
+        "liblibc",
+        "libbinder_ndk_sys",
+    ],
+}
diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING
new file mode 100644
index 0000000..50c474c
--- /dev/null
+++ b/libs/binder/rust/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "libbinder_rs-internal_test"
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "rustBinderTest"
+    }
+  ]
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
new file mode 100644
index 0000000..037ee95
--- /dev/null
+++ b/libs/binder/rust/src/binder.rs
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Trait definitions for binder objects
+
+use crate::error::{status_t, Result};
+use crate::parcel::Parcel;
+use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::sys;
+
+use std::ffi::{c_void, CString};
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// Binder action to perform.
+///
+/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and
+/// [`IBinder::LAST_CALL_TRANSACTION`].
+pub type TransactionCode = u32;
+
+/// Additional operation flags.
+///
+/// `IBinder::FLAG_*` values.
+pub type TransactionFlags = u32;
+
+/// Super-trait for Binder interfaces.
+///
+/// This trait allows conversion of a Binder interface trait object into an
+/// IBinder object for IPC calls. All Binder remotable interface (i.e. AIDL
+/// interfaces) must implement this trait.
+///
+/// This is equivalent `IInterface` in C++.
+pub trait Interface {
+    /// Convert this binder object into a generic [`SpIBinder`] reference.
+    fn as_binder(&self) -> SpIBinder {
+        panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
+    }
+}
+
+/// A local service that can be remotable via Binder.
+///
+/// An object that implement this interface made be made into a Binder service
+/// via `Binder::new(object)`.
+///
+/// This is a low-level interface that should normally be automatically
+/// generated from AIDL via the [`declare_binder_interface!`] macro. When using
+/// the AIDL backend, users need only implement the high-level AIDL-defined
+/// interface. The AIDL compiler then generates a container struct that wraps
+/// the user-defined service and implements `Remotable`.
+pub trait Remotable: Send + Sync {
+    /// The Binder interface descriptor string.
+    ///
+    /// This string is a unique identifier for a Binder interface, and should be
+    /// the same between all implementations of that interface.
+    fn get_descriptor() -> &'static str;
+
+    /// Handle and reply to a request to invoke a transaction on this object.
+    ///
+    /// `reply` may be [`None`] if the sender does not expect a reply.
+    fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>;
+
+    /// Retrieve the class of this remote object.
+    ///
+    /// This method should always return the same InterfaceClass for the same
+    /// type.
+    fn get_class() -> InterfaceClass;
+}
+
+/// Interface of binder local or remote objects.
+///
+/// This trait corresponds to the interface of the C++ `IBinder` class.
+pub trait IBinder {
+    /// First transaction code available for user commands (inclusive)
+    const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
+    /// Last transaction code available for user commands (inclusive)
+    const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
+
+    /// Corresponds to TF_ONE_WAY -- an asynchronous call.
+    const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+    /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+    const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
+
+    /// 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.
+    fn set_requesting_sid(&mut self, enable: bool);
+
+    /// Dump this object to the given file handle
+    fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>;
+
+    /// Get a new interface that exposes additional extension functionality, if
+    /// available.
+    fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
+
+    /// Perform a generic operation with the object.
+    ///
+    /// # Arguments
+    /// * `code` - Transaction code for the operation
+    /// * `data` - [`Parcel`] with input data
+    /// * `reply` - Optional [`Parcel`] for reply data
+    /// * `flags` - Transaction flags, e.g. marking the transaction as
+    /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY))
+    fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+        &self,
+        code: TransactionCode,
+        flags: TransactionFlags,
+        input_callback: F,
+    ) -> Result<Parcel>;
+
+    /// Register the recipient for a notification if this binder
+    /// goes away. If this binder object unexpectedly goes away
+    /// (typically because its hosting process has been killed),
+    /// then DeathRecipient::binder_died() will be called with a reference
+    /// to this.
+    ///
+    /// You will only receive death notifications for remote binders,
+    /// as local binders by definition can't die without you dying as well.
+    /// Trying to use this function on a local binder will result in an
+    /// INVALID_OPERATION code being returned and nothing happening.
+    ///
+    /// This link always holds a weak reference to its recipient.
+    ///
+    /// You will only receive a weak reference to the dead
+    /// binder. You should not try to promote this to a strong reference.
+    /// (Nor should you need to, as there is nothing useful you can
+    /// directly do with it now that it has passed on.)
+    fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+    /// Remove a previously registered death notification.
+    /// The recipient will no longer be called if this object
+    /// dies.
+    fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+}
+
+/// Opaque reference to the type of a Binder interface.
+///
+/// This object encapsulates the Binder interface descriptor string, along with
+/// the binder transaction callback, if the class describes a local service.
+///
+/// A Binder remotable object may only have a single interface class, and any
+/// given object can only be associated with one class. Two objects with
+/// different classes are incompatible, even if both classes have the same
+/// interface descriptor.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct InterfaceClass(*const sys::AIBinder_Class);
+
+impl InterfaceClass {
+    /// Get a Binder NDK `AIBinder_Class` pointer for this object type.
+    ///
+    /// Note: the returned pointer will not be constant. Calling this method
+    /// multiple times for the same type will result in distinct class
+    /// pointers. A static getter for this value is implemented in
+    /// [`declare_binder_interface!`].
+    pub fn new<I: InterfaceClassMethods>() -> InterfaceClass {
+        let descriptor = CString::new(I::get_descriptor()).unwrap();
+        let ptr = unsafe {
+            // Safety: `AIBinder_Class_define` expects a valid C string, and
+            // three valid callback functions, all non-null pointers. The C
+            // string is copied and need not be valid for longer than the call,
+            // so we can drop it after the call. We can safely assign null to
+            // the onDump and handleShellCommand callbacks as long as the class
+            // pointer was non-null. Rust None for a Option<fn> is guaranteed to
+            // be a NULL pointer. Rust retains ownership of the pointer after it
+            // is defined.
+            let class = sys::AIBinder_Class_define(
+                descriptor.as_ptr(),
+                Some(I::on_create),
+                Some(I::on_destroy),
+                Some(I::on_transact),
+            );
+            if class.is_null() {
+                panic!("Expected non-null class pointer from AIBinder_Class_define!");
+            }
+            sys::AIBinder_Class_setOnDump(class, None);
+            sys::AIBinder_Class_setHandleShellCommand(class, None);
+            class
+        };
+        InterfaceClass(ptr)
+    }
+
+    /// Construct an `InterfaceClass` out of a raw, non-null `AIBinder_Class`
+    /// pointer.
+    ///
+    /// # Safety
+    ///
+    /// This function is safe iff `ptr` is a valid, non-null pointer to an
+    /// `AIBinder_Class`.
+    pub(crate) unsafe fn from_ptr(ptr: *const sys::AIBinder_Class) -> InterfaceClass {
+        InterfaceClass(ptr)
+    }
+}
+
+impl From<InterfaceClass> for *const sys::AIBinder_Class {
+    fn from(class: InterfaceClass) -> *const sys::AIBinder_Class {
+        class.0
+    }
+}
+
+/// Create a function implementing a static getter for an interface class.
+///
+/// Each binder interface (i.e. local [`Remotable`] service or remote proxy
+/// [`Interface`]) must have global, static class that uniquely identifies
+/// it. This macro implements an [`InterfaceClass`] getter to simplify these
+/// implementations.
+///
+/// The type of a structure that implements [`InterfaceClassMethods`] must be
+/// passed to this macro. For local services, this should be `Binder<Self>`
+/// since [`Binder`] implements [`InterfaceClassMethods`].
+///
+/// # Examples
+///
+/// When implementing a local [`Remotable`] service `ExampleService`, the
+/// `get_class` method is required in the [`Remotable`] impl block. This macro
+/// should be used as follows to implement this functionality:
+///
+/// ```rust
+/// impl Remotable for ExampleService {
+///     fn get_descriptor() -> &'static str {
+///         "android.os.IExampleInterface"
+///     }
+///
+///     fn on_transact(
+///         &self,
+///         code: TransactionCode,
+///         data: &Parcel,
+///         reply: &mut Parcel,
+///     ) -> Result<()> {
+///         // ...
+///     }
+///
+///     binder_fn_get_class!(Binder<Self>);
+/// }
+/// ```
+macro_rules! binder_fn_get_class {
+    ($class:ty) => {
+        binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+    };
+
+    ($constructor:expr) => {
+        fn get_class() -> $crate::InterfaceClass {
+            static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+            static mut CLASS: Option<$crate::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($constructor);
+            });
+            unsafe {
+                // Safety: The `CLASS` variable can only be mutated once, above,
+                // and is subsequently safe to read from any thread.
+                CLASS.unwrap()
+            }
+        }
+    };
+}
+
+pub trait InterfaceClassMethods {
+    /// Get the interface descriptor string for this object type.
+    fn get_descriptor() -> &'static str
+    where
+        Self: Sized;
+
+    /// Called during construction of a new `AIBinder` object of this interface
+    /// class.
+    ///
+    /// The opaque pointer parameter will be the parameter provided to
+    /// `AIBinder_new`. Returns an opaque userdata to be associated with the new
+    /// `AIBinder` object.
+    ///
+    /// # Safety
+    ///
+    /// Callback called from C++. The parameter argument provided to
+    /// `AIBinder_new` must match the type expected here. The `AIBinder` object
+    /// will take ownership of the returned pointer, which it will free via
+    /// `on_destroy`.
+    unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void;
+
+    /// Called when a transaction needs to be processed by the local service
+    /// implementation.
+    ///
+    /// # Safety
+    ///
+    /// Callback called from C++. The `binder` parameter must be a valid pointer
+    /// to a binder object of this class with userdata initialized via this
+    /// class's `on_create`. The parcel parameters must be valid pointers to
+    /// parcel objects.
+    unsafe extern "C" fn on_transact(
+        binder: *mut sys::AIBinder,
+        code: u32,
+        data: *const sys::AParcel,
+        reply: *mut sys::AParcel,
+    ) -> status_t;
+
+    /// Called whenever an `AIBinder` object is no longer referenced and needs
+    /// to be destroyed.
+    ///
+    /// # Safety
+    ///
+    /// Callback called from C++. The opaque pointer parameter must be the value
+    /// returned by `on_create` for this class. This function takes ownership of
+    /// the provided pointer and destroys it.
+    unsafe extern "C" fn on_destroy(object: *mut c_void);
+}
+
+/// Interface for transforming a generic SpIBinder into a specific remote
+/// interface trait.
+///
+/// # Example
+///
+/// For Binder interface `IFoo`, the following implementation should be made:
+/// ```no_run
+/// # use binder::{FromIBinder, SpIBinder, Result};
+/// # trait IFoo {}
+/// impl FromIBinder for dyn IFoo {
+///     fn try_from(ibinder: SpIBinder) -> Result<Box<Self>> {
+///         // ...
+///         # Err(binder::StatusCode::OK)
+///     }
+/// }
+/// ```
+pub trait FromIBinder {
+    /// Try to interpret a generic Binder object as this interface.
+    ///
+    /// Returns a trait object for the `Self` interface if this object
+    /// implements that interface.
+    fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+}
+
+/// Trait for transparent Rust wrappers around android C++ native types.
+///
+/// The pointer return by this trait's methods should be immediately passed to
+/// C++ and not stored by Rust. The pointer is valid only as long as the
+/// underlying C++ object is alive, so users must be careful to take this into
+/// account, as Rust cannot enforce this.
+///
+/// # Safety
+///
+/// For this trait to be a correct implementation, `T` must be a valid android
+/// C++ type. Since we cannot constrain this via the type system, this trait is
+/// marked as unsafe.
+pub unsafe trait AsNative<T> {
+    /// Return a pointer to the native version of `self`
+    fn as_native(&self) -> *const T;
+
+    /// Return a mutable pointer to the native version of `self`
+    fn as_native_mut(&mut self) -> *mut T;
+}
+
+unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
+    fn as_native(&self) -> *const T {
+        self.as_ref().map_or(ptr::null(), |v| v.as_native())
+    }
+
+    fn as_native_mut(&mut self) -> *mut T {
+        self.as_mut().map_or(ptr::null_mut(), |v| v.as_native_mut())
+    }
+}
+
+/// Declare typed interfaces for a binder object.
+///
+/// Given an interface trait and descriptor string, create a native and remote
+/// proxy wrapper for this interface. The native service object (`$native`)
+/// implements `Remotable` and will dispatch to the function `$on_transact` to
+/// handle transactions. The typed proxy object (`$proxy`) wraps remote binder
+/// objects for this interface and can optionally contain additional fields.
+///
+/// Assuming the interface trait is `Interface`, `$on_transact` function must
+/// have the following type:
+///
+/// ```
+/// # use binder::{Interface, TransactionCode, Parcel};
+/// # trait Placeholder {
+/// fn on_transact(
+///     service: &dyn Interface,
+///     code: TransactionCode,
+///     data: &Parcel,
+///     reply: &mut Parcel,
+/// ) -> binder::Result<()>;
+/// # }
+/// ```
+///
+/// # Examples
+///
+/// The following example declares the local service type `BnServiceManager` and
+/// a remote proxy type `BpServiceManager` (the `n` and `p` stand for native and
+/// proxy respectively) for the `IServiceManager` Binder interface. The
+/// interfaces will be identified by the descriptor string
+/// "android.os.IServiceManager". The local service will dispatch transactions
+/// using the provided function, `on_transact`.
+///
+/// ```
+/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel};
+///
+/// pub trait IServiceManager: Interface {
+///     // remote methods...
+/// }
+///
+/// declare_binder_interface! {
+///     IServiceManager["android.os.IServiceManager"] {
+///         native: BnServiceManager(on_transact),
+///         proxy: BpServiceManager,
+///     }
+/// }
+///
+/// fn on_transact(
+///     service: &dyn IServiceManager,
+///     code: TransactionCode,
+///     data: &Parcel,
+///     reply: &mut Parcel,
+/// ) -> binder::Result<()> {
+///     // ...
+///     Ok(())
+/// }
+///
+/// impl IServiceManager for BpServiceManager {
+///     // parceling/unparceling code for the IServiceManager emitted here
+/// }
+///
+/// impl IServiceManager for Binder<BnServiceManager> {
+///     // Forward calls to local implementation
+/// }
+/// ```
+#[macro_export]
+macro_rules! declare_binder_interface {
+    {
+        $interface:path[$descriptor:expr] {
+            native: $native:ident($on_transact:path),
+            proxy: $proxy:ident,
+        }
+    } => {
+        $crate::declare_binder_interface! {
+            $interface[$descriptor] {
+                native: $native($on_transact),
+                proxy: $proxy {},
+            }
+        }
+    };
+
+    {
+        $interface:path[$descriptor:expr] {
+            native: $native:ident($on_transact:path),
+            proxy: $proxy:ident {
+                $($fname:ident: $fty:ty = $finit:expr),*
+            },
+        }
+    } => {
+        $crate::declare_binder_interface! {
+            $interface[$descriptor] {
+                @doc[concat!("A binder [`Remotable`]($crate::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.")]
+                proxy: $proxy {
+                    $($fname: $fty = $finit),*
+                },
+            }
+        }
+    };
+
+    {
+        $interface:path[$descriptor:expr] {
+            @doc[$native_doc:expr]
+            native: $native:ident($on_transact:path),
+
+            @doc[$proxy_doc:expr]
+            proxy: $proxy:ident {
+                $($fname:ident: $fty:ty = $finit:expr),*
+            },
+        }
+    } => {
+        #[doc = $proxy_doc]
+        pub struct $proxy {
+            binder: $crate::SpIBinder,
+            $($fname: $fty,)*
+        }
+
+        impl $crate::Interface for $proxy {
+            fn as_binder(&self) -> $crate::SpIBinder {
+                self.binder.clone()
+            }
+        }
+
+        impl $crate::Proxy for $proxy
+        where
+            $proxy: $interface,
+        {
+            fn get_descriptor() -> &'static str {
+                $descriptor
+            }
+
+            fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+                use $crate::AssociateClass;
+                if binder.associate_class(<$native as $crate::Remotable>::get_class()) {
+                    Ok(Self { binder, $($fname: $finit),* })
+                } else {
+                    Err($crate::StatusCode::BAD_TYPE)
+                }
+            }
+        }
+
+        #[doc = $native_doc]
+        #[repr(transparent)]
+        pub struct $native(Box<dyn $interface + Sync + Send + 'static>);
+
+        impl $native {
+            /// Create a new binder service.
+            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
+                $crate::Binder::new($native(Box::new(inner)))
+            }
+        }
+
+        impl $crate::Remotable for $native {
+            fn get_descriptor() -> &'static str {
+                $descriptor
+            }
+
+            fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
+                match $on_transact(&*self.0, code, data, reply) {
+                    // The C++ backend converts UNEXPECTED_NULL into an exception
+                    Err($crate::StatusCode::UNEXPECTED_NULL) => {
+                        let status = $crate::Status::new_exception(
+                            $crate::ExceptionCode::NULL_POINTER,
+                            None,
+                        );
+                        reply.write(&status)
+                    },
+                    result => result
+                }
+            }
+
+            fn get_class() -> $crate::InterfaceClass {
+                static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+                static mut CLASS: Option<$crate::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>>());
+                });
+                unsafe {
+                    // Safety: The `CLASS` variable can only be mutated once, above,
+                    // and is subsequently safe to read from any thread.
+                    CLASS.unwrap()
+                }
+            }
+        }
+
+        impl $crate::FromIBinder for dyn $interface {
+            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+                use $crate::AssociateClass;
+                if !ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+                    return Err($crate::StatusCode::BAD_TYPE.into());
+                }
+
+                let service: $crate::Result<$crate::Binder<$native>> = std::convert::TryFrom::try_from(ibinder.clone());
+                if let Ok(service) = service {
+                    Ok(Box::new(service))
+                } else {
+                    Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))
+                }
+            }
+        }
+
+        impl $crate::parcel::Serialize for dyn $interface + '_
+        where
+            $interface: $crate::Interface
+        {
+            fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+                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::Parcel) -> $crate::Result<()> {
+                parcel.write(&this.map($crate::Interface::as_binder))
+            }
+        }
+
+        impl std::fmt::Debug for dyn $interface {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                f.pad(stringify!($interface))
+            }
+        }
+
+        // Convert a &dyn $interface to Box<dyn $interface>
+        impl std::borrow::ToOwned for dyn $interface {
+            type Owned = Box<dyn $interface>;
+            fn to_owned(&self) -> Self::Owned {
+                self.as_binder().into_interface()
+                    .expect(concat!("Error cloning interface ", stringify!($interface)))
+            }
+        }
+    };
+}
+
+/// Declare an AIDL enumeration.
+///
+/// This is mainly used internally by the AIDL compiler.
+#[macro_export]
+macro_rules! declare_binder_enum {
+    {
+        $enum:ident : $backing:ty {
+            $( $name:ident = $value:expr, )*
+        }
+    } => {
+        #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+        pub struct $enum(pub $backing);
+        impl $enum {
+            $( pub const $name: Self = Self($value); )*
+        }
+
+        impl $crate::parcel::Serialize for $enum {
+            fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+                parcel.write(&self.0)
+            }
+        }
+
+        impl $crate::parcel::SerializeArray for $enum {
+            fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+                let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
+                <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+            }
+        }
+
+        impl $crate::parcel::Deserialize for $enum {
+            fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> {
+                parcel.read().map(Self)
+            }
+        }
+
+        impl $crate::parcel::DeserializeArray for $enum {
+            fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> {
+                let v: Option<Vec<$backing>> =
+                    <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+                Ok(v.map(|v| v.into_iter().map(Self).collect()))
+            }
+        }
+    };
+}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
new file mode 100644
index 0000000..4492cf7
--- /dev/null
+++ b/libs/binder/rust/src/error.rs
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::AsNative;
+use crate::sys;
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+use std::result;
+
+pub use sys::binder_status_t as status_t;
+
+/// Low-level status codes from Android `libutils`.
+// All error codes are negative integer values. Derived from the anonymous enum
+// in utils/Errors.h
+pub use sys::android_c_interface_StatusCode as StatusCode;
+
+/// A specialized [`Result`](result::Result) for binder operations.
+pub type Result<T> = result::Result<T, StatusCode>;
+
+/// Convert a low-level status code into an empty result.
+///
+/// An OK status is converted into an `Ok` result, any other status is converted
+/// into an `Err` result holding the status code.
+pub fn status_result(status: status_t) -> Result<()> {
+    match parse_status_code(status) {
+        StatusCode::OK => Ok(()),
+        e => Err(e),
+    }
+}
+
+fn parse_status_code(code: i32) -> StatusCode {
+    match code {
+        e if e == StatusCode::OK as i32 => StatusCode::OK,
+        e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
+        e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
+        e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
+        e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
+        e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
+        e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
+        e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
+        e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
+        e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
+        e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
+        e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
+        e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
+        e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
+        e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
+        e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
+        e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
+        e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
+        _ => StatusCode::UNKNOWN_ERROR,
+    }
+}
+
+pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
+
+fn parse_exception_code(code: i32) -> ExceptionCode {
+    match code {
+        e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
+        e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
+        e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
+        e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
+        e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
+        e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
+        e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
+            ExceptionCode::NETWORK_MAIN_THREAD
+        }
+        e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
+            ExceptionCode::UNSUPPORTED_OPERATION
+        }
+        e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
+        _ => ExceptionCode::TRANSACTION_FAILED,
+    }
+}
+
+// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
+// lifetime of the contained pointer is the same as the `Status` object.
+/// High-level binder status object that encapsulates a standard way to keep
+/// track of and chain binder errors along with service specific errors.
+///
+/// Used in AIDL transactions to represent failed transactions.
+pub struct Status(*mut sys::AStatus);
+
+impl Status {
+    /// Create a status object representing a successful transaction.
+    pub fn ok() -> Self {
+        let ptr = unsafe {
+            // Safety: `AStatus_newOk` always returns a new, heap allocated
+            // pointer to an `ASTatus` object, so we know this pointer will be
+            // valid.
+            //
+            // Rust takes ownership of the returned pointer.
+            sys::AStatus_newOk()
+        };
+        Self(ptr)
+    }
+
+    /// Create a status object from a service specific error
+    pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
+        let ptr = if let Some(message) = message {
+            unsafe {
+                // Safety: Any i32 is a valid service specific error for the
+                // error code parameter. We construct a valid, null-terminated
+                // `CString` from the message, which must be a valid C-style
+                // string to pass as the message. This function always returns a
+                // new, heap allocated pointer to an `AStatus` object, so we
+                // know the returned pointer will be valid.
+                //
+                // Rust takes ownership of the returned pointer.
+                sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
+            }
+        } else {
+            unsafe {
+                // Safety: Any i32 is a valid service specific error for the
+                // error code parameter. This function always returns a new,
+                // heap allocated pointer to an `AStatus` object, so we know the
+                // returned pointer will be valid.
+                //
+                // Rust takes ownership of the returned pointer.
+                sys::AStatus_fromServiceSpecificError(err)
+            }
+        };
+        Self(ptr)
+    }
+
+    /// Create a status object from an exception code
+    pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
+        if let Some(message) = message {
+            let ptr = unsafe {
+                sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
+            };
+            Self(ptr)
+        } else {
+            exception.into()
+        }
+    }
+
+    /// Create a status object from a raw `AStatus` pointer.
+    ///
+    /// # Safety
+    ///
+    /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
+    pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
+        Self(ptr)
+    }
+
+    /// Returns `true` if this status represents a successful transaction.
+    pub fn is_ok(&self) -> bool {
+        unsafe {
+            // Safety: `Status` always contains a valid `AStatus` pointer, so we
+            // are always passing a valid pointer to `AStatus_isOk` here.
+            sys::AStatus_isOk(self.as_native())
+        }
+    }
+
+    /// Returns a description of the status.
+    pub fn get_description(&self) -> String {
+        let description_ptr = unsafe {
+            // Safety: `Status` always contains a valid `AStatus` pointer, so we
+            // are always passing a valid pointer to `AStatus_getDescription`
+            // here.
+            //
+            // `AStatus_getDescription` always returns a valid pointer to a null
+            // terminated C string. Rust is responsible for freeing this pointer
+            // via `AStatus_deleteDescription`.
+            sys::AStatus_getDescription(self.as_native())
+        };
+        let description = unsafe {
+            // Safety: `AStatus_getDescription` always returns a valid C string,
+            // which can be safely converted to a `CStr`.
+            CStr::from_ptr(description_ptr)
+        };
+        let description = description.to_string_lossy().to_string();
+        unsafe {
+            // Safety: `description_ptr` was returned from
+            // `AStatus_getDescription` above, and must be freed via
+            // `AStatus_deleteDescription`. We must not access the pointer after
+            // this call, so we copy it into an owned string above and return
+            // that string.
+            sys::AStatus_deleteDescription(description_ptr);
+        }
+        description
+    }
+
+    /// Returns the exception code of the status.
+    pub fn exception_code(&self) -> ExceptionCode {
+        let code = unsafe {
+            // Safety: `Status` always contains a valid `AStatus` pointer, so we
+            // are always passing a valid pointer to `AStatus_getExceptionCode`
+            // here.
+            sys::AStatus_getExceptionCode(self.as_native())
+        };
+        parse_exception_code(code)
+    }
+
+    /// Return a status code representing a transaction failure, or
+    /// `StatusCode::OK` if there was no transaction failure.
+    ///
+    /// If this method returns `OK`, the status may still represent a different
+    /// exception or a service specific error. To find out if this transaction
+    /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
+    pub fn transaction_error(&self) -> StatusCode {
+        let code = unsafe {
+            // Safety: `Status` always contains a valid `AStatus` pointer, so we
+            // are always passing a valid pointer to `AStatus_getStatus` here.
+            sys::AStatus_getStatus(self.as_native())
+        };
+        parse_status_code(code)
+    }
+
+    /// Return a service specific error if this status represents one.
+    ///
+    /// This function will only ever return a non-zero result if
+    /// [`exception_code`](Self::exception_code) returns
+    /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
+    /// status object may still represent a different exception or status. To
+    /// find out if this transaction as a whole is okay, use
+    /// [`is_ok`](Self::is_ok) instead.
+    pub fn service_specific_error(&self) -> i32 {
+        unsafe {
+            // Safety: `Status` always contains a valid `AStatus` pointer, so we
+            // are always passing a valid pointer to
+            // `AStatus_getServiceSpecificError` here.
+            sys::AStatus_getServiceSpecificError(self.as_native())
+        }
+    }
+
+    /// Calls `op` if the status was ok, otherwise returns an `Err` value of
+    /// `self`.
+    pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
+    where
+        F: FnOnce() -> result::Result<T, Status>,
+    {
+        <result::Result<(), Status>>::from(self)?;
+        op()
+    }
+}
+
+impl error::Error for Status {}
+
+impl Display for Status {
+    fn fmt(&self, f: &mut Formatter) -> FmtResult {
+        f.write_str(&self.get_description())
+    }
+}
+
+impl Debug for Status {
+    fn fmt(&self, f: &mut Formatter) -> FmtResult {
+        f.write_str(&self.get_description())
+    }
+}
+
+impl PartialEq for Status {
+    fn eq(&self, other: &Status) -> bool {
+        let self_code = self.exception_code();
+        let other_code = other.exception_code();
+
+        match (self_code, other_code) {
+            (ExceptionCode::NONE, ExceptionCode::NONE) => true,
+            (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
+                self.transaction_error() == other.transaction_error()
+                    && self.get_description() == other.get_description()
+            }
+            (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
+                self.service_specific_error() == other.service_specific_error()
+                    && self.get_description() == other.get_description()
+            }
+            (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
+        }
+    }
+}
+
+impl Eq for Status {}
+
+impl From<StatusCode> for Status {
+    fn from(status: StatusCode) -> Status {
+        (status as status_t).into()
+    }
+}
+
+impl From<status_t> for Status {
+    fn from(status: status_t) -> Status {
+        let ptr = unsafe {
+            // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
+            // this is a safe FFI call. Unknown values will be coerced into
+            // UNKNOWN_ERROR.
+            sys::AStatus_fromStatus(status)
+        };
+        Self(ptr)
+    }
+}
+
+impl From<ExceptionCode> for Status {
+    fn from(code: ExceptionCode) -> Status {
+        let ptr = unsafe {
+            // Safety: `AStatus_fromExceptionCode` expects any
+            // `binder_exception_t` (i32) integer, so this is a safe FFI call.
+            // Unknown values will be coerced into EX_TRANSACTION_FAILED.
+            sys::AStatus_fromExceptionCode(code as i32)
+        };
+        Self(ptr)
+    }
+}
+
+// TODO: impl Try for Status when try_trait is stabilized
+// https://github.com/rust-lang/rust/issues/42327
+impl From<Status> for result::Result<(), Status> {
+    fn from(status: Status) -> result::Result<(), Status> {
+        if status.is_ok() {
+            Ok(())
+        } else {
+            Err(status)
+        }
+    }
+}
+
+impl From<Status> for status_t {
+    fn from(status: Status) -> status_t {
+        status.transaction_error() as status_t
+    }
+}
+
+impl Drop for Status {
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: `Status` manages the lifetime of its inner `AStatus`
+            // pointee, so we need to delete it here. We know that the pointer
+            // will be valid here since `Status` always contains a valid pointer
+            // while it is alive.
+            sys::AStatus_delete(self.0);
+        }
+    }
+}
+
+/// # Safety
+///
+/// `Status` always contains a valid pointer to an `AStatus` object, so we can
+/// trivially convert it to a correctly-typed raw pointer.
+///
+/// Care must be taken that the returned pointer is only dereferenced while the
+/// `Status` object is still alive.
+unsafe impl AsNative<sys::AStatus> for Status {
+    fn as_native(&self) -> *const sys::AStatus {
+        self.0
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AStatus {
+        self.0
+    }
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
new file mode 100644
index 0000000..8ee6a62
--- /dev/null
+++ b/libs/binder/rust/src/lib.rs
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Safe Rust interface to Android `libbinder`.
+//!
+//! This crate is primarily designed as an target for a Rust AIDL compiler
+//! backend, and should generally not be used directly by users. It is built on
+//! top of the binder NDK library to be usable by APEX modules, and therefore
+//! only exposes functionality available in the NDK interface.
+//!
+//! # Example
+//!
+//! The following example illustrates how the AIDL backend will use this crate.
+//!
+//! ```
+//! use binder::{
+//!     declare_binder_interface, Binder, IBinder, Interface, Remotable, Parcel, SpIBinder,
+//!     StatusCode, TransactionCode,
+//! };
+//!
+//! // Generated by AIDL compiler
+//! pub trait ITest: Interface {
+//!     fn test(&self) -> binder::Result<String>;
+//! }
+//!
+//! // Creates a new local (native) service object, BnTest, and a remote proxy
+//! // object, BpTest, that are the typed interfaces for their respective ends
+//! // of the binder transaction. Generated by AIDL compiler.
+//! declare_binder_interface! {
+//!     ITest["android.os.ITest"] {
+//!         native: BnTest(on_transact),
+//!         proxy: BpTest,
+//!     }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! fn on_transact(
+//!     service: &dyn ITest,
+//!     code: TransactionCode,
+//!     _data: &Parcel,
+//!     reply: &mut Parcel,
+//! ) -> binder::Result<()> {
+//!     match code {
+//!         SpIBinder::FIRST_CALL_TRANSACTION => {
+//!             reply.write(&service.test()?)?;
+//!             Ok(())
+//!         }
+//!         _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+//!     }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for Binder<BnTest> {
+//!     fn test(&self) -> binder::Result<String> {
+//!         self.0.test()
+//!     }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for BpTest {
+//!     fn test(&self) -> binder::Result<String> {
+//!        let reply = self
+//!            .as_binder()
+//!            .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+//!        reply.read()
+//!     }
+//! }
+//!
+//! // User implemented:
+//!
+//! // Local implementation of the ITest remotable interface.
+//! struct TestService;
+//!
+//! impl Interface for TestService {}
+//!
+//! impl ITest for TestService {
+//!     fn test(&self) -> binder::Result<String> {
+//!        Ok("testing service".to_string())
+//!     }
+//! }
+//! ```
+
+#[macro_use]
+mod proxy;
+
+#[macro_use]
+mod binder;
+mod error;
+mod native;
+mod state;
+
+use binder_ndk_sys as sys;
+
+pub mod parcel;
+
+pub use crate::binder::{
+    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+};
+pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
+pub use native::add_service;
+pub use native::Binder;
+pub use parcel::Parcel;
+pub use proxy::{get_interface, get_service};
+pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use state::{ProcessState, ThreadState};
+
+/// The public API usable outside AIDL-generated interface crates.
+pub mod public_api {
+    pub use super::parcel::ParcelFileDescriptor;
+    pub use super::{add_service, get_interface};
+    pub use super::{
+        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode,
+    };
+
+    /// Binder result containing a [`Status`] on error.
+    pub type Result<T> = std::result::Result<T, Status>;
+}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
new file mode 100644
index 0000000..185645e
--- /dev/null
+++ b/libs/binder/rust/src/native.rs
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
+use crate::error::{status_result, status_t, Result, StatusCode};
+use crate::parcel::{Parcel, Serialize};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryFrom;
+use std::ffi::{c_void, CString};
+use std::mem::ManuallyDrop;
+use std::ops::Deref;
+use std::ptr;
+
+/// Rust wrapper around Binder remotable objects.
+///
+/// Implements the C++ `BBinder` class, and therefore implements the C++
+/// `IBinder` interface.
+#[repr(C)]
+pub struct Binder<T: Remotable> {
+    ibinder: *mut sys::AIBinder,
+    rust_object: *mut T,
+}
+
+/// # Safety
+///
+/// A `Binder<T>` is a pair of unique owning pointers to two values:
+///   * a C++ ABBinder which the C++ API guarantees can be passed between threads
+///   * a Rust object which implements `Remotable`; this trait requires `Send + Sync`
+///
+/// Both pointers are unique (never escape the `Binder<T>` object and are not copied)
+/// so we can essentially treat `Binder<T>` as a box-like containing the two objects;
+/// the box-like object inherits `Send` from the two inner values, similarly
+/// to how `Box<T>` is `Send` if `T` is `Send`.
+unsafe impl<T: Remotable> Send for Binder<T> {}
+
+impl<T: Remotable> Binder<T> {
+    /// Create a new Binder remotable object.
+    ///
+    /// This moves the `rust_object` into an owned [`Box`] and Binder will
+    /// manage its lifetime.
+    pub fn new(rust_object: T) -> Binder<T> {
+        let class = T::get_class();
+        let rust_object = Box::into_raw(Box::new(rust_object));
+        let ibinder = unsafe {
+            // Safety: `AIBinder_new` expects a valid class pointer (which we
+            // initialize via `get_class`), and an arbitrary pointer
+            // argument. The caller owns the returned `AIBinder` pointer, which
+            // is a strong reference to a `BBinder`. This reference should be
+            // decremented via `AIBinder_decStrong` when the reference lifetime
+            // ends.
+            sys::AIBinder_new(class.into(), rust_object as *mut c_void)
+        };
+        Binder {
+            ibinder,
+            rust_object,
+        }
+    }
+
+    /// Set the extension of a binder interface. This allows a downstream
+    /// developer to add an extension to an interface without modifying its
+    /// interface file. This should be called immediately when the object is
+    /// created before it is passed to another thread.
+    ///
+    /// # Examples
+    ///
+    /// For instance, imagine if we have this Binder AIDL interface definition:
+    ///     interface IFoo { void doFoo(); }
+    ///
+    /// If an unrelated owner (perhaps in a downstream codebase) wants to make a
+    /// change to the interface, they have two options:
+    ///
+    /// 1) Historical option that has proven to be BAD! Only the original
+    ///    author of an interface should change an interface. If someone
+    ///    downstream wants additional functionality, they should not ever
+    ///    change the interface or use this method.
+    ///    ```AIDL
+    ///    BAD TO DO:  interface IFoo {                       BAD TO DO
+    ///    BAD TO DO:      void doFoo();                      BAD TO DO
+    ///    BAD TO DO: +    void doBar(); // adding a method   BAD TO DO
+    ///    BAD TO DO:  }                                      BAD TO DO
+    ///    ```
+    ///
+    /// 2) Option that this method enables!
+    ///    Leave the original interface unchanged (do not change IFoo!).
+    ///    Instead, create a new AIDL interface in a downstream package:
+    ///    ```AIDL
+    ///    package com.<name>; // new functionality in a new package
+    ///    interface IBar { void doBar(); }
+    ///    ```
+    ///
+    ///    When registering the interface, add:
+    ///
+    ///        # use binder::{Binder, Interface};
+    ///        # type MyFoo = ();
+    ///        # type MyBar = ();
+    ///        # let my_foo = ();
+    ///        # let my_bar = ();
+    ///        let mut foo: Binder<MyFoo> = Binder::new(my_foo); // class in AOSP codebase
+    ///        let bar: Binder<MyBar> = Binder::new(my_bar);     // custom extension class
+    ///        foo.set_extension(&mut bar.as_binder());          // use method in Binder
+    ///
+    ///    Then, clients of `IFoo` can get this extension:
+    ///
+    ///        # use binder::{declare_binder_interface, Binder, TransactionCode, Parcel};
+    ///        # trait IBar {}
+    ///        # declare_binder_interface! {
+    ///        #     IBar["test"] {
+    ///        #         native: BnBar(on_transact),
+    ///        #         proxy: BpBar,
+    ///        #     }
+    ///        # }
+    ///        # fn on_transact(
+    ///        #     service: &dyn IBar,
+    ///        #     code: TransactionCode,
+    ///        #     data: &Parcel,
+    ///        #     reply: &mut Parcel,
+    ///        # ) -> binder::Result<()> {
+    ///        #     Ok(())
+    ///        # }
+    ///        # impl IBar for BpBar {}
+    ///        # impl IBar for Binder<BnBar> {}
+    ///        # fn main() -> binder::Result<()> {
+    ///        # let binder = Binder::new(());
+    ///        if let Some(barBinder) = binder.get_extension()? {
+    ///            let bar = BpBar::new(barBinder)
+    ///                .expect("Extension was not of type IBar");
+    ///        } else {
+    ///            // There was no extension
+    ///        }
+    ///        # }
+    pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
+        let status = unsafe {
+            // Safety: `AIBinder_setExtension` expects two valid, mutable
+            // `AIBinder` pointers. We are guaranteed that both `self` and
+            // `extension` contain valid `AIBinder` pointers, because they
+            // cannot be initialized without a valid
+            // pointer. `AIBinder_setExtension` does not take ownership of
+            // either parameter.
+            sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut())
+        };
+        status_result(status)
+    }
+
+    /// Retrieve the interface descriptor string for this object's Binder
+    /// interface.
+    pub fn get_descriptor() -> &'static str {
+        T::get_descriptor()
+    }
+}
+
+impl<T: Remotable> Interface for Binder<T> {
+    /// Converts the local remotable object into a generic `SpIBinder`
+    /// reference.
+    ///
+    /// The resulting `SpIBinder` will hold its own strong reference to this
+    /// remotable object, which will prevent the object from being dropped while
+    /// the `SpIBinder` is alive.
+    fn as_binder(&self) -> SpIBinder {
+        unsafe {
+            // Safety: `self.ibinder` is guaranteed to always be a valid pointer
+            // to an `AIBinder` by the `Binder` constructor. We are creating a
+            // copy of the `self.ibinder` strong reference, but
+            // `SpIBinder::from_raw` assumes it receives an owned pointer with
+            // its own strong reference. We first increment the reference count,
+            // so that the new `SpIBinder` will be tracked as a new reference.
+            sys::AIBinder_incStrong(self.ibinder);
+            SpIBinder::from_raw(self.ibinder).unwrap()
+        }
+    }
+}
+
+impl<T: Remotable> InterfaceClassMethods for Binder<T> {
+    fn get_descriptor() -> &'static str {
+        <T as Remotable>::get_descriptor()
+    }
+
+    /// Called whenever a transaction needs to be processed by a local
+    /// implementation.
+    ///
+    /// # Safety
+    ///
+    /// Must be called with a non-null, valid pointer to a local `AIBinder` that
+    /// contains a `T` pointer in its user data. The `data` and `reply` parcel
+    /// parameters must be valid pointers to `AParcel` objects. This method does
+    /// not take ownership of any of its parameters.
+    ///
+    /// These conditions hold when invoked by `ABBinder::onTransact`.
+    unsafe extern "C" fn on_transact(
+        binder: *mut sys::AIBinder,
+        code: u32,
+        data: *const sys::AParcel,
+        reply: *mut sys::AParcel,
+    ) -> status_t {
+        let res = {
+            let mut reply = Parcel::borrowed(reply).unwrap();
+            let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap();
+            let object = sys::AIBinder_getUserData(binder);
+            let binder: &T = &*(object as *const T);
+            binder.on_transact(code, &data, &mut reply)
+        };
+        match res {
+            Ok(()) => 0i32,
+            Err(e) => e as i32,
+        }
+    }
+
+    /// Called whenever an `AIBinder` object is no longer referenced and needs
+    /// destroyed.
+    ///
+    /// # Safety
+    ///
+    /// Must be called with a valid pointer to a `T` object. After this call,
+    /// the pointer will be invalid and should not be dereferenced.
+    unsafe extern "C" fn on_destroy(object: *mut c_void) {
+        ptr::drop_in_place(object as *mut T)
+    }
+
+    /// Called whenever a new, local `AIBinder` object is needed of a specific
+    /// class.
+    ///
+    /// Constructs the user data pointer that will be stored in the object,
+    /// which will be a heap-allocated `T` object.
+    ///
+    /// # Safety
+    ///
+    /// Must be called with a valid pointer to a `T` object allocated via `Box`.
+    unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void {
+        // We just return the argument, as it is already a pointer to the rust
+        // object created by Box.
+        args
+    }
+}
+
+impl<T: Remotable> Drop for Binder<T> {
+    // This causes C++ to decrease the strong ref count of the `AIBinder`
+    // object. We specifically do not drop the `rust_object` here. When C++
+    // actually destroys the object, it calls `on_destroy` and we can drop the
+    // `rust_object` then.
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: When `self` is dropped, we can no longer access the
+            // reference, so can decrement the reference count. `self.ibinder`
+            // is always a valid `AIBinder` pointer, so is valid to pass to
+            // `AIBinder_decStrong`.
+            sys::AIBinder_decStrong(self.ibinder);
+        }
+    }
+}
+
+impl<T: Remotable> Deref for Binder<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            // Safety: While `self` is alive, the reference count of the
+            // underlying object is > 0 and therefore `on_destroy` cannot be
+            // called. Therefore while `self` is alive, we know that
+            // `rust_object` is still a valid pointer to a heap allocated object
+            // of type `T`.
+            &*self.rust_object
+        }
+    }
+}
+
+impl<B: Remotable> Serialize for Binder<B> {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        parcel.write_binder(Some(&self.as_binder()))
+    }
+}
+
+// This implementation is an idiomatic implementation of the C++
+// `IBinder::localBinder` interface if the binder object is a Rust binder
+// service.
+impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> {
+    type Error = StatusCode;
+
+    fn try_from(mut ibinder: SpIBinder) -> Result<Self> {
+        let class = B::get_class();
+        if Some(class) != ibinder.get_class() {
+            return Err(StatusCode::BAD_TYPE);
+        }
+        let userdata = unsafe {
+            // Safety: `SpIBinder` always holds a valid pointer pointer to an
+            // `AIBinder`, which we can safely pass to
+            // `AIBinder_getUserData`. `ibinder` retains ownership of the
+            // returned pointer.
+            sys::AIBinder_getUserData(ibinder.as_native_mut())
+        };
+        if userdata.is_null() {
+            return Err(StatusCode::UNEXPECTED_NULL);
+        }
+        // We are transferring the ownership of the AIBinder into the new Binder
+        // object.
+        let mut ibinder = ManuallyDrop::new(ibinder);
+        Ok(Binder {
+            ibinder: ibinder.as_native_mut(),
+            rust_object: userdata as *mut B,
+        })
+    }
+}
+
+/// # Safety
+///
+/// The constructor for `Binder` guarantees that `self.ibinder` will contain a
+/// valid, non-null pointer to an `AIBinder`, so this implementation is type
+/// safe. `self.ibinder` will remain valid for the entire lifetime of `self`
+/// because we hold a strong reference to the `AIBinder` until `self` is
+/// dropped.
+unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
+    fn as_native(&self) -> *const sys::AIBinder {
+        self.ibinder
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+        self.ibinder
+    }
+}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+    let instance = CString::new(identifier).unwrap();
+    let status = unsafe {
+        // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+        // string pointers. Caller retains ownership of both
+        // pointers. `AServiceManager_addService` creates a new strong reference
+        // and copies the string, so both pointers need only be valid until the
+        // call returns.
+        sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr())
+    };
+    status_result(status)
+}
+
+/// Tests often create a base BBinder instance; so allowing the unit
+/// type to be remotable translates nicely to Binder::new(()).
+impl Remotable for () {
+    fn get_descriptor() -> &'static str {
+        ""
+    }
+
+    fn on_transact(
+        &self,
+        _code: TransactionCode,
+        _data: &Parcel,
+        _reply: &mut Parcel,
+    ) -> Result<()> {
+        Ok(())
+    }
+
+    binder_fn_get_class!(Binder::<Self>);
+}
+
+impl Interface for () {}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
new file mode 100644
index 0000000..6c34824
--- /dev/null
+++ b/libs/binder/rust/src/parcel.rs
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Container for messages that are sent via binder.
+
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::cell::RefCell;
+use std::convert::TryInto;
+use std::mem::ManuallyDrop;
+use std::ptr;
+
+mod file_descriptor;
+mod parcelable;
+
+pub use self::file_descriptor::ParcelFileDescriptor;
+pub use self::parcelable::{
+    Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
+};
+
+/// Container for a message (data and object references) that can be sent
+/// through Binder.
+///
+/// A Parcel can contain both serialized data that will be deserialized on the
+/// other side of the IPC, and references to live Binder objects that will
+/// result in the other side receiving a proxy Binder connected with the
+/// original Binder in the Parcel.
+pub enum Parcel {
+    /// Owned parcel pointer
+    Owned(*mut sys::AParcel),
+    /// Borrowed parcel pointer (will not be destroyed on drop)
+    Borrowed(*mut sys::AParcel),
+}
+
+/// # Safety
+///
+/// The `Parcel` constructors guarantee that a `Parcel` object will always
+/// contain a valid pointer to an `AParcel`.
+unsafe impl AsNative<sys::AParcel> for Parcel {
+    fn as_native(&self) -> *const sys::AParcel {
+        match *self {
+            Self::Owned(x) | Self::Borrowed(x) => x,
+        }
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AParcel {
+        match *self {
+            Self::Owned(x) | Self::Borrowed(x) => x,
+        }
+    }
+}
+
+impl Parcel {
+    /// Create a borrowed reference to a parcel object from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// This constructor is safe if the raw pointer parameter is either null
+    /// (resulting in `None`), or a valid pointer to an `AParcel` object.
+    pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
+        ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
+    }
+
+    /// Create an owned reference to a parcel object from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// This constructor is safe if the raw pointer parameter is either null
+    /// (resulting in `None`), or a valid pointer to an `AParcel` object. The
+    /// parcel object must be owned by the caller prior to this call, as this
+    /// constructor takes ownership of the parcel and will destroy it on drop.
+    pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> {
+        ptr.as_mut().map(|ptr| Self::Owned(ptr))
+    }
+
+    /// Consume the parcel, transferring ownership to the caller if the parcel
+    /// was owned.
+    pub(crate) fn into_raw(mut self) -> *mut sys::AParcel {
+        let ptr = self.as_native_mut();
+        let _ = ManuallyDrop::new(self);
+        ptr
+    }
+}
+
+// Data serialization methods
+impl Parcel {
+    /// Data written to parcelable is zero'd before being deleted or reallocated.
+    pub fn mark_sensitive(&mut self) {
+        unsafe {
+            // Safety: guaranteed to have a parcel object, and this method never fails
+            sys::AParcel_markSensitive(self.as_native())
+        }
+    }
+
+    /// Write a type that implements [`Serialize`] to the `Parcel`.
+    pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+        parcelable.serialize(self)
+    }
+
+    /// Writes the length of a slice to the `Parcel`.
+    ///
+    /// This is used in AIDL-generated client side code to indicate the
+    /// allocated space for an output array parameter.
+    pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
+        if let Some(slice) = slice {
+            let len: i32 = slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?;
+            self.write(&len)
+        } else {
+            self.write(&-1i32)
+        }
+    }
+
+    /// Perform a series of writes to the `Parcel`, prepended with the length
+    /// (in bytes) of the written data.
+    ///
+    /// The length `0i32` will be written to the parcel first, followed by the
+    /// writes performed by the callback. The initial length will then be
+    /// updated to the length of all data written by the callback, plus the
+    /// size of the length elemement itself (4 bytes).
+    ///
+    /// # Examples
+    ///
+    /// After the following call:
+    ///
+    /// ```
+    /// # use binder::{Binder, Interface, Parcel};
+    /// # let mut parcel = Parcel::Owned(std::ptr::null_mut());
+    /// parcel.sized_write(|subparcel| {
+    ///     subparcel.write(&1u32)?;
+    ///     subparcel.write(&2u32)?;
+    ///     subparcel.write(&3u32)
+    /// });
+    /// ```
+    ///
+    /// `parcel` will contain the following:
+    ///
+    /// ```ignore
+    /// [16i32, 1u32, 2u32, 3u32]
+    /// ```
+    pub fn sized_write<F>(&mut self, f: F) -> Result<()>
+    where for<'a>
+        F: Fn(&'a WritableSubParcel<'a>) -> Result<()>
+    {
+        let start = self.get_data_position();
+        self.write(&0i32)?;
+        {
+            let subparcel = WritableSubParcel(RefCell::new(self));
+            f(&subparcel)?;
+        }
+        let end = self.get_data_position();
+        unsafe {
+            self.set_data_position(start)?;
+        }
+        assert!(end >= start);
+        self.write(&(end - start))?;
+        unsafe {
+            self.set_data_position(end)?;
+        }
+        Ok(())
+    }
+
+    /// Returns the current position in the parcel data.
+    pub fn get_data_position(&self) -> i32 {
+        unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
+            // and this call is otherwise safe.
+            sys::AParcel_getDataPosition(self.as_native())
+        }
+    }
+
+    /// Move the current read/write position in the parcel.
+    ///
+    /// The new position must be a position previously returned by
+    /// `self.get_data_position()`.
+    ///
+    /// # Safety
+    ///
+    /// This method is safe if `pos` is less than the current size of the parcel
+    /// data buffer. Otherwise, we are relying on correct bounds checking in the
+    /// Parcel C++ code on every subsequent read or write to this parcel. If all
+    /// accesses are bounds checked, this call is still safe, but we can't rely
+    /// on that.
+    pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
+        status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
+    }
+}
+
+/// A segment of a writable parcel, used for [`Parcel::sized_write`].
+pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>);
+
+impl<'a> WritableSubParcel<'a> {
+    /// Write a type that implements [`Serialize`] to the sub-parcel.
+    pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> {
+        parcelable.serialize(&mut *self.0.borrow_mut())
+    }
+}
+
+// Data deserialization methods
+impl Parcel {
+    /// Attempt to read a type that implements [`Deserialize`] from this
+    /// `Parcel`.
+    pub fn read<D: Deserialize>(&self) -> Result<D> {
+        D::deserialize(self)
+    }
+
+    /// Read a vector size from the `Parcel` and resize the given output vector
+    /// to be correctly sized for that amount of data.
+    ///
+    /// This method is used in AIDL-generated server side code for methods that
+    /// take a mutable slice reference parameter.
+    pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> {
+        let len: i32 = self.read()?;
+
+        if len < 0 {
+            return Err(StatusCode::UNEXPECTED_NULL);
+        }
+
+        // usize in Rust may be 16-bit, so i32 may not fit
+        let len = len.try_into().unwrap();
+        out_vec.resize_with(len, Default::default);
+
+        Ok(())
+    }
+
+    /// Read a vector size from the `Parcel` and either create a correctly sized
+    /// vector for that amount of data or set the output parameter to None if
+    /// the vector should be null.
+    ///
+    /// This method is used in AIDL-generated server side code for methods that
+    /// take a mutable slice reference parameter.
+    pub fn resize_nullable_out_vec<D: Default + Deserialize>(
+        &self,
+        out_vec: &mut Option<Vec<D>>,
+    ) -> Result<()> {
+        let len: i32 = self.read()?;
+
+        if len < 0 {
+            *out_vec = None;
+        } else {
+            // usize in Rust may be 16-bit, so i32 may not fit
+            let len = len.try_into().unwrap();
+            let mut vec = Vec::with_capacity(len);
+            vec.resize_with(len, Default::default);
+            *out_vec = Some(vec);
+        }
+
+        Ok(())
+    }
+}
+
+// Internal APIs
+impl Parcel {
+    pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
+        unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
+            // null or a valid pointer to an `AIBinder`, both of which are
+            // valid, safe inputs to `AParcel_writeStrongBinder`.
+            //
+            // This call does not take ownership of the binder. However, it does
+            // require a mutable pointer, which we cannot extract from an
+            // immutable reference, so we clone the binder, incrementing the
+            // refcount before the call. The refcount will be immediately
+            // decremented when this temporary is dropped.
+            status_result(sys::AParcel_writeStrongBinder(
+                self.as_native_mut(),
+                binder.cloned().as_native_mut(),
+            ))
+        }
+    }
+
+    pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
+        let mut binder = ptr::null_mut();
+        let status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. We pass a valid, mutable out pointer to the `binder`
+            // parameter. After this call, `binder` will be either null or a
+            // valid pointer to an `AIBinder` owned by the caller.
+            sys::AParcel_readStrongBinder(self.as_native(), &mut binder)
+        };
+
+        status_result(status)?;
+
+        Ok(unsafe {
+            // Safety: `binder` is either null or a valid, owned pointer at this
+            // point, so can be safely passed to `SpIBinder::from_raw`.
+            SpIBinder::from_raw(binder)
+        })
+    }
+}
+
+impl Drop for Parcel {
+    fn drop(&mut self) {
+        // Run the C++ Parcel complete object destructor
+        if let Self::Owned(ptr) = *self {
+            unsafe {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. If we own the parcel, we can safely delete it
+                // here.
+                sys::AParcel_delete(ptr)
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+impl Parcel {
+    /// Create a new parcel tied to a bogus binder. TESTING ONLY!
+    ///
+    /// This can only be used for testing! All real parcel operations must be
+    /// done in the callback to [`IBinder::transact`] or in
+    /// [`Remotable::on_transact`] using the parcels provided to these methods.
+    pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> {
+        let mut input = ptr::null_mut();
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `binder` always contains a
+            // valid pointer to an `AIBinder`. We pass a valid, mutable out
+            // pointer to receive a newly constructed parcel. When successful
+            // this function assigns a new pointer to an `AParcel` to `input`
+            // and transfers ownership of this pointer to the caller. Thus,
+            // after this call, `input` will either be null or point to a valid,
+            // owned `AParcel`.
+            sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input)
+        };
+        status_result(status)?;
+        unsafe {
+            // Safety: `input` is either null or a valid, owned pointer to an
+            // `AParcel`, so is valid to safe to
+            // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel
+            // pointer.
+            Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+        }
+    }
+}
+
+#[test]
+fn test_read_write() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+
+    let mut service = Binder::new(()).as_binder();
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<i8>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<u16>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<u32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<i64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<u64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<f32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<f64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+    assert_eq!(parcel.read::<Option<String>>(), Ok(None));
+    assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
+
+    assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
+
+    parcel.write(&1i32).unwrap();
+
+    unsafe {
+        parcel.set_data_position(start).unwrap();
+    }
+
+    let i: i32 = parcel.read().unwrap();
+    assert_eq!(i, 1i32);
+}
+
+#[test]
+#[allow(clippy::float_cmp)]
+fn test_read_data() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+
+    let mut service = Binder::new(()).as_binder();
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let str_start = parcel.get_data_position();
+
+    parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
+    // Skip over string length
+    unsafe {
+        assert!(parcel.set_data_position(str_start).is_ok());
+    }
+    assert_eq!(parcel.read::<i32>().unwrap(), 15);
+    let start = parcel.get_data_position();
+
+    assert_eq!(parcel.read::<bool>().unwrap(), true);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<i8>().unwrap(), 72i8);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u16>().unwrap(), 25928);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<i32>().unwrap(), 1819043144);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 1819043144);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(
+        parcel.read::<f32>().unwrap(),
+        1143139100000000000000000000.0
+    );
+    assert_eq!(parcel.read::<f32>().unwrap(), 40.043392);
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815);
+
+    // Skip back to before the string length
+    unsafe {
+        assert!(parcel.set_data_position(str_start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<Vec<u8>>().unwrap(), b"Hello, Binder!\0");
+}
+
+#[test]
+fn test_utf8_utf16_conversions() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+
+    let mut service = Binder::new(()).as_binder();
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    assert!(parcel.write("Hello, Binder!").is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert_eq!(
+        parcel.read::<Option<String>>().unwrap().unwrap(),
+        "Hello, Binder!",
+    );
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert!(parcel.write("Embedded null \0 inside a string").is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert_eq!(
+        parcel.read::<Option<String>>().unwrap().unwrap(),
+        "Embedded null \0 inside a string",
+    );
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok());
+    assert!(parcel
+        .write(
+            &[
+                String::from("str4"),
+                String::from("str5"),
+                String::from("str6"),
+            ][..]
+        )
+        .is_ok());
+
+    let s1 = "Hello, Binder!";
+    let s2 = "This is a utf8 string.";
+    let s3 = "Some more text here.";
+
+    assert!(parcel.write(&[s1, s2, s3][..]).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(
+        parcel.read::<Vec<String>>().unwrap(),
+        ["str1", "str2", "str3"]
+    );
+    assert_eq!(
+        parcel.read::<Vec<String>>().unwrap(),
+        ["str4", "str5", "str6"]
+    );
+    assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]);
+}
+
+#[test]
+fn test_sized_write() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+
+    let mut service = Binder::new(()).as_binder();
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    let arr = [1i32, 2i32, 3i32];
+
+    parcel.sized_write(|subparcel| {
+        subparcel.write(&arr[..])
+    }).expect("Could not perform sized write");
+
+    // i32 sub-parcel length + i32 array length + 3 i32 elements
+    let expected_len = 20i32;
+
+    assert_eq!(parcel.get_data_position(), start + expected_len);
+
+    unsafe {
+        parcel.set_data_position(start).unwrap();
+    }
+
+    assert_eq!(
+        expected_len,
+        parcel.read().unwrap(),
+    );
+
+    assert_eq!(
+        parcel.read::<Vec<i32>>().unwrap(),
+        &arr,
+    );
+}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
new file mode 100644
index 0000000..20e9178
--- /dev/null
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use super::{
+    Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+    SerializeOption,
+};
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::sys;
+
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+
+/// Rust version of the Java class android.os.ParcelFileDescriptor
+#[derive(Debug)]
+pub struct ParcelFileDescriptor(File);
+
+impl ParcelFileDescriptor {
+    /// Create a new `ParcelFileDescriptor`
+    pub fn new(file: File) -> Self {
+        Self(file)
+    }
+}
+
+impl AsRef<File> for ParcelFileDescriptor {
+    fn as_ref(&self) -> &File {
+        &self.0
+    }
+}
+
+impl From<ParcelFileDescriptor> for File {
+    fn from(file: ParcelFileDescriptor) -> File {
+        file.0
+    }
+}
+
+impl Serialize for ParcelFileDescriptor {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        let fd = self.0.as_raw_fd();
+        let status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
+            // valid file, so we can borrow a valid file
+            // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
+            // ownership of the fd, so we need not duplicate it first.
+            sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd)
+        };
+        status_result(status)
+    }
+}
+
+impl SerializeArray for ParcelFileDescriptor {}
+
+impl SerializeOption for ParcelFileDescriptor {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        if let Some(f) = this {
+            f.serialize(parcel)
+        } else {
+            let status = unsafe {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
+                // value `-1` as the file descriptor to signify serializing a
+                // null file descriptor.
+                sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32)
+            };
+            status_result(status)
+        }
+    }
+}
+
+impl SerializeArray for Option<ParcelFileDescriptor> {}
+
+impl DeserializeOption for ParcelFileDescriptor {
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+        let mut fd = -1i32;
+        unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. We pass a valid mutable pointer to an i32, which
+            // `AParcel_readParcelFileDescriptor` assigns the valid file
+            // descriptor into, or `-1` if deserializing a null file
+            // descriptor. The read function passes ownership of the file
+            // descriptor to its caller if it was non-null, so we must take
+            // ownership of the file and ensure that it is eventually closed.
+            status_result(sys::AParcel_readParcelFileDescriptor(
+                parcel.as_native(),
+                &mut fd,
+            ))?;
+        }
+        if fd < 0 {
+            Ok(None)
+        } else {
+            let file = unsafe {
+                // Safety: At this point, we know that the file descriptor was
+                // not -1, so must be a valid, owned file descriptor which we
+                // can safely turn into a `File`.
+                File::from_raw_fd(fd)
+            };
+            Ok(Some(ParcelFileDescriptor::new(file)))
+        }
+    }
+}
+
+impl DeserializeArray for Option<ParcelFileDescriptor> {}
+
+impl Deserialize for ParcelFileDescriptor {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        Deserialize::deserialize(parcel)
+            .transpose()
+            .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+    }
+}
+
+impl DeserializeArray for ParcelFileDescriptor {}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
new file mode 100644
index 0000000..138b360
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::{AsNative, FromIBinder};
+use crate::error::{status_result, Result, Status, StatusCode};
+use crate::parcel::Parcel;
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::c_void;
+use std::os::raw::c_char;
+use std::ptr;
+
+/// A struct whose instances can be written to a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Serialize {
+    /// Serialize this instance into the given [`Parcel`].
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()>;
+}
+
+/// A struct whose instances can be restored from a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Deserialize: Sized {
+    /// Deserialize an instance from the given [`Parcel`].
+    fn deserialize(parcel: &Parcel) -> Result<Self>;
+}
+
+/// Helper trait for types that can be serialized as arrays.
+/// Defaults to calling Serialize::serialize() manually for every element,
+/// but can be overridden for custom implementations like `writeByteArray`.
+// Until specialization is stabilized in Rust, we need this to be a separate
+// trait because it's the only way to have a default implementation for a method.
+// We want the default implementation for most types, but an override for
+// a few special ones like `readByteArray` for `u8`.
+pub trait SerializeArray: Serialize + Sized {
+    /// Serialize an array of this type into the given [`Parcel`].
+    fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+        parcel.write_slice_size(Some(slice))?;
+
+        for item in slice {
+            parcel.write(item)?;
+        }
+
+        Ok(())
+    }
+}
+
+/// Helper trait for types that can be deserialized as arrays.
+/// Defaults to calling Deserialize::deserialize() manually for every element,
+/// but can be overridden for custom implementations like `readByteArray`.
+pub trait DeserializeArray: Deserialize {
+    /// Deserialize an array of type from the given [`Parcel`].
+    fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+        let len: i32 = parcel.read()?;
+        if len < 0 {
+            return Ok(None);
+        }
+
+        // TODO: Assumes that usize is at least 32 bits
+        let mut vec = Vec::with_capacity(len as usize);
+
+        for _ in 0..len {
+            vec.push(parcel.read()?);
+        }
+
+        Ok(Some(vec))
+    }
+}
+
+/// Helper trait for types that can be nullable when serialized.
+// We really need this trait instead of implementing `Serialize for Option<T>`
+// because of the Rust orphan rule which prevents us from doing
+// `impl Serialize for Option<&dyn IFoo>` for AIDL interfaces.
+// Instead we emit `impl SerializeOption for dyn IFoo` which is allowed.
+// We also use it to provide a default implementation for AIDL-generated
+// parcelables.
+pub trait SerializeOption: Serialize {
+    /// Serialize an Option of this type into the given [`Parcel`].
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        if let Some(inner) = this {
+            parcel.write(&1i32)?;
+            parcel.write(inner)
+        } else {
+            parcel.write(&0i32)
+        }
+    }
+}
+
+/// Helper trait for types that can be nullable when deserialized.
+pub trait DeserializeOption: Deserialize {
+    /// Deserialize an Option of this type from the given [`Parcel`].
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+        let null: i32 = parcel.read()?;
+        if null == 0 {
+            Ok(None)
+        } else {
+            parcel.read().map(Some)
+        }
+    }
+}
+
+/// Callback to allocate a vector for parcel array read functions.
+///
+/// # Safety
+///
+/// The opaque data pointer passed to the array read function must be a mutable
+/// pointer to an `Option<Vec<T>>`. `buffer` will be assigned a mutable pointer
+/// to the allocated vector data if this function returns true.
+unsafe extern "C" fn allocate_vec<T: Clone + Default>(
+    data: *mut c_void,
+    len: i32,
+    buffer: *mut *mut T,
+) -> bool {
+    let vec = &mut *(data as *mut Option<Vec<T>>);
+    if len < 0 {
+        *vec = None;
+        return true;
+    }
+    let mut new_vec: Vec<T> = Vec::with_capacity(len as usize);
+    new_vec.resize_with(len as usize, Default::default);
+    *buffer = new_vec.as_mut_ptr();
+    *vec = Some(new_vec);
+    true
+}
+
+macro_rules! parcelable_primitives {
+    {
+        $(
+            impl $trait:ident for $ty:ty = $fn:path;
+        )*
+    } => {
+        $(impl_parcelable!{$trait, $ty, $fn})*
+    };
+}
+
+macro_rules! impl_parcelable {
+    {Serialize, $ty:ty, $write_fn:path} => {
+        impl Serialize for $ty {
+            fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+                unsafe {
+                    // Safety: `Parcel` always contains a valid pointer to an
+                    // `AParcel`, and any `$ty` literal value is safe to pass to
+                    // `$write_fn`.
+                    status_result($write_fn(parcel.as_native_mut(), *self))
+                }
+            }
+        }
+    };
+
+    {Deserialize, $ty:ty, $read_fn:path} => {
+        impl Deserialize for $ty {
+            fn deserialize(parcel: &Parcel) -> Result<Self> {
+                let mut val = Self::default();
+                unsafe {
+                    // Safety: `Parcel` always contains a valid pointer to an
+                    // `AParcel`. We pass a valid, mutable pointer to `val`, a
+                    // literal of type `$ty`, and `$read_fn` will write the
+                    // value read into `val` if successful
+                    status_result($read_fn(parcel.as_native(), &mut val))?
+                };
+                Ok(val)
+            }
+        }
+    };
+
+    {SerializeArray, $ty:ty, $write_array_fn:path} => {
+        impl SerializeArray for $ty {
+            fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+                let status = unsafe {
+                    // Safety: `Parcel` always contains a valid pointer to an
+                    // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
+                    // will be a valid pointer to an array of elements of type
+                    // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
+                    // dangling, but this is safe since the pointer is not
+                    // dereferenced if the length parameter is 0.
+                    $write_array_fn(
+                        parcel.as_native_mut(),
+                        slice.as_ptr(),
+                        slice
+                            .len()
+                            .try_into()
+                            .or(Err(StatusCode::BAD_VALUE))?,
+                    )
+                };
+                status_result(status)
+            }
+        }
+    };
+
+    {DeserializeArray, $ty:ty, $read_array_fn:path} => {
+        impl DeserializeArray for $ty {
+            fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+                let mut vec: Option<Vec<Self>> = None;
+                let status = unsafe {
+                    // Safety: `Parcel` always contains a valid pointer to an
+                    // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
+                    // be of type `*mut Option<Vec<T>>`, so `&mut vec` is
+                    // correct for it.
+                    $read_array_fn(
+                        parcel.as_native(),
+                        &mut vec as *mut _ as *mut c_void,
+                        Some(allocate_vec),
+                    )
+                };
+                status_result(status)?;
+                Ok(vec)
+            }
+        }
+    };
+}
+
+parcelable_primitives! {
+    impl Serialize for bool = sys::AParcel_writeBool;
+    impl Deserialize for bool = sys::AParcel_readBool;
+
+    // This is only safe because `Option<Vec<u8>>` is interchangeable with
+    // `Option<Vec<i8>>` (what the allocator function actually allocates.
+    impl DeserializeArray for u8 = sys::AParcel_readByteArray;
+
+    impl Serialize for i8 = sys::AParcel_writeByte;
+    impl Deserialize for i8 = sys::AParcel_readByte;
+    impl SerializeArray for i8 = sys::AParcel_writeByteArray;
+    impl DeserializeArray for i8 = sys::AParcel_readByteArray;
+
+    impl Serialize for u16 = sys::AParcel_writeChar;
+    impl Deserialize for u16 = sys::AParcel_readChar;
+    impl SerializeArray for u16 = sys::AParcel_writeCharArray;
+    impl DeserializeArray for u16 = sys::AParcel_readCharArray;
+
+    // This is only safe because `Option<Vec<i16>>` is interchangeable with
+    // `Option<Vec<u16>>` (what the allocator function actually allocates.
+    impl DeserializeArray for i16 = sys::AParcel_readCharArray;
+
+    impl Serialize for u32 = sys::AParcel_writeUint32;
+    impl Deserialize for u32 = sys::AParcel_readUint32;
+    impl SerializeArray for u32 = sys::AParcel_writeUint32Array;
+    impl DeserializeArray for u32 = sys::AParcel_readUint32Array;
+
+    impl Serialize for i32 = sys::AParcel_writeInt32;
+    impl Deserialize for i32 = sys::AParcel_readInt32;
+    impl SerializeArray for i32 = sys::AParcel_writeInt32Array;
+    impl DeserializeArray for i32 = sys::AParcel_readInt32Array;
+
+    impl Serialize for u64 = sys::AParcel_writeUint64;
+    impl Deserialize for u64 = sys::AParcel_readUint64;
+    impl SerializeArray for u64 = sys::AParcel_writeUint64Array;
+    impl DeserializeArray for u64 = sys::AParcel_readUint64Array;
+
+    impl Serialize for i64 = sys::AParcel_writeInt64;
+    impl Deserialize for i64 = sys::AParcel_readInt64;
+    impl SerializeArray for i64 = sys::AParcel_writeInt64Array;
+    impl DeserializeArray for i64 = sys::AParcel_readInt64Array;
+
+    impl Serialize for f32 = sys::AParcel_writeFloat;
+    impl Deserialize for f32 = sys::AParcel_readFloat;
+    impl SerializeArray for f32 = sys::AParcel_writeFloatArray;
+    impl DeserializeArray for f32 = sys::AParcel_readFloatArray;
+
+    impl Serialize for f64 = sys::AParcel_writeDouble;
+    impl Deserialize for f64 = sys::AParcel_readDouble;
+    impl SerializeArray for f64 = sys::AParcel_writeDoubleArray;
+    impl DeserializeArray for f64 = sys::AParcel_readDoubleArray;
+}
+
+impl SerializeArray for bool {}
+impl DeserializeArray for bool {}
+
+impl Serialize for u8 {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        (*self as i8).serialize(parcel)
+    }
+}
+
+impl Deserialize for u8 {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        i8::deserialize(parcel).map(|v| v as u8)
+    }
+}
+
+impl SerializeArray for u8 {
+    fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+        let status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+            // valid pointer to an array of elements of type `$ty`. If the slice
+            // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+            // since the pointer is not dereferenced if the length parameter is
+            // 0.
+            sys::AParcel_writeByteArray(
+                parcel.as_native_mut(),
+                slice.as_ptr() as *const i8,
+                slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+            )
+        };
+        status_result(status)
+    }
+}
+
+impl Serialize for i16 {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        (*self as u16).serialize(parcel)
+    }
+}
+
+impl Deserialize for i16 {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        u16::deserialize(parcel).map(|v| v as i16)
+    }
+}
+
+impl SerializeArray for i16 {
+    fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+        let status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+            // valid pointer to an array of elements of type `$ty`. If the slice
+            // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+            // since the pointer is not dereferenced if the length parameter is
+            // 0.
+            sys::AParcel_writeCharArray(
+                parcel.as_native_mut(),
+                slice.as_ptr() as *const u16,
+                slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+            )
+        };
+        status_result(status)
+    }
+}
+
+impl SerializeOption for str {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        match this {
+            None => unsafe {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. If the string pointer is null,
+                // `AParcel_writeString` requires that the length is -1 to
+                // indicate that we want to serialize a null string.
+                status_result(sys::AParcel_writeString(
+                    parcel.as_native_mut(),
+                    ptr::null(),
+                    -1,
+                ))
+            },
+            Some(s) => unsafe {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
+                // string pointer of `length` bytes, which is what str in Rust
+                // is. The docstring for `AParcel_writeString` says that the
+                // string input should be null-terminated, but it doesn't
+                // actually rely on that fact in the code. If this ever becomes
+                // necessary, we will need to null-terminate the str buffer
+                // before sending it.
+                status_result(sys::AParcel_writeString(
+                    parcel.as_native_mut(),
+                    s.as_ptr() as *const c_char,
+                    s.as_bytes()
+                        .len()
+                        .try_into()
+                        .or(Err(StatusCode::BAD_VALUE))?,
+                ))
+            },
+        }
+    }
+}
+
+impl SerializeArray for Option<&str> {}
+
+impl Serialize for str {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Some(self).serialize(parcel)
+    }
+}
+
+impl SerializeArray for &str {}
+
+impl Serialize for String {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Some(self.as_str()).serialize(parcel)
+    }
+}
+
+impl SerializeArray for String {}
+
+impl SerializeOption for String {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(this.map(String::as_str), parcel)
+    }
+}
+
+impl SerializeArray for Option<String> {}
+
+impl Deserialize for Option<String> {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        let mut vec: Option<Vec<u8>> = None;
+        let status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
+            // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
+            // for `allocate_vec`, so `vec` is safe to pass as the opaque data
+            // pointer on platforms where char is signed.
+            sys::AParcel_readString(
+                parcel.as_native(),
+                &mut vec as *mut _ as *mut c_void,
+                Some(allocate_vec),
+            )
+        };
+
+        status_result(status)?;
+        vec.map(|mut s| {
+            // The vector includes a null-terminator and we don't want the
+            // string to be null-terminated for Rust.
+            s.pop();
+            String::from_utf8(s).or(Err(StatusCode::BAD_VALUE))
+        })
+        .transpose()
+    }
+}
+
+impl DeserializeArray for Option<String> {}
+
+impl Deserialize for String {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        Deserialize::deserialize(parcel)
+            .transpose()
+            .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+    }
+}
+
+impl DeserializeArray for String {}
+
+impl<T: SerializeArray> Serialize for [T] {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        SerializeArray::serialize_array(self, parcel)
+    }
+}
+
+impl<T: SerializeArray> Serialize for Vec<T> {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        SerializeArray::serialize_array(&self[..], parcel)
+    }
+}
+
+impl<T: SerializeArray> SerializeOption for [T] {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        if let Some(v) = this {
+            SerializeArray::serialize_array(v, parcel)
+        } else {
+            parcel.write(&-1i32)
+        }
+    }
+}
+
+impl<T: SerializeArray> SerializeOption for Vec<T> {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(this.map(Vec::as_slice), parcel)
+    }
+}
+
+impl<T: DeserializeArray> Deserialize for Vec<T> {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        DeserializeArray::deserialize_array(parcel)
+            .transpose()
+            .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+    }
+}
+
+impl<T: DeserializeArray> DeserializeOption for Vec<T> {
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+        DeserializeArray::deserialize_array(parcel)
+    }
+}
+
+impl Serialize for Status {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an `AParcel`
+            // and `Status` always contains a valid pointer to an `AStatus`, so
+            // both parameters are valid and safe. This call does not take
+            // ownership of either of its parameters.
+            status_result(sys::AParcel_writeStatusHeader(
+                parcel.as_native_mut(),
+                self.as_native(),
+            ))
+        }
+    }
+}
+
+impl Deserialize for Status {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        let mut status_ptr = ptr::null_mut();
+        let ret_status = unsafe {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. We pass a mutable out pointer which will be
+            // assigned a valid `AStatus` pointer if the function returns
+            // status OK. This function passes ownership of the status
+            // pointer to the caller, if it was assigned.
+            sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr)
+        };
+        status_result(ret_status)?;
+        Ok(unsafe {
+            // Safety: At this point, the return status of the read call was ok,
+            // so we know that `status_ptr` is a valid, owned pointer to an
+            // `AStatus`, from which we can safely construct a `Status` object.
+            Status::from_ptr(status_ptr)
+        })
+    }
+}
+
+impl<T: Serialize + ?Sized> Serialize for Box<T> {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Serialize::serialize(&**self, parcel)
+    }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(this.map(|b| &**b), parcel)
+    }
+}
+
+impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        let ibinder: SpIBinder = parcel.read()?;
+        FromIBinder::try_from(ibinder)
+    }
+}
+
+impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+        let ibinder: Option<SpIBinder> = parcel.read()?;
+        ibinder.map(FromIBinder::try_from).transpose()
+    }
+}
+
+// We need these to support Option<&T> for all T
+impl<T: Serialize + ?Sized> Serialize for &T {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Serialize::serialize(*self, parcel)
+    }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for &T {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(this.copied(), parcel)
+    }
+}
+
+impl<T: SerializeOption> Serialize for Option<T> {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(self.as_ref(), parcel)
+    }
+}
+
+impl<T: DeserializeOption> Deserialize for Option<T> {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        DeserializeOption::deserialize_option(parcel)
+    }
+}
+
+#[test]
+fn test_custom_parcelable() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+    let mut service = Binder::new(()).as_binder();
+
+    struct Custom(u32, bool, String, Vec<String>);
+
+    impl Serialize for Custom {
+        fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+            self.0.serialize(parcel)?;
+            self.1.serialize(parcel)?;
+            self.2.serialize(parcel)?;
+            self.3.serialize(parcel)
+        }
+    }
+
+    impl Deserialize for Custom {
+        fn deserialize(parcel: &Parcel) -> Result<Self> {
+            Ok(Custom(
+                parcel.read()?,
+                parcel.read()?,
+                parcel.read()?,
+                parcel.read::<Option<Vec<String>>>()?.unwrap(),
+            ))
+        }
+    }
+
+    let string8 = "Custom Parcelable".to_string();
+
+    let s1 = "str1".to_string();
+    let s2 = "str2".to_string();
+    let s3 = "str3".to_string();
+
+    let strs = vec![s1, s2, s3];
+
+    let custom = Custom(123_456_789, true, string8, strs);
+
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    assert!(custom.serialize(&mut parcel).is_ok());
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let custom2 = Custom::deserialize(&parcel).unwrap();
+
+    assert_eq!(custom2.0, 123_456_789);
+    assert!(custom2.1);
+    assert_eq!(custom2.2, custom.2);
+    assert_eq!(custom2.3, custom.3);
+}
+
+#[test]
+#[allow(clippy::excessive_precision)]
+fn test_slice_parcelables() {
+    use crate::binder::Interface;
+    use crate::native::Binder;
+    let mut service = Binder::new(()).as_binder();
+
+    let bools = [true, false, false, true];
+
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    assert!(bools.serialize(&mut parcel).is_ok());
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4);
+    assert_eq!(parcel.read::<u32>().unwrap(), 1);
+    assert_eq!(parcel.read::<u32>().unwrap(), 0);
+    assert_eq!(parcel.read::<u32>().unwrap(), 0);
+    assert_eq!(parcel.read::<u32>().unwrap(), 1);
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<bool>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [true, false, false, true]);
+
+    let u8s = [101u8, 255, 42, 117];
+
+    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let start = parcel.get_data_position();
+
+    assert!(parcel.write(&u8s[..]).is_ok());
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+    assert_eq!(vec, [101, 255, 42, 117]);
+
+    let i8s = [-128i8, 127, 42, -117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert!(parcel.write(&i8s[..]).is_ok());
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+    assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
+
+    let u16s = [u16::max_value(), 12_345, 42, 117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(u16s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+    assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+    assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<u16>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+
+    let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(i16s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+    assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<i16>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+
+    let u32s = [u32::max_value(), 12_345, 42, 117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(u32s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+    assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+    assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<u32>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+
+    let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(i32s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+    assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+    assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<i32>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+
+    let u64s = [u64::max_value(), 12_345, 42, 117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(u64s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<u64>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+
+    let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(i64s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<i64>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+
+    let f32s = [
+        std::f32::NAN,
+        std::f32::INFINITY,
+        1.23456789,
+        std::f32::EPSILON,
+    ];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(f32s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<f32>::deserialize(&parcel).unwrap();
+
+    // NAN != NAN so we can't use it in the assert_eq:
+    assert!(vec[0].is_nan());
+    assert_eq!(vec[1..], f32s[1..]);
+
+    let f64s = [
+        std::f64::NAN,
+        std::f64::INFINITY,
+        1.234567890123456789,
+        std::f64::EPSILON,
+    ];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(f64s.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<f64>::deserialize(&parcel).unwrap();
+
+    // NAN != NAN so we can't use it in the assert_eq:
+    assert!(vec[0].is_nan());
+    assert_eq!(vec[1..], f64s[1..]);
+
+    let s1 = "Hello, Binder!";
+    let s2 = "This is a utf8 string.";
+    let s3 = "Some more text here.";
+    let s4 = "Embedded nulls \0 \0";
+
+    let strs = [s1, s2, s3, s4];
+
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+    assert!(strs.serialize(&mut parcel).is_ok());
+    unsafe {
+        assert!(parcel.set_data_position(start).is_ok());
+    }
+
+    let vec = Vec::<String>::deserialize(&parcel).unwrap();
+
+    assert_eq!(vec, strs);
+}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
new file mode 100644
index 0000000..5002fc6
--- /dev/null
+++ b/libs/binder/rust/src/proxy.rs
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust API for interacting with a remote binder service.
+
+use crate::binder::{
+    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+};
+use crate::error::{status_result, Result, StatusCode};
+use crate::parcel::{
+    Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+    SerializeOption,
+};
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::{c_void, CString};
+use std::fmt;
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// A strong reference to a Binder remote object.
+///
+/// This struct encapsulates the generic C++ `sp<IBinder>` class. This wrapper
+/// is untyped; typed interface access is implemented by the AIDL compiler.
+pub struct SpIBinder(*mut sys::AIBinder);
+
+impl fmt::Debug for SpIBinder {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("SpIBinder")
+    }
+}
+
+/// # Safety
+///
+/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
+unsafe impl Send for SpIBinder {}
+
+impl SpIBinder {
+    /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer.
+    ///
+    /// # Safety
+    ///
+    /// This constructor is safe iff `ptr` is a null pointer or a valid pointer
+    /// to an `AIBinder`.
+    ///
+    /// In the non-null case, this method conceptually takes ownership of a strong
+    /// reference to the object, so `AIBinder_incStrong` must have been called
+    /// on the pointer before passing it to this constructor. This is generally
+    /// done by Binder NDK methods that return an `AIBinder`, but care should be
+    /// taken to ensure this invariant.
+    ///
+    /// All `SpIBinder` objects that are constructed will hold a valid pointer
+    /// to an `AIBinder`, which will remain valid for the entire lifetime of the
+    /// `SpIBinder` (we keep a strong reference, and only decrement on drop).
+    pub(crate) unsafe fn from_raw(ptr: *mut sys::AIBinder) -> Option<Self> {
+        ptr.as_mut().map(|p| Self(p))
+    }
+
+    /// Return true if this binder object is hosted in a different process than
+    /// the current one.
+    pub fn is_remote(&self) -> bool {
+        unsafe {
+            // Safety: `SpIBinder` guarantees that it always contains a valid
+            // `AIBinder` pointer.
+            sys::AIBinder_isRemote(self.as_native())
+        }
+    }
+
+    /// Try to convert this Binder object into a trait object for the given
+    /// Binder interface.
+    ///
+    /// If this object does not implement the expected interface, the error
+    /// `StatusCode::BAD_TYPE` is returned.
+    pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+        FromIBinder::try_from(self)
+    }
+
+    /// Return the interface class of this binder object, if associated with
+    /// one.
+    pub(crate) fn get_class(&mut self) -> Option<InterfaceClass> {
+        unsafe {
+            // Safety: `SpIBinder` guarantees that it always contains a valid
+            // `AIBinder` pointer. `AIBinder_getClass` returns either a null
+            // pointer or a valid pointer to an `AIBinder_Class`. After mapping
+            // null to None, we can safely construct an `InterfaceClass` if the
+            // pointer was non-null.
+            let class = sys::AIBinder_getClass(self.as_native_mut());
+            class.as_ref().map(|p| InterfaceClass::from_ptr(p))
+        }
+    }
+}
+
+/// An object that can be associate with an [`InterfaceClass`].
+pub trait AssociateClass {
+    /// Check if this object is a valid object for the given interface class
+    /// `I`.
+    ///
+    /// Returns `Some(self)` if this is a valid instance of the interface, and
+    /// `None` otherwise.
+    ///
+    /// Classes constructed by `InterfaceClass` are unique per type, so
+    /// repeatedly calling this method for the same `InterfaceClass` is allowed.
+    fn associate_class(&mut self, class: InterfaceClass) -> bool;
+}
+
+impl AssociateClass for SpIBinder {
+    fn associate_class(&mut self, class: InterfaceClass) -> bool {
+        unsafe {
+            // Safety: `SpIBinder` guarantees that it always contains a valid
+            // `AIBinder` pointer. An `InterfaceClass` can always be converted
+            // into a valid `AIBinder_Class` pointer, so these parameters are
+            // always safe.
+            sys::AIBinder_associateClass(self.as_native_mut(), class.into())
+        }
+    }
+}
+
+impl PartialEq for SpIBinder {
+    fn eq(&self, other: &Self) -> bool {
+        ptr::eq(self.0, other.0)
+    }
+}
+
+impl Eq for SpIBinder {}
+
+impl Clone for SpIBinder {
+    fn clone(&self) -> Self {
+        unsafe {
+            // Safety: Cloning a strong reference must increment the reference
+            // count. We are guaranteed by the `SpIBinder` constructor
+            // invariants that `self.0` is always a valid `AIBinder` pointer.
+            sys::AIBinder_incStrong(self.0);
+        }
+        Self(self.0)
+    }
+}
+
+impl Drop for SpIBinder {
+    // We hold a strong reference to the IBinder in SpIBinder and need to give up
+    // this reference on drop.
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
+            // know this pointer is safe to pass to `AIBinder_decStrong` here.
+            sys::AIBinder_decStrong(self.as_native_mut());
+        }
+    }
+}
+
+impl<T: AsNative<sys::AIBinder>> IBinder for T {
+    /// Perform a binder transaction
+    fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+        &self,
+        code: TransactionCode,
+        flags: TransactionFlags,
+        input_callback: F,
+    ) -> Result<Parcel> {
+        let mut input = ptr::null_mut();
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. It is safe to cast from an
+            // immutable pointer to a mutable pointer here, because
+            // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
+            // methods but the parameter is unfortunately not marked as const.
+            //
+            // After the call, input will be either a valid, owned `AParcel`
+            // pointer, or null.
+            sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
+        };
+        status_result(status)?;
+        let mut input = unsafe {
+            // Safety: At this point, `input` is either a valid, owned `AParcel`
+            // pointer, or null. `Parcel::owned` safely handles both cases,
+            // taking ownership of the parcel.
+            Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
+        };
+        input_callback(&mut input)?;
+        let mut reply = ptr::null_mut();
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. Although `IBinder::transact` is
+            // not a const method, it is still safe to cast our immutable
+            // pointer to mutable for the call. First, `IBinder::transact` is
+            // thread-safe, so concurrency is not an issue. The only way that
+            // `transact` can affect any visible, mutable state in the current
+            // process is by calling `onTransact` for a local service. However,
+            // in order for transactions to be thread-safe, this method must
+            // dynamically lock its data before modifying it. We enforce this
+            // property in Rust by requiring `Sync` for remotable objects and
+            // only providing `on_transact` with an immutable reference to
+            // `self`.
+            //
+            // This call takes ownership of the `input` parcel pointer, and
+            // passes ownership of the `reply` out parameter to its caller. It
+            // does not affect ownership of the `binder` parameter.
+            sys::AIBinder_transact(
+                self.as_native() as *mut sys::AIBinder,
+                code,
+                &mut input.into_raw(),
+                &mut reply,
+                flags,
+            )
+        };
+        status_result(status)?;
+
+        unsafe {
+            // Safety: `reply` is either a valid `AParcel` pointer or null
+            // after the call to `AIBinder_transact` above, so we can
+            // construct a `Parcel` out of it. `AIBinder_transact` passes
+            // ownership of the `reply` parcel to Rust, so we need to
+            // construct an owned variant. `Parcel::owned` takes ownership
+            // of the parcel pointer.
+            Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+        }
+    }
+
+    fn is_binder_alive(&self) -> bool {
+        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_isAlive(self.as_native())
+        }
+    }
+
+    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)
+    }
+
+    fn set_requesting_sid(&mut self, enable: bool) {
+        unsafe {
+            sys::AIBinder_setRequestingSid(self.as_native_mut(), enable)
+        };
+    }
+
+    fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
+        let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
+        let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
+            // file descriptor parameter is always be a valid open file. The
+            // `args` pointer parameter is a valid pointer to an array of C
+            // strings that will outlive the call since `args` lives for the
+            // whole function scope.
+            //
+            // This call does not affect ownership of its binder pointer
+            // parameter and does not take ownership of the file or args array
+            // parameters.
+            sys::AIBinder_dump(
+                self.as_native_mut(),
+                fp.as_raw_fd(),
+                arg_ptrs.as_mut_ptr(),
+                arg_ptrs.len().try_into().unwrap(),
+            )
+        };
+        status_result(status)
+    }
+
+    fn get_extension(&mut self) -> Result<Option<SpIBinder>> {
+        let mut out = ptr::null_mut();
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. After this call, the `out`
+            // parameter will be either null, or a valid pointer to an
+            // `AIBinder`.
+            //
+            // This call passes ownership of the out pointer to its caller
+            // (assuming it is set to a non-null value).
+            sys::AIBinder_getExtension(self.as_native_mut(), &mut out)
+        };
+        let ibinder = unsafe {
+            // Safety: The call above guarantees that `out` is either null or a
+            // valid, owned pointer to an `AIBinder`, both of which are safe to
+            // pass to `SpIBinder::from_raw`.
+            SpIBinder::from_raw(out)
+        };
+
+        status_result(status)?;
+        Ok(ibinder)
+    }
+
+    fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+        status_result(unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. `recipient` can always be
+            // converted into a valid pointer to an
+            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // cookie, although we depend on this value being set by
+            // `get_cookie` when the death recipient callback is called.
+            sys::AIBinder_linkToDeath(
+                self.as_native_mut(),
+                recipient.as_native_mut(),
+                recipient.get_cookie(),
+            )
+        })
+    }
+
+    fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+        status_result(unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`. `recipient` can always be
+            // converted into a valid pointer to an
+            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // cookie, although we depend on this value being set by
+            // `get_cookie` when the death recipient callback is called.
+            sys::AIBinder_unlinkToDeath(
+                self.as_native_mut(),
+                recipient.as_native_mut(),
+                recipient.get_cookie(),
+            )
+        })
+    }
+}
+
+impl Serialize for SpIBinder {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        parcel.write_binder(Some(self))
+    }
+}
+
+impl SerializeOption for SpIBinder {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        parcel.write_binder(this)
+    }
+}
+
+impl SerializeArray for SpIBinder {}
+impl SerializeArray for Option<&SpIBinder> {}
+
+impl Deserialize for SpIBinder {
+    fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
+        parcel
+            .read_binder()
+            .transpose()
+            .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+    }
+}
+
+impl DeserializeOption for SpIBinder {
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> {
+        parcel.read_binder()
+    }
+}
+
+impl DeserializeArray for SpIBinder {}
+impl DeserializeArray for Option<SpIBinder> {}
+
+/// A weak reference to a Binder remote object.
+///
+/// This struct encapsulates the C++ `wp<IBinder>` class. However, this wrapper
+/// is untyped, so properly typed versions implementing a particular binder
+/// interface should be crated with [`declare_binder_interface!`].
+pub struct WpIBinder(*mut sys::AIBinder_Weak);
+
+impl WpIBinder {
+    /// Create a new weak reference from an object that can be converted into a
+    /// raw `AIBinder` pointer.
+    pub fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
+        let ptr = unsafe {
+            // Safety: `SpIBinder` guarantees that `binder` always contains a
+            // valid pointer to an `AIBinder`.
+            sys::AIBinder_Weak_new(binder.as_native_mut())
+        };
+        assert!(!ptr.is_null());
+        Self(ptr)
+    }
+
+    /// Promote this weak reference to a strong reference to the binder object.
+    pub fn promote(&self) -> Option<SpIBinder> {
+        unsafe {
+            // Safety: `WpIBinder` always contains a valid weak reference, so we
+            // can pass this pointer to `AIBinder_Weak_promote`. Returns either
+            // null or an AIBinder owned by the caller, both of which are valid
+            // to pass to `SpIBinder::from_raw`.
+            let ptr = sys::AIBinder_Weak_promote(self.0);
+            SpIBinder::from_raw(ptr)
+        }
+    }
+}
+
+/// Rust wrapper around DeathRecipient objects.
+#[repr(C)]
+pub struct DeathRecipient {
+    recipient: *mut sys::AIBinder_DeathRecipient,
+    callback: Box<dyn Fn() + Send + 'static>,
+}
+
+impl DeathRecipient {
+    /// Create a new death recipient that will call the given callback when its
+    /// associated object dies.
+    pub fn new<F>(callback: F) -> DeathRecipient
+    where
+        F: Fn() + Send + 'static,
+    {
+        let callback = Box::new(callback);
+        let recipient = unsafe {
+            // Safety: The function pointer is a valid death recipient callback.
+            //
+            // This call returns an owned `AIBinder_DeathRecipient` pointer
+            // which must be destroyed via `AIBinder_DeathRecipient_delete` when
+            // no longer needed.
+            sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
+        };
+        DeathRecipient {
+            recipient,
+            callback,
+        }
+    }
+
+    /// Get the opaque cookie that identifies this death recipient.
+    ///
+    /// This cookie will be used to link and unlink this death recipient to a
+    /// binder object and will be passed to the `binder_died` callback as an
+    /// opaque userdata pointer.
+    fn get_cookie(&self) -> *mut c_void {
+        &*self.callback as *const _ as *mut c_void
+    }
+
+    /// Callback invoked from C++ when the binder object dies.
+    ///
+    /// # Safety
+    ///
+    /// The `cookie` parameter must have been created with the `get_cookie`
+    /// method of this object.
+    unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
+    where
+        F: Fn() + Send + 'static,
+    {
+        let callback = (cookie as *mut F).as_ref().unwrap();
+        callback();
+    }
+}
+
+/// # Safety
+///
+/// A `DeathRecipient` is always constructed with a valid raw pointer to an
+/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this
+/// pointer.
+unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient {
+    fn as_native(&self) -> *const sys::AIBinder_DeathRecipient {
+        self.recipient
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AIBinder_DeathRecipient {
+        self.recipient
+    }
+}
+
+impl Drop for DeathRecipient {
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: `self.recipient` is always a valid, owned
+            // `AIBinder_DeathRecipient` pointer returned by
+            // `AIBinder_DeathRecipient_new` when `self` was created. This
+            // delete method can only be called once when `self` is dropped.
+            sys::AIBinder_DeathRecipient_delete(self.recipient);
+        }
+    }
+}
+
+/// Generic interface to remote binder objects.
+///
+/// Corresponds to the C++ `BpInterface` class.
+pub trait Proxy: Sized + Interface {
+    /// The Binder interface descriptor string.
+    ///
+    /// This string is a unique identifier for a Binder interface, and should be
+    /// the same between all implementations of that interface.
+    fn get_descriptor() -> &'static str;
+
+    /// Create a new interface from the given proxy, if it matches the expected
+    /// type of this interface.
+    fn from_binder(binder: SpIBinder) -> Result<Self>;
+}
+
+/// # Safety
+///
+/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow
+/// invocation of `IBinder` methods directly from `Interface` objects. It shares
+/// the same safety as the implementation for `SpIBinder`.
+unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T {
+    fn as_native(&self) -> *const sys::AIBinder {
+        self.as_binder().as_native()
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+        self.as_binder().as_native_mut()
+    }
+}
+
+/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
+/// exist.
+pub fn get_service(name: &str) -> Option<SpIBinder> {
+    let name = CString::new(name).ok()?;
+    unsafe {
+        // Safety: `AServiceManager_getService` returns either a null pointer or
+        // a valid pointer to an owned `AIBinder`. Either of these values is
+        // safe to pass to `SpIBinder::from_raw`.
+        SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr()))
+    }
+}
+
+/// Retrieve an existing service for a particular interface, blocking for a few
+/// seconds if it doesn't yet exist.
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> {
+    let service = get_service(name);
+    match service {
+        Some(service) => FromIBinder::try_from(service),
+        None => Err(StatusCode::NAME_NOT_FOUND),
+    }
+}
+
+/// # Safety
+///
+/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
+/// `AIBinder`, so we can trivially extract this pointer here.
+unsafe impl AsNative<sys::AIBinder> for SpIBinder {
+    fn as_native(&self) -> *const sys::AIBinder {
+        self.0
+    }
+
+    fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+        self.0
+    }
+}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
new file mode 100644
index 0000000..0e05f10
--- /dev/null
+++ b/libs/binder/rust/src/state.rs
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::sys;
+
+use libc::{pid_t, uid_t};
+
+/// Static utility functions to manage Binder process state.
+pub struct ProcessState;
+
+impl ProcessState {
+    /// Start the Binder IPC thread pool
+    pub fn start_thread_pool() {
+        unsafe {
+            // Safety: Safe FFI
+            sys::ABinderProcess_startThreadPool();
+        }
+    }
+
+    /// Set the maximum number of threads that can be started in the threadpool.
+    ///
+    /// By default, after startThreadPool is called, this is 15. If it is called
+    /// additional times, it will only prevent the kernel from starting new
+    /// threads and will not delete already existing threads.
+    pub fn set_thread_pool_max_thread_count(num_threads: u32) {
+        unsafe {
+            // Safety: Safe FFI
+            sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads);
+        }
+    }
+
+    /// Block on the Binder IPC thread pool
+    pub fn join_thread_pool() {
+        unsafe {
+            // Safety: Safe FFI
+            sys::ABinderProcess_joinThreadPool();
+        }
+    }
+}
+
+/// Static utility functions to manage Binder thread state.
+pub struct ThreadState;
+
+impl ThreadState {
+    /// This returns the calling UID assuming that this thread is called from a
+    /// thread that is processing a binder transaction (for instance, in the
+    /// implementation of
+    /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+    ///
+    /// This can be used with higher-level system services to determine the
+    /// caller's identity and check permissions.
+    ///
+    /// Available since API level 29.
+    ///
+    /// \return calling uid or the current process's UID if this thread isn't
+    /// processing a transaction.
+    pub fn get_calling_uid() -> uid_t {
+        unsafe {
+            // Safety: Safe FFI
+            sys::AIBinder_getCallingUid()
+        }
+    }
+
+    /// This returns the calling PID assuming that this thread is called from a
+    /// thread that is processing a binder transaction (for instance, in the
+    /// implementation of
+    /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+    ///
+    /// This can be used with higher-level system services to determine the
+    /// caller's identity and check permissions. However, when doing this, one
+    /// should be aware of possible TOCTOU problems when the calling process
+    /// dies and is replaced with another process with elevated permissions and
+    /// the same PID.
+    ///
+    /// Available since API level 29.
+    ///
+    /// \return calling pid or the current process's PID if this thread isn't
+    /// processing a transaction.
+    ///
+    /// If the transaction being processed is a oneway transaction, then this
+    /// method will return 0.
+    pub fn get_calling_pid() -> pid_t {
+        unsafe {
+            // Safety: Safe FFI
+            sys::AIBinder_getCallingPid()
+        }
+    }
+
+    /// This function makes the client's security context available to the
+    /// service calling this function. This can be used for access control.
+    /// It does not suffer from the TOCTOU issues of get_calling_pid.
+    ///
+    /// Implementations of `check_permission` should use the given CStr
+    /// argument as context for selinux permission checks. If `None` is
+    /// given, the implementation should fall back to using the PID
+    /// instead.
+    ///
+    /// Note: `None` may be passed to the callback if the caller did not
+    /// `set_requesting_sid` on the serviced binder, or if the underlying
+    /// kernel is too old to support this feature.
+    pub fn with_calling_sid<T, F>(check_permission: F) -> T
+    where
+        for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T {
+        // Safety: AIBinder_getCallingSid returns a c-string pointer
+        // that is valid for a transaction. Also, the string returned
+        // is thread local. By restricting the lifetime of the CStr
+        // reference to the scope of the callback, we prevent it being
+        // used beyond the guaranteed lifetime.
+        check_permission(unsafe {
+            let sid = sys::AIBinder_getCallingSid();
+            // AIBinder_getCallingSid() returns a '\0' terminated string
+            // or NULL.
+            if sid.is_null() {
+                None
+            } else {
+                Some(std::ffi::CStr::from_ptr(sid))
+            }
+        })
+    }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
new file mode 100644
index 0000000..ef142b5
--- /dev/null
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_manager.h>
+#include <android/binder_parcel.h>
+#include <android/binder_parcel_platform.h>
+#include <android/binder_process.h>
+#include <android/binder_shell.h>
+#include <android/binder_status.h>
+
+namespace android {
+
+namespace c_interface {
+
+// Expose error codes from anonymous enum in binder_status.h
+enum StatusCode {
+    OK = STATUS_OK,
+    UNKNOWN_ERROR = STATUS_UNKNOWN_ERROR,
+    NO_MEMORY = STATUS_NO_MEMORY,
+    INVALID_OPERATION = STATUS_INVALID_OPERATION,
+    BAD_VALUE = STATUS_BAD_VALUE,
+    BAD_TYPE = STATUS_BAD_TYPE,
+    NAME_NOT_FOUND = STATUS_NAME_NOT_FOUND,
+    PERMISSION_DENIED = STATUS_PERMISSION_DENIED,
+    NO_INIT = STATUS_NO_INIT,
+    ALREADY_EXISTS = STATUS_ALREADY_EXISTS,
+    DEAD_OBJECT = STATUS_DEAD_OBJECT,
+    FAILED_TRANSACTION = STATUS_FAILED_TRANSACTION,
+    BAD_INDEX = STATUS_BAD_INDEX,
+    NOT_ENOUGH_DATA = STATUS_NOT_ENOUGH_DATA,
+    WOULD_BLOCK = STATUS_WOULD_BLOCK,
+    TIMED_OUT = STATUS_TIMED_OUT,
+    UNKNOWN_TRANSACTION = STATUS_UNKNOWN_TRANSACTION,
+    FDS_NOT_ALLOWED = STATUS_FDS_NOT_ALLOWED,
+    UNEXPECTED_NULL = STATUS_UNEXPECTED_NULL,
+};
+
+// Expose exception codes from anonymous enum in binder_status.h
+enum ExceptionCode {
+    NONE = EX_NONE,
+    SECURITY = EX_SECURITY,
+    BAD_PARCELABLE = EX_BAD_PARCELABLE,
+    ILLEGAL_ARGUMENT = EX_ILLEGAL_ARGUMENT,
+    NULL_POINTER = EX_NULL_POINTER,
+    ILLEGAL_STATE = EX_ILLEGAL_STATE,
+    NETWORK_MAIN_THREAD = EX_NETWORK_MAIN_THREAD,
+    UNSUPPORTED_OPERATION = EX_UNSUPPORTED_OPERATION,
+    SERVICE_SPECIFIC = EX_SERVICE_SPECIFIC,
+    PARCELABLE = EX_PARCELABLE,
+
+    /**
+     * This is special, and indicates to native binder proxies that the
+     * transaction has failed at a low level.
+     */
+    TRANSACTION_FAILED = EX_TRANSACTION_FAILED,
+};
+
+namespace consts {
+
+enum {
+    FIRST_CALL_TRANSACTION = FIRST_CALL_TRANSACTION,
+    LAST_CALL_TRANSACTION = LAST_CALL_TRANSACTION,
+};
+
+enum {
+    FLAG_ONEWAY = FLAG_ONEWAY,
+    FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
+};
+
+} // namespace consts
+
+} // namespace c_interface
+
+} // namespace android
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
new file mode 100644
index 0000000..9095af2
--- /dev/null
+++ b/libs/binder/rust/sys/lib.rs
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Generated Rust bindings to libbinder_ndk
+
+#![allow(
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unused,
+    improper_ctypes,
+    missing_docs
+)]
+use std::error::Error;
+use std::fmt;
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+impl Error for android_c_interface_StatusCode {}
+
+impl fmt::Display for android_c_interface_StatusCode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "StatusCode::{:?}", self)
+    }
+}
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
new file mode 100644
index 0000000..3db40ba
--- /dev/null
+++ b/libs/binder/rust/tests/Android.bp
@@ -0,0 +1,32 @@
+rust_test {
+    name: "rustBinderTest",
+    srcs: ["integration.rs"],
+    rustlibs: [
+        "libbinder_rs",
+        "libselinux_bindgen",
+    ],
+    shared_libs: [
+        "libselinux",
+    ],
+    // For the binaries to be pushed properly as specified in AndroidTest.xml,
+    // this cannot be the same as the module name.
+    stem: "rustBinderTestClientBinary",
+    test_suites: ["general-tests"],
+}
+
+rust_test {
+    name: "rustBinderTestService",
+    srcs: ["integration.rs"],
+    rustlibs: [
+        "libbinder_rs",
+        "liblibc",
+    ],
+    // For the binaries to be pushed properly as specified in AndroidTest.xml,
+    // this cannot be the same as the module name.
+    stem: "rustBinderTestServiceBinary",
+    test_harness: false,
+    // TODO(b/164473602): Remove this setting and add the module to `data`
+    // attribute of rustBinderTest.
+    auto_gen_config: false,
+    test_suites: ["general-tests"],
+}
diff --git a/libs/binder/ndk/tests/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml
similarity index 61%
copy from libs/binder/ndk/tests/AndroidTest.xml
copy to libs/binder/rust/tests/AndroidTest.xml
index 89646f7..d8d735d 100644
--- a/libs/binder/ndk/tests/AndroidTest.xml
+++ b/libs/binder/rust/tests/AndroidTest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,20 +13,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs binderVendorDoubleLoadTest.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-native" />
-
+<configuration description="Runs Binder Rust integration tests.">
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" />
+        <option name="push" value="rustBinderTestClientBinary->/data/local/tmp/rustBinderTest" />
+        <option name="push" value="rustBinderTestServiceBinary->/data/local/tmp/rustBinderTestService" />
     </target_preparer>
 
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/nativetest/vendor" />
-        <option name="module-name" value="binderVendorDoubleLoadTest" />
+    <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+        <option name="test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="rustBinderTest" />
     </test>
 </configuration>
-
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
new file mode 100644
index 0000000..953d328
--- /dev/null
+++ b/libs/binder/rust/tests/integration.rs
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust Binder crate integration tests
+
+use binder::declare_binder_interface;
+use binder::parcel::Parcel;
+use binder::{Binder, IBinder, Interface, SpIBinder, StatusCode, ThreadState, TransactionCode};
+use std::convert::{TryFrom, TryInto};
+
+/// Name of service runner.
+///
+/// Must match the binary name in Android.bp
+const RUST_SERVICE_BINARY: &str = "rustBinderTestService";
+
+/// Binary to run a test service.
+///
+/// This needs to be in a separate process from the tests, so we spawn this
+/// binary as a child, providing the service name as an argument.
+fn main() -> Result<(), &'static str> {
+    // Ensure that we can handle all transactions on the main thread.
+    binder::ProcessState::set_thread_pool_max_thread_count(0);
+    binder::ProcessState::start_thread_pool();
+
+    let mut args = std::env::args().skip(1);
+    if args.len() < 1 || args.len() > 2 {
+        print_usage();
+        return Err("");
+    }
+    let service_name = args.next().ok_or_else(|| {
+        print_usage();
+        "Missing SERVICE_NAME argument"
+    })?;
+    let extension_name = args.next();
+
+    {
+        let mut service = Binder::new(BnTest(Box::new(TestService {
+            s: service_name.clone(),
+        })));
+        service.set_requesting_sid(true);
+        if let Some(extension_name) = extension_name {
+            let extension = BnTest::new_binder(TestService { s: extension_name });
+            service
+                .set_extension(&mut extension.as_binder())
+                .expect("Could not add extension");
+        }
+        binder::add_service(&service_name, service.as_binder())
+            .expect("Could not register service");
+    }
+
+    binder::ProcessState::join_thread_pool();
+    Err("Unexpected exit after join_thread_pool")
+}
+
+fn print_usage() {
+    eprintln!(
+        "Usage: {} SERVICE_NAME [EXTENSION_NAME]",
+        RUST_SERVICE_BINARY
+    );
+    eprintln!(concat!(
+        "Spawn a Binder test service identified by SERVICE_NAME,",
+        " optionally with an extesion named EXTENSION_NAME",
+    ));
+}
+
+#[derive(Clone)]
+struct TestService {
+    s: String,
+}
+
+#[repr(u32)]
+enum TestTransactionCode {
+    Test = SpIBinder::FIRST_CALL_TRANSACTION,
+    GetSelinuxContext,
+}
+
+impl TryFrom<u32> for TestTransactionCode {
+    type Error = StatusCode;
+
+    fn try_from(c: u32) -> Result<Self, Self::Error> {
+        match c {
+            _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
+            _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
+                Ok(TestTransactionCode::GetSelinuxContext)
+            }
+            _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+        }
+    }
+}
+
+impl Interface for TestService {}
+
+impl ITest for TestService {
+    fn test(&self) -> binder::Result<String> {
+        Ok(self.s.clone())
+    }
+
+    fn get_selinux_context(&self) -> binder::Result<String> {
+        let sid =
+            ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
+        sid.ok_or(StatusCode::UNEXPECTED_NULL)
+    }
+}
+
+/// Trivial testing binder interface
+pub trait ITest: Interface {
+    /// Returns a test string
+    fn test(&self) -> binder::Result<String>;
+
+    /// Returns the caller's SELinux context
+    fn get_selinux_context(&self) -> binder::Result<String>;
+}
+
+declare_binder_interface! {
+    ITest["android.os.ITest"] {
+        native: BnTest(on_transact),
+        proxy: BpTest {
+            x: i32 = 100
+        },
+    }
+}
+
+fn on_transact(
+    service: &dyn ITest,
+    code: TransactionCode,
+    _data: &Parcel,
+    reply: &mut Parcel,
+) -> binder::Result<()> {
+    match code.try_into()? {
+        TestTransactionCode::Test => reply.write(&service.test()?),
+        TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+    }
+}
+
+impl ITest for BpTest {
+    fn test(&self) -> binder::Result<String> {
+        let reply =
+            self.binder
+                .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
+        reply.read()
+    }
+
+    fn get_selinux_context(&self) -> binder::Result<String> {
+        let reply = self.binder.transact(
+            TestTransactionCode::GetSelinuxContext as TransactionCode,
+            0,
+            |_| Ok(()),
+        )?;
+        reply.read()
+    }
+}
+
+impl ITest for Binder<BnTest> {
+    fn test(&self) -> binder::Result<String> {
+        self.0.test()
+    }
+
+    fn get_selinux_context(&self) -> binder::Result<String> {
+        self.0.get_selinux_context()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use selinux_bindgen as selinux_sys;
+    use std::ffi::CStr;
+    use std::fs::File;
+    use std::process::{Child, Command};
+    use std::ptr;
+    use std::sync::atomic::{AtomicBool, Ordering};
+    use std::sync::Arc;
+    use std::thread;
+    use std::time::Duration;
+
+    use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode};
+
+    use super::{ITest, RUST_SERVICE_BINARY};
+
+    pub struct ScopedServiceProcess(Child);
+
+    impl ScopedServiceProcess {
+        pub fn new(identifier: &str) -> Self {
+            Self::new_internal(identifier, None)
+        }
+
+        pub fn new_with_extension(identifier: &str, extension: &str) -> Self {
+            Self::new_internal(identifier, Some(extension))
+        }
+
+        fn new_internal(identifier: &str, extension: Option<&str>) -> Self {
+            let mut binary_path =
+                std::env::current_exe().expect("Could not retrieve current executable path");
+            binary_path.pop();
+            binary_path.push(RUST_SERVICE_BINARY);
+            let mut command = Command::new(&binary_path);
+            command.arg(identifier);
+            if let Some(ext) = extension {
+                command.arg(ext);
+            }
+            let child = command.spawn().expect("Could not start service");
+            Self(child)
+        }
+    }
+
+    impl Drop for ScopedServiceProcess {
+        fn drop(&mut self) {
+            self.0.kill().expect("Could not kill child process");
+            self.0
+                .wait()
+                .expect("Could not wait for child process to die");
+        }
+    }
+
+    #[test]
+    fn check_services() {
+        let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+        assert!(sm.is_binder_alive());
+        assert!(sm.ping_binder().is_ok());
+
+        assert!(binder::get_service("this_service_does_not_exist").is_none());
+        assert_eq!(
+            binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
+            Some(StatusCode::NAME_NOT_FOUND)
+        );
+
+        // The service manager service isn't an ITest, so this must fail.
+        assert_eq!(
+            binder::get_interface::<dyn ITest>("manager").err(),
+            Some(StatusCode::BAD_TYPE)
+        );
+    }
+
+    #[test]
+    fn trivial_client() {
+        let service_name = "trivial_client_test";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Box<dyn ITest> =
+            binder::get_interface(service_name).expect("Did not get manager binder service");
+        assert_eq!(test_client.test().unwrap(), "trivial_client_test");
+    }
+
+    #[test]
+    fn get_selinux_context() {
+        let service_name = "get_selinux_context";
+        let _process = ScopedServiceProcess::new(service_name);
+        let test_client: Box<dyn ITest> =
+            binder::get_interface(service_name).expect("Did not get manager binder service");
+        let expected_context = unsafe {
+            let mut out_ptr = ptr::null_mut();
+            assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+            assert!(!out_ptr.is_null());
+            CStr::from_ptr(out_ptr)
+        };
+        assert_eq!(
+            test_client.get_selinux_context().unwrap(),
+            expected_context.to_str().expect("context was invalid UTF-8"),
+        );
+    }
+
+    fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+        let binder_died = Arc::new(AtomicBool::new(false));
+
+        let mut death_recipient = {
+            let flag = binder_died.clone();
+            DeathRecipient::new(move || {
+                flag.store(true, Ordering::Relaxed);
+            })
+        };
+
+        binder
+            .link_to_death(&mut death_recipient)
+            .expect("link_to_death failed");
+
+        (binder_died, death_recipient)
+    }
+
+    /// Killing a remote service should unregister the service and trigger
+    /// death notifications.
+    #[test]
+    fn test_death_notifications() {
+        binder::ProcessState::start_thread_pool();
+
+        let service_name = "test_death_notifications";
+        let service_process = ScopedServiceProcess::new(service_name);
+        let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+        let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+        drop(service_process);
+        remote
+            .ping_binder()
+            .expect_err("Service should have died already");
+
+        // Pause to ensure any death notifications get delivered
+        thread::sleep(Duration::from_secs(1));
+
+        assert!(
+            binder_died.load(Ordering::Relaxed),
+            "Did not receive death notification"
+        );
+    }
+
+    /// Test unregistering death notifications.
+    #[test]
+    fn test_unregister_death_notifications() {
+        binder::ProcessState::start_thread_pool();
+
+        let service_name = "test_unregister_death_notifications";
+        let service_process = ScopedServiceProcess::new(service_name);
+        let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+        let (binder_died, mut recipient) = register_death_notification(&mut remote);
+
+        remote
+            .unlink_to_death(&mut recipient)
+            .expect("Could not unlink death notifications");
+
+        drop(service_process);
+        remote
+            .ping_binder()
+            .expect_err("Service should have died already");
+
+        // Pause to ensure any death notifications get delivered
+        thread::sleep(Duration::from_secs(1));
+
+        assert!(
+            !binder_died.load(Ordering::Relaxed),
+            "Received unexpected death notification after unlinking",
+        );
+    }
+
+    /// Dropping a remote handle should unregister any death notifications.
+    #[test]
+    fn test_death_notification_registration_lifetime() {
+        binder::ProcessState::start_thread_pool();
+
+        let service_name = "test_death_notification_registration_lifetime";
+        let service_process = ScopedServiceProcess::new(service_name);
+        let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+        let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+        // This should automatically unregister our death notification.
+        drop(remote);
+
+        drop(service_process);
+
+        // Pause to ensure any death notifications get delivered
+        thread::sleep(Duration::from_secs(1));
+
+        // We dropped the remote handle, so we should not receive the death
+        // notification when the remote process dies here.
+        assert!(
+            !binder_died.load(Ordering::Relaxed),
+            "Received unexpected death notification after dropping remote handle"
+        );
+    }
+
+    /// Test IBinder interface methods not exercised elsewhere.
+    #[test]
+    fn test_misc_ibinder() {
+        let service_name = "rust_test_ibinder";
+
+        {
+            let _process = ScopedServiceProcess::new(service_name);
+
+            let mut remote = binder::get_service(service_name);
+            assert!(remote.is_binder_alive());
+            remote.ping_binder().expect("Could not ping remote service");
+
+            // We're not testing the output of dump here, as that's really a
+            // property of the C++ implementation. There is the risk that the
+            // method just does nothing, but we don't want to depend on any
+            // particular output from the underlying library.
+            let null_out = File::open("/dev/null").expect("Could not open /dev/null");
+            remote
+                .dump(&null_out, &[])
+                .expect("Could not dump remote service");
+        }
+
+        // get/set_extensions is tested in test_extensions()
+
+        // transact is tested everywhere else, and we can't make raw
+        // transactions outside the [FIRST_CALL_TRANSACTION,
+        // LAST_CALL_TRANSACTION] range from the NDK anyway.
+
+        // link_to_death is tested in test_*_death_notification* tests.
+    }
+
+    #[test]
+    fn test_extensions() {
+        let service_name = "rust_test_extensions";
+        let extension_name = "rust_test_extensions_ext";
+
+        {
+            let _process = ScopedServiceProcess::new(service_name);
+
+            let mut remote = binder::get_service(service_name);
+            assert!(remote.is_binder_alive());
+
+            let extension = remote
+                .get_extension()
+                .expect("Could not check for an extension");
+            assert!(extension.is_none());
+        }
+
+        {
+            let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name);
+
+            let mut remote = binder::get_service(service_name);
+            assert!(remote.is_binder_alive());
+
+            let maybe_extension = remote
+                .get_extension()
+                .expect("Could not check for an extension");
+
+            let extension = maybe_extension.expect("Remote binder did not have an extension");
+
+            let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+                .expect("Extension could not be converted to the expected interface");
+
+            assert_eq!(extension.test().unwrap(), extension_name);
+        }
+    }
+}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 2680e84..87f1d45 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,23 @@
     require_root: true,
 }
 
+// unit test only, which can run on host and doesn't use /dev/binder
+cc_test {
+    name: "binderParcelTest",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    srcs: ["binderParcelTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
+
 cc_test {
     name: "binderLibTest",
     defaults: ["binder_test_defaults"],
@@ -155,6 +172,7 @@
         "binderStabilityTest.cpp",
     ],
 
+    // critical that libbinder/libbinder_ndk are shared for VTS
     shared_libs: [
         "libbinder_ndk",
         "libbinder",
@@ -166,7 +184,7 @@
         "binderStabilityTestIface-ndk_platform",
     ],
 
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts"],
     require_root: true,
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 40de2db..ad4729d 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fstream>
 #include <poll.h>
 #include <pthread.h>
 #include <stdio.h>
@@ -50,6 +51,9 @@
 static char *binderserversuffix;
 static char binderserverarg[] = "--binderserver";
 
+static constexpr int kSchedPolicy = SCHED_RR;
+static constexpr int kSchedPriority = 7;
+
 static String16 binderLibTestServiceName = String16("test.binderLib");
 
 enum BinderLibTestTranscationCode {
@@ -75,6 +79,9 @@
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
     BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
     BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
+    BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
+    BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
+    BINDER_LIB_TEST_GETPID,
     BINDER_LIB_TEST_ECHO_VECTOR,
     BINDER_LIB_TEST_REJECT_BUF,
 };
@@ -395,6 +402,57 @@
     EXPECT_EQ(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, NopTransactionClear) {
+    status_t ret;
+    Parcel data, reply;
+    // make sure it accepts the transaction flag
+    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, Freeze) {
+    status_t ret;
+    Parcel data, reply, replypid;
+    std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+
+    //Pass test on devices where the freezer is not supported
+    if (freezer_file.fail()) {
+        GTEST_SKIP();
+        return;
+    }
+
+    std::string freezer_enabled;
+    std::getline(freezer_file, freezer_enabled);
+
+    //Pass test on devices where the freezer is disabled
+    if (freezer_enabled != "1") {
+        GTEST_SKIP();
+        return;
+    }
+
+    ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+    int32_t pid = replypid.readInt32();
+    EXPECT_EQ(NO_ERROR, ret);
+    for (int i = 0; i < 10; i++) {
+        EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
+    }
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+    EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+
+    bool sync_received, async_received;
+
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
+                &async_received));
+
+    EXPECT_EQ(sync_received, 1);
+    EXPECT_EQ(async_received, 0);
+
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+    EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+}
+
 TEST_F(BinderLibTest, SetError) {
     int32_t testValue[] = { 0, -123, 123 };
     for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
@@ -1015,6 +1073,22 @@
     EXPECT_EQ(NO_ERROR, ret2);
 }
 
+TEST_F(BinderLibTest, SchedPolicySet) {
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    Parcel data, reply;
+    status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
+    EXPECT_EQ(NO_ERROR, ret);
+
+    int policy = reply.readInt32();
+    int priority = reply.readInt32();
+
+    EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK));
+    EXPECT_EQ(kSchedPriority, priority);
+}
+
+
 TEST_F(BinderLibTest, VectorSent) {
     Parcel data, reply;
     sp<IBinder> server = addServer();
@@ -1158,6 +1232,12 @@
                 pthread_mutex_unlock(&m_serverWaitMutex);
                 return ret;
             }
+            case BINDER_LIB_TEST_GETPID:
+                reply->writeInt32(getpid());
+                return NO_ERROR;
+            case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT:
+                usleep(5000);
+                return NO_ERROR;
             case BINDER_LIB_TEST_NOP_TRANSACTION:
                 return NO_ERROR;
             case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
@@ -1332,6 +1412,16 @@
                 reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_SCHEDULING_POLICY: {
+                int policy = 0;
+                sched_param param;
+                if (0 != pthread_getschedparam(pthread_self(), &policy, &param)) {
+                    return UNKNOWN_ERROR;
+                }
+                reply->writeInt32(policy);
+                reply->writeInt32(param.sched_priority);
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_ECHO_VECTOR: {
                 std::vector<uint64_t> vector;
                 auto err = data.readUint64Vector(&vector);
@@ -1368,6 +1458,8 @@
     {
         sp<BinderLibTestService> testService = new BinderLibTestService(index);
 
+        testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority);
+
         /*
          * Normally would also contain functionality as well, but we are only
          * testing the extension mechanism.
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
new file mode 100644
index 0000000..1764228
--- /dev/null
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+
+using android::IPCThreadState;
+using android::OK;
+using android::Parcel;
+using android::String16;
+using android::String8;
+using android::status_t;
+
+// Tests a second operation results in a parcel at the same location as it
+// started.
+void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
+    Parcel p;
+    a(&p);
+    size_t end = p.dataPosition();
+    p.setDataPosition(0);
+    b(&p);
+    EXPECT_EQ(end, p.dataPosition());
+}
+
+TEST(Parcel, InverseInterfaceToken) {
+    const String16 token = String16("asdf");
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeInterfaceToken(token);
+    }, [&] (Parcel* p) {
+        EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+    });
+}
+
+TEST(Parcel, Utf8FromUtf16Read) {
+    const char* token = "asdf";
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeString16(String16(token));
+    }, [&] (Parcel* p) {
+        std::string s;
+        EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+        EXPECT_EQ(token, s);
+    });
+}
+
+TEST(Parcel, Utf8AsUtf16Write) {
+    std::string token = "asdf";
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeUtf8AsUtf16(token);
+    }, [&] (Parcel* p) {
+        String16 s;
+        EXPECT_EQ(OK, p->readString16(&s));
+        EXPECT_EQ(s, String16(token.c_str()));
+    });
+}
+
+template <typename T>
+using readFunc = status_t (Parcel::*)(T* out) const;
+template <typename T>
+using writeFunc = status_t (Parcel::*)(const T& in);
+template <typename T>
+using copyWriteFunc = status_t (Parcel::*)(T in);
+
+template <typename T, typename WRITE_FUNC>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
+    for (const T& value : ts) {
+        parcelOpSameLength([&] (Parcel* p) {
+            (*p.*w)(value);
+        }, [&] (Parcel* p) {
+            T outValue;
+            EXPECT_EQ(OK, (*p.*r)(&outValue));
+            EXPECT_EQ(value, outValue);
+        });
+    }
+}
+
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
+    readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
+}
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
+    readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
+}
+
+#define TEST_READ_WRITE_INVERSE(type, name, ...) \
+    TEST(Parcel, Inverse##name) { \
+        readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
+    }
+
+TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
+TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
+TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
+TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
+TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
+TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
+TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..c465bed
--- /dev/null
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "binder_fuzz_defaults",
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libbase",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    }
+}
+
+cc_fuzz {
+    name: "binder_binderFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["BinderFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_bpBinderFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    host_supported: false,
+    srcs: ["BpBinderFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_persistableBundleFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["PersistableBundleFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_stabilityFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["StabilityFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_statusFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["StatusFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_textOutputFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["TextOutputFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/fuzzers/BinderFuzz.cpp
new file mode 100644
index 0000000..1e5d80a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzz.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    sp<BBinder> bbinder = new BBinder();
+
+    // To prevent memory from running out from calling too many add item operations.
+    const uint32_t MAX_RUNS = 2048;
+    uint32_t count = 0;
+
+    while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+        if (fdp.ConsumeBool()) {
+            callArbitraryFunction(&fdp, gBBinderOperations, bbinder);
+        } else {
+            callArbitraryFunction(&fdp, gIBinderOperations,
+                                  reinterpret_cast<IBinder *>(bbinder.get()));
+        }
+    }
+
+    return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
new file mode 100644
index 0000000..9ac65bb
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <stdint.h>
+#include <atomic>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BBinder>&)>>
+        gBBinderOperations = {[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  bbinder->isRequestingSid();
+                              },
+                              [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+                                  bool request_sid = fdp->ConsumeBool();
+                                  bbinder->setRequestingSid(request_sid);
+                              },
+                              [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  bbinder->getExtension();
+                              },
+                              [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  static IBinder* extension = nullptr;
+                                  bbinder->setExtension(extension);
+                              },
+                              [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+                                  int priority;
+                                  int policy = fdp->ConsumeIntegralInRange<int>(0, 2);
+                                  if (policy == 0) {
+                                      priority = fdp->ConsumeIntegralInRange<int>(-20, 19);
+                                  } else {
+                                      priority = fdp->ConsumeIntegralInRange<int>(1, 99);
+                                  }
+                                  bbinder->setMinSchedulerPolicy(policy, priority);
+                              },
+                              [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  bbinder->getMinSchedulerPolicy();
+                              },
+                              [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  bbinder->getMinSchedulerPriority();
+                              },
+                              [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+                                  bbinder->getDebugPid();
+                              }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
new file mode 100644
index 0000000..c50279b
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BpBinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
+    // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
+    // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
+    // crashes that are not easy to diagnose.
+    int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
+    sp<BpBinder> bpbinder = BpBinder::create(handle);
+    if (bpbinder == nullptr) return 0;
+
+    // To prevent memory from running out from calling too many add item operations.
+    const uint32_t MAX_RUNS = 2048;
+    uint32_t count = 0;
+    sp<IBinder::DeathRecipient> s_recipient = new FuzzDeathRecipient();
+
+    while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+        if (fdp.ConsumeBool()) {
+            callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+        } else {
+            callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+        }
+    }
+
+    return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
new file mode 100644
index 0000000..c685b41
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// Static variable to reference so we don't consume a bunch of memory to link and
+// unlink DeathRecipients.
+static int8_t kBpBinderCookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
+                                            const sp<IBinder::DeathRecipient>&)>>
+        gBPBinderOperations =
+                {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
+                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>& s_recipient) -> void {
+                     // Clean up possible leftover memory.
+                     wp<IBinder::DeathRecipient> outRecipient(nullptr);
+                     bpbinder->sendObituary();
+                     bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
+                                             &outRecipient);
+
+                     uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+                     kBpBinderCookie = fdp->ConsumeIntegral<int8_t>();
+                     bpbinder->linkToDeath(s_recipient.get(),
+                                           reinterpret_cast<void*>(&kBpBinderCookie), flags);
+                 },
+                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     wp<IBinder::DeathRecipient> out_recipient(nullptr);
+                     uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+                     int8_t random_cookie = fdp->ConsumeIntegral<int8_t>();
+                     bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&random_cookie),
+                                             flags, &out_recipient);
+                 },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
+                     bpbinder->getBinderProxyCount(uid);
+                 },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->enableCountByUid(); },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->disableCountByUid(); },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     Vector<uint32_t> uids;
+                     Vector<uint32_t> counts;
+                     bpbinder->getCountByUid(uids, counts);
+                 },
+                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     bool enable = fdp->ConsumeBool();
+                     bpbinder->setCountByUidEnabled(enable);
+                 },
+                 [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     binder_proxy_limit_callback cb = binder_proxy_limit_callback();
+                     bpbinder->setLimitCallback(cb);
+                 },
+                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     int high = fdp->ConsumeIntegral<int>();
+                     int low = fdp->ConsumeIntegral<int>();
+                     bpbinder->setBinderProxyCountWatermarks(high, low);
+                 }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
new file mode 100644
index 0000000..626b758
--- /dev/null
+++ b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FuzzDeathRecipient : public IBinder::DeathRecipient {
+private:
+    virtual void binderDied(const wp<IBinder>& who) { (void)who; };
+};
+
+// Allow objects to be attached that aren't stack locals
+static uint32_t objectID = 0;
+static uint32_t object = 0;
+static uint32_t cleanup_cookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, IBinder*)>> gIBinderOperations =
+        {[](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->getInterfaceDescriptor(); },
+         [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->isBinderAlive(); },
+         [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->pingBinder(); },
+         [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+             int fd = STDOUT_FILENO;
+             std::string rand_str = fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+             Vector<String16> args;
+             args.push(String16(rand_str.c_str()));
+             ibinder->dump(fd, args);
+         },
+         [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+             objectID = fdp->ConsumeIntegral<uint32_t>();
+             object = fdp->ConsumeIntegral<uint32_t>();
+             cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
+             IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
+             ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+                                                      : nullptr,
+                                   fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
+                                   fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
+                                                      : nullptr,
+                                   func);
+         },
+         [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+             uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+             ibinder->findObject(reinterpret_cast<void*>(&id));
+         },
+         [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+             uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+             ibinder->detachObject(reinterpret_cast<void*>(&id));
+         },
+         [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+             uint32_t code = fdp->ConsumeIntegral<uint32_t>();
+             Parcel p_data;
+             Parcel reply;
+             uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+             ibinder->transact(code, p_data, &reply, flags);
+         }};
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
new file mode 100644
index 0000000..4843c46
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <PersistableBundleFuzzFunctions.h>
+#include <binder/PersistableBundle.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    std::shared_ptr<os::PersistableBundle> p_bundle(new os::PersistableBundle());
+
+    while (fdp.remaining_bytes() > 0) {
+        String16 key(fdp.ConsumeRandomLengthString(fdp.remaining_bytes()).c_str());
+        callArbitraryFunction(&fdp, gPersistableBundleOperations, p_bundle, &key);
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
new file mode 100644
index 0000000..820e9e8
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<
+        void(FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const&, String16*)>>
+        gPersistableBundleOperations =
+                {[](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16*) -> void { p_bundle->empty(); },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16*) -> void {
+                     Parcel parcel;
+                     p_bundle->writeToParcel(&parcel);
+                 },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16*) -> void {
+                     Parcel parcel;
+                     std::vector<uint8_t> buf = fdp->ConsumeBytes<uint8_t>(
+                             fdp->ConsumeIntegralInRange<size_t>(0, fdp->remaining_bytes() - 1));
+                     parcel.write(buf.data(), buf.size());
+                     p_bundle->readFromParcel(&parcel);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16*) -> void { p_bundle->size(); },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void { p_bundle->erase(*key); },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     bool value = fdp->ConsumeBool();
+                     p_bundle->putBoolean(*key, value);
+                 },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     int32_t value = fdp->ConsumeIntegral<int32_t>();
+                     p_bundle->putInt(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     os::PersistableBundle value = os::PersistableBundle();
+                     p_bundle->putPersistableBundle(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<String16> value;
+                     p_bundle->putStringVector(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<double> value;
+                     p_bundle->putDoubleVector(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<int64_t> value;
+                     p_bundle->putLongVector(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<int32_t> value;
+                     p_bundle->putIntVector(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<bool> value;
+                     p_bundle->putBooleanVector(*key, value);
+                 },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     String16 value(fdp->ConsumeRandomLengthString(fdp->remaining_bytes()).c_str());
+                     p_bundle->putString(*key, value);
+                 },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     int64_t value = fdp->ConsumeIntegral<int64_t>();
+                     p_bundle->putLong(*key, value);
+                 },
+                 [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     double value = fdp->ConsumeFloatingPoint<double>();
+                     p_bundle->putDouble(*key, value);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     bool out;
+                     p_bundle->getBoolean(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     os::PersistableBundle out;
+                     p_bundle->getPersistableBundle(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<String16> out;
+                     p_bundle->getStringVector(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<double> out;
+                     p_bundle->getDoubleVector(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<int64_t> out;
+                     p_bundle->getLongVector(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<int32_t> out;
+                     p_bundle->getIntVector(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     std::vector<bool> out;
+                     p_bundle->getBooleanVector(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     String16 out;
+                     p_bundle->getString(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     double out;
+                     p_bundle->getDouble(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     int64_t out;
+                     p_bundle->getLong(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16* key) -> void {
+                     int32_t out;
+                     p_bundle->getInt(*key, &out);
+                 },
+                 [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+                    String16*) -> void {
+                     p_bundle->getBooleanKeys();
+                     p_bundle->getIntKeys();
+                     p_bundle->getLongKeys();
+                     p_bundle->getDoubleKeys();
+                     p_bundle->getStringKeys();
+                     p_bundle->getBooleanVectorKeys();
+                     p_bundle->getIntVectorKeys();
+                     p_bundle->getLongVectorKeys();
+                     p_bundle->getDoubleVectorKeys();
+                     p_bundle->getStringVectorKeys();
+                     p_bundle->getPersistableBundleKeys();
+                 }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
new file mode 100644
index 0000000..8ad9b44
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <StabilityFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    // Init our wrapper
+    FuzzedDataProvider dataProvider(data, size);
+    android::sp<android::IBinder> bbinder = new android::BBinder();
+
+    // Call some functions
+    while (dataProvider.remaining_bytes() > 0) {
+        callArbitraryFunction(&dataProvider, gStabilityOperations, bbinder);
+    }
+
+    return 0;
+}
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
new file mode 100644
index 0000000..8b4ed70
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Binder.h>
+#include <binder/Stability.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#define STABILITY_MAX_TAG_LENGTH 2048
+static bool marked = false;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<
+        std::function<void(FuzzedDataProvider*, android::sp<android::IBinder> const&)>>
+        gStabilityOperations = {
+                // markCompilationUnit(IBinder* binder)
+                [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+                    if (!marked) {
+                        android::internal::Stability::markCompilationUnit(bbinder.get());
+                        marked = true;
+                    }
+                },
+
+                // markVintf(IBinder* binder)
+                [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+                    if (!marked) {
+                        android::internal::Stability::markVintf(bbinder.get());
+                        marked = true;
+                    }
+                },
+
+                // debugLogStability(const std::string& tag, const sp<IBinder>& binder)
+                [](FuzzedDataProvider* fdp, android::sp<android::IBinder> const& bbinder) -> void {
+                    std::string tag = fdp->ConsumeRandomLengthString(STABILITY_MAX_TAG_LENGTH);
+                    android::internal::Stability::debugLogStability(tag, bbinder);
+                },
+
+                // markVndk(IBinder* binder)
+                [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+                    if (!marked) {
+                        android::internal::Stability::markVndk(bbinder.get());
+                        marked = true;
+                    }
+                },
+
+                // requiresVintfDeclaration(const sp<IBinder>& binder)
+                [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+                    android::internal::Stability::requiresVintfDeclaration(bbinder);
+                }};
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/fuzzers/StatusFuzz.cpp
new file mode 100644
index 0000000..4f6ad6f
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzz.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <StatusFuzzFunctions.h>
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    int32_t exceptionCode = fdp.ConsumeIntegral<int32_t>();
+    std::string message_str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+    String8 message(message_str.c_str());
+
+    Parcel parcel;
+    std::vector<uint8_t> buf = fdp.ConsumeBytes<uint8_t>(
+            fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes() - 1));
+    parcel.write(buf.data(), buf.size());
+    binder::Status status = binder::Status::fromExceptionCode(exceptionCode, message);
+
+    while (fdp.remaining_bytes() > 0) {
+        callArbitraryFunction(&fdp, gStatusOperations, &status, &parcel);
+    }
+    return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
new file mode 100644
index 0000000..bc8d17a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <stdio.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+/* This is a vector of lambda functions the fuzzer will pull from.
+ *  This is done so new functions can be added to the fuzzer easily
+ *  without requiring modifications to the main fuzzer file. This also
+ *  allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, binder::Status*, Parcel*)>>
+        gStatusOperations = {
+                [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+                    parcel->setDataPosition(0);
+                    status->readFromParcel(*parcel);
+                },
+                [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+                    status->writeToParcel(parcel);
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    std::string message_str =
+                            fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+                    String8 message(message_str.c_str());
+                    status->setServiceSpecificError(fdp->ConsumeIntegral<int32_t>(), message);
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    std::string message_str =
+                            fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+                    String8 message(message_str.c_str());
+                    status->setException(fdp->ConsumeIntegral<int32_t>(), message);
+                },
+                [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void { status->ok(); },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    std::string message_str =
+                            fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+                    String8 message(message_str.c_str());
+                    *status = binder::Status::fromExceptionCode(fdp->ConsumeIntegral<int32_t>(),
+                                                                message);
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    *status = binder::Status::fromServiceSpecificError(
+                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [](FuzzedDataProvider* fdp, binder::Status*, Parcel*) -> void {
+                    binder::Status::exceptionToString(fdp->ConsumeIntegral<int32_t>());
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    std::string message_str =
+                            fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+                    String8 message(message_str.c_str());
+                    *status = binder::Status::fromServiceSpecificError(fdp->ConsumeIntegral<
+                                                                               int32_t>(),
+                                                                       message);
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    *status = binder::Status::fromStatusT(fdp->ConsumeIntegral<status_t>());
+                },
+                [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+                    status->setFromStatusT(fdp->ConsumeIntegral<status_t>());
+                },
+                [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void {
+                    std::stringstream ss;
+                    ss << *status;
+                },
+};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
new file mode 100644
index 0000000..c950020
--- /dev/null
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Debug.h>
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstddef>
+#include <limits>
+
+// Fuzzer for the TextOutput class. These were lifted from the existing
+// test suite.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    CapturedStderr cap;
+
+    while (fdp.remaining_bytes() > 1) {
+        switch (fdp.ConsumeIntegral<uint8_t>() % 3) {
+            case 0: {
+                std::string input = fdp.ConsumeBytesAsString(
+                        fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()));
+                android::aerr << input << android::endl;
+                break;
+            }
+            case 1: {
+                std::string str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+                android::HexDump input(str.c_str(), sizeof(str.c_str()));
+                android::aerr << input << android::endl;
+                break;
+            }
+            case 2: {
+                android::TypeCode input(fdp.ConsumeIntegral<uint32_t>());
+                android::aerr << input << android::endl;
+            }
+        }
+    }
+    cap.Stop();
+
+    return 0;
+}
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
new file mode 100644
index 0000000..d58d9b6
--- /dev/null
+++ b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+// Calls a function from the ops_vector
+template <class F, class T, class... Types>
+void callArbitraryFunction(F* fdp, T const& ops_vector, Types... args) {
+    // Choose which function we'll be calling
+    uint8_t function_id = fdp->template ConsumeIntegralInRange<uint8_t>(0, ops_vector.size() - 1);
+
+    // Call the function we've chosen
+    ops_vector[function_id](fdp, args...);
+}
+
+template <class T>
+T getArbitraryVectorElement(FuzzedDataProvider* fdp, std::vector<T> const& vect, bool allow_null) {
+    // If we're allowing null, give it a 50:50 shot at returning a nullptr
+    if (vect.empty() || (allow_null && fdp->ConsumeBool())) {
+        return nullptr;
+    }
+
+    // Otherwise, return an element from our vector
+    return vect.at(fdp->ConsumeIntegralInRange<size_t>(0, vect.size() - 1));
+}
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 88752ee..08c62df 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -19,6 +19,11 @@
     double_loadable: true,
     vendor_available: true,
     host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
 
     shared_libs: [
         "libbinder",
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index 68cc225..44e2fd1 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -165,7 +165,6 @@
     android::ProcessState::self()->startThreadPool();
 
     // HIDL
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
     android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
     sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
     CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str()));
@@ -176,7 +175,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+    android::hardware::details::setTrebleTestingOverride(true);
     if (fork() == 0) {
         prctl(PR_SET_PDEATHSIG, SIGHUP);
         return server(kP1Id, kP2Id);
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 0b77ab3..6058430 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -88,16 +88,6 @@
     return policyN1 - policyN2;
 }
 
-static int bpf_obj_get_wronly(const char *pathname) {
-    union bpf_attr attr;
-
-    memset(&attr, 0, sizeof(attr));
-    attr.pathname = ptr_to_u64((void *)pathname);
-    attr.file_flags = BPF_F_WRONLY;
-
-    return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
-}
-
 static bool initGlobals() {
     std::lock_guard<std::mutex> guard(gInitializedMutex);
     if (gInitialized) return true;
@@ -156,7 +146,7 @@
 static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
     std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
                                     eventType.c_str(), eventName.c_str());
-    int prog_fd = bpfFdGet(path.c_str(), BPF_F_RDONLY);
+    int prog_fd = retrieveProgram(path.c_str());
     if (prog_fd < 0) return false;
     return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
 }
@@ -183,7 +173,7 @@
     if (!initGlobals()) return false;
     if (gTracking) return true;
 
-    unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
+    unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
     if (cpuPolicyFd < 0) return false;
 
     for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) {
@@ -192,7 +182,7 @@
         }
     }
 
-    unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
+    unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
     if (freqToIdxFd < 0) return false;
     freq_idx_key_t key;
     for (uint32_t i = 0; i < gNPolicies; ++i) {
@@ -207,23 +197,23 @@
         }
     }
 
-    unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
+    unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
     if (cpuLastUpdateFd < 0) return false;
     std::vector<uint64_t> zeros(get_nprocs_conf(), 0);
     uint32_t zero = 0;
     if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false;
 
-    unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map"));
+    unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map"));
     if (nrActiveFd < 0) return false;
     if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false;
 
-    unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
+    unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
     if (policyNrActiveFd < 0) return false;
     for (uint32_t i = 0; i < gNPolicies; ++i) {
         if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false;
     }
 
-    unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
+    unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
     if (policyFreqIdxFd < 0) return false;
     for (uint32_t i = 0; i < gNPolicies; ++i) {
         auto freqIdx = getPolicyFreqIdx(i);
@@ -261,7 +251,7 @@
     for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
         key.bucket = i;
         if (findMapEntry(gTisMapFd, &key, vals.data())) {
-            if (errno != ENOENT) return {};
+            if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
             continue;
         }
 
@@ -372,7 +362,7 @@
     time_key_t key = {.uid = uid};
     for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
         if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
-            if (errno != ENOENT) return {};
+            if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
             continue;
         }
         auto offset = key.bucket * CPUS_PER_ENTRY;
@@ -435,6 +425,7 @@
 
     uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
     do {
+        if (key.bucket > (gNCpus - 1) / CPUS_PER_ENTRY) return {};
         if (lastUpdate) {
             auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
             if (!uidUpdated.has_value()) return {};
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index ea2a200..0d5f412 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -387,6 +387,28 @@
     }
 }
 
+TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
+    uint32_t uid = 0;
+    {
+        // Find an unused UID
+        auto map = getUidsConcurrentTimes();
+        ASSERT_TRUE(map.has_value());
+        ASSERT_FALSE(map->empty());
+        for (const auto &kv : *map) uid = std::max(uid, kv.first);
+        ++uid;
+    }
+    android::base::unique_fd fd{
+        bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+    ASSERT_GE(fd, 0);
+    uint32_t nCpus = get_nprocs_conf();
+    uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
+    time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
+    std::vector<concurrent_val_t> vals(nCpus);
+    ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
+    EXPECT_FALSE(getUidsConcurrentTimes().has_value());
+    ASSERT_FALSE(deleteMapEntry(fd, &key));
+}
+
 TEST(TimeInStateTest, AllUidTimesConsistent) {
     auto tisMap = getUidsCpuFreqTimes();
     ASSERT_TRUE(tisMap.has_value());
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 2e14408..143fa13 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -33,6 +33,7 @@
         "/system/bin/mediaextractor", // media.extractor
         "/system/bin/mediametrics", // media.metrics
         "/system/bin/mediaserver",
+        "/system/bin/mediatranscoding", // media.transcoding
         "/system/bin/netd",
         "/system/bin/sdcard",
         "/apex/com.android.os.statsd/bin/statsd",
@@ -56,6 +57,7 @@
         "android.hardware.audio@4.0::IDevicesFactory",
         "android.hardware.audio@5.0::IDevicesFactory",
         "android.hardware.audio@6.0::IDevicesFactory",
+        "android.hardware.audio@7.0::IDevicesFactory",
         "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
         "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
         "android.hardware.automotive.evs@1.0::IEvsCamera",
@@ -86,7 +88,7 @@
 
 static void read_extra_hals_to_dump_from_property() {
     // extra hals to dump are already filled
-    if (extra_hal_interfaces_to_dump.size() > 0) {
+    if (!extra_hal_interfaces_to_dump.empty()) {
         return;
     }
     std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 6909637..76518c1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -9,6 +9,11 @@
         "libbinder",
         "libutils",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 cc_library {
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6964324..4ecbe53 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -61,4 +61,16 @@
     return mNameToService.find(name) != mNameToService.end();
 }
 
+Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+    Vector<String16> out;
+    const String16 prefix = name + String16("/");
+    for (const auto& [registeredName, service] : mNameToService) {
+        (void) service;
+        if (registeredName.startsWith(prefix)) {
+            out.add(String16(registeredName.string() + prefix.size()));
+        }
+    }
+    return out;
+}
+
 }  // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 62311d4..4ef47fb 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -30,42 +30,23 @@
 public:
     ServiceManager();
 
-    /**
-     * Equivalent of checkService.
-     */
     sp<IBinder> getService( const String16& name) const override;
 
-    /**
-     * Retrieve an existing service, non-blocking.
-     */
     sp<IBinder> checkService( const String16& name) const override;
 
-    /**
-     * Register a service.
-     */
     status_t addService(const String16& name, const sp<IBinder>& service,
                         bool allowIsolated = false,
                         int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
 
-    /**
-     * Return list of all existing services.
-     */
     Vector<String16> listServices(int dumpsysFlags = 0) override;
 
     IBinder* onAsBinder() override;
 
-    /**
-     * Effectively no-oped in this implementation - equivalent to checkService.
-     */
     sp<IBinder> waitForService(const String16& name) override;
 
-    /**
-     * Check if a service is declared (e.g. VINTF manifest).
-     *
-     * If this returns true, waitForService should always be able to return the
-     * service.
-     */
-     bool isDeclared(const String16& name) override;
+    bool isDeclared(const String16& name) override;
+
+    Vector<String16> getDeclaredInstances(const String16& iface) override;
 
 private:
     std::map<String16, sp<IBinder>> mNameToService;
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
new file mode 100644
index 0000000..fb32382
--- /dev/null
+++ b/libs/ftl/Android.bp
@@ -0,0 +1,17 @@
+cc_test {
+    name: "ftl_test",
+    test_suites: ["device-tests"],
+    sanitize: {
+        address: true,
+    },
+    srcs: [
+        "SmallVector_test.cpp",
+        "StaticVector_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wpedantic",
+    ],
+}
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
new file mode 100644
index 0000000..c41e622
--- /dev/null
+++ b/libs/ftl/SmallVector_test.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/SmallVector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::SmallVector;
+
+// Keep in sync with example usage in header file.
+TEST(SmallVector, Example) {
+    ftl::SmallVector<char, 3> vector;
+    EXPECT_TRUE(vector.empty());
+    EXPECT_FALSE(vector.dynamic());
+
+    vector = {'a', 'b', 'c'};
+    EXPECT_EQ(vector.size(), 3u);
+    EXPECT_FALSE(vector.dynamic());
+
+    vector.push_back('d');
+    EXPECT_TRUE(vector.dynamic());
+
+    vector.unstable_erase(vector.begin());
+    EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
+
+    vector.pop_back();
+    EXPECT_EQ(vector.back(), 'b');
+    EXPECT_TRUE(vector.dynamic());
+
+    const char array[] = "hi";
+    vector = ftl::SmallVector(array);
+    EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
+    EXPECT_FALSE(vector.dynamic());
+}
+
+TEST(SmallVector, Construct) {
+    {
+        // Default constructor.
+        SmallVector<std::string, 2> vector;
+
+        EXPECT_TRUE(vector.empty());
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // Array constructor.
+        const float kFloats[] = {.1f, .2f, .3f};
+        SmallVector vector(kFloats);
+
+        EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // Iterator constructor.
+        const char chars[] = "abcdef";
+        std::string string(chars);
+        SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+        EXPECT_STREQ(vector.begin(), chars);
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // Variadic constructor with same types.
+        SmallVector vector = {1, 2, 3};
+
+        static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
+        EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // Variadic constructor with different types.
+        const auto copy = "quince"s;
+        auto move = "tart"s;
+        SmallVector vector = {copy, std::move(move)};
+
+        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
+        EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // In-place constructor with same types.
+        SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+
+        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+        EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // In-place constructor with different types.
+        const auto copy = "red"s;
+        auto move = "velvet"s;
+        std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+        SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+
+        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+        EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+    {
+        // Conversion from StaticVector.
+        ftl::StaticVector doubles = {.1, .2, .3};
+        SmallVector vector = std::move(doubles);
+        EXPECT_TRUE(doubles.empty());
+
+        static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
+        EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
+        EXPECT_FALSE(vector.dynamic());
+    }
+}
+
+TEST(SmallVector, String) {
+    SmallVector<char, 10> chars;
+    char c = 'a';
+    std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+    chars.push_back('\0');
+
+    EXPECT_TRUE(chars.dynamic());
+    EXPECT_EQ(chars.size(), 11u);
+    EXPECT_STREQ(chars.begin(), "abcdefghij");
+
+    // Constructor takes iterator range.
+    const char kString[] = "123456";
+    SmallVector<char, 10> string(std::begin(kString), std::end(kString));
+
+    EXPECT_FALSE(string.dynamic());
+    EXPECT_STREQ(string.begin(), "123456");
+    EXPECT_EQ(string.size(), 7u);
+
+    // Similar to emplace, but replaces rather than inserts.
+    string.replace(string.begin() + 5, '\0');
+    EXPECT_STREQ(string.begin(), "12345");
+
+    swap(chars, string);
+
+    EXPECT_STREQ(chars.begin(), "12345");
+    EXPECT_STREQ(string.begin(), "abcdefghij");
+
+    EXPECT_FALSE(chars.dynamic());
+    EXPECT_TRUE(string.dynamic());
+}
+
+TEST(SmallVector, CopyableElement) {
+    struct Pair {
+        // Needed because std::vector emplace does not use uniform initialization.
+        Pair(int a, int b) : a(a), b(b) {}
+
+        const int a, b;
+        bool operator==(Pair p) const { return p.a == a && p.b == b; }
+    };
+
+    SmallVector<Pair, 5> pairs;
+
+    EXPECT_TRUE(pairs.empty());
+    EXPECT_EQ(pairs.max_size(), 5u);
+
+    for (size_t i = 0; i < pairs.max_size(); ++i) {
+        EXPECT_EQ(pairs.size(), i);
+
+        const int a = static_cast<int>(i) * 2;
+        EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
+    }
+
+    EXPECT_EQ(pairs.size(), 5u);
+    EXPECT_FALSE(pairs.dynamic());
+
+    // The vector is promoted when full.
+    EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
+    EXPECT_TRUE(pairs.dynamic());
+
+    EXPECT_EQ(pairs,
+              (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
+                           Pair{10, 11}}));
+
+    // Constructor takes at most N elements.
+    SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
+    EXPECT_FALSE(sums.dynamic());
+
+    // Random-access iterators comply with standard.
+    std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+    EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
+
+    sums.pop_back();
+    std::reverse(sums.begin(), sums.end());
+
+    EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
+}
+
+TEST(SmallVector, MovableElement) {
+    // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
+    // element type would be deduced from the first element, i.e. const char*.
+    SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+    strings.pop_back();
+
+    EXPECT_EQ(strings.max_size(), 7u);
+    EXPECT_EQ(strings.size(), 6u);
+
+    // Erase "cake" and append a substring copy.
+    {
+        const auto it = std::find_if(strings.begin(), strings.end(),
+                                     [](const auto& s) { return !s.empty(); });
+        ASSERT_FALSE(it == strings.end());
+        EXPECT_EQ(*it, "cake");
+
+        strings.unstable_erase(it);
+        EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
+    }
+
+    strings[1] = "quince"s;
+
+    // Replace last empty string with "tart".
+    {
+        const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+        ASSERT_FALSE(rit == strings.rend());
+
+        std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+        strings.replace(rit.base() - 1, list);
+    }
+
+    strings.front().assign("pie");
+
+    EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+
+    strings.push_back("nougat");
+    strings.push_back("oreo");
+    EXPECT_TRUE(strings.dynamic());
+
+    std::rotate(strings.begin(), strings.end() - 2, strings.end());
+
+    EXPECT_EQ(strings,
+              (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
+                           "cake"s}));
+}
+
+TEST(SmallVector, Replace) {
+    // Replacing does not require a copy/move assignment operator.
+    struct Word {
+        explicit Word(std::string str) : str(std::move(str)) {}
+        const std::string str;
+
+        bool operator==(const Word& other) const { return other.str == str; }
+    };
+
+    SmallVector words(std::in_place_type<Word>, "colored", "velour");
+
+    // The replaced element can be referenced by the replacement.
+    {
+        const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
+        EXPECT_EQ(word, Word("velvet"));
+    }
+
+    // The vector is not promoted if replacing while full.
+    EXPECT_FALSE(words.dynamic());
+
+    words.emplace_back("cake");
+    EXPECT_TRUE(words.dynamic());
+
+    {
+        const Word& word = words.replace(words.begin(), words.front().str.substr(4));
+        EXPECT_EQ(word, Word("red"));
+    }
+
+    EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
+}
+
+TEST(SmallVector, ReverseAppend) {
+    SmallVector strings = {"red"s, "velvet"s, "cake"s};
+    EXPECT_FALSE(strings.dynamic());
+
+    auto rit = strings.rbegin();
+    while (rit != strings.rend()) {
+        // Iterator and reference are invalidated on insertion.
+        const auto i = std::distance(strings.begin(), rit.base());
+        std::string s = *rit;
+
+        strings.push_back(std::move(s));
+        rit = std::make_reverse_iterator(strings.begin() + i) + 1;
+    }
+
+    EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
+    EXPECT_TRUE(strings.dynamic());
+}
+
+TEST(SmallVector, Sort) {
+    SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet");
+    strings.push_back("cake"s);
+
+    auto sorted = std::move(strings);
+    EXPECT_TRUE(strings.empty());
+
+    EXPECT_TRUE(sorted.dynamic());
+    EXPECT_TRUE(strings.dynamic());
+
+    std::sort(sorted.begin(), sorted.end());
+    EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+    // Constructor takes array reference.
+    {
+        const char* kStrings[] = {"cake", "lie"};
+        strings = SmallVector(kStrings);
+        EXPECT_FALSE(strings.dynamic());
+    }
+
+    EXPECT_GT(sorted, strings);
+    swap(sorted, strings);
+    EXPECT_LT(sorted, strings);
+
+    EXPECT_FALSE(sorted.dynamic());
+    EXPECT_TRUE(strings.dynamic());
+
+    // Append remaining elements, such that "pie" is the only difference.
+    for (const char* str : {"quince", "red", "tart", "velvet"}) {
+        sorted.emplace_back(str);
+    }
+    EXPECT_TRUE(sorted.dynamic());
+
+    EXPECT_NE(sorted, strings);
+
+    // Replace second element with "pie".
+    const auto it = sorted.begin() + 1;
+    EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+    EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+    DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+    DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+    DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+    ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+    struct {
+        int& live;
+        int& dead;
+    } counts;
+
+    bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+    std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(SmallVector, Destroy) {
+    int live = 0;
+    int dead = 0;
+
+    { SmallVector<DestroyCounts, 3> counts; }
+    EXPECT_EQ(0, live);
+    EXPECT_EQ(0, dead);
+
+    {
+        SmallVector<DestroyCounts, 3> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        EXPECT_FALSE(counts.dynamic());
+    }
+    EXPECT_EQ(3, live);
+    EXPECT_EQ(0, dead);
+
+    live = 0;
+    {
+        SmallVector<DestroyCounts, 3> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        EXPECT_TRUE(counts.dynamic());
+    }
+    EXPECT_EQ(4, live);
+    EXPECT_EQ(3, dead);
+
+    live = dead = 0;
+    {
+        SmallVector<DestroyCounts, 2> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        auto copy = counts;
+        EXPECT_TRUE(copy.dynamic());
+    }
+    EXPECT_EQ(6, live);
+    EXPECT_EQ(2, dead);
+
+    live = dead = 0;
+    {
+        SmallVector<DestroyCounts, 2> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        auto move = std::move(counts);
+        EXPECT_TRUE(move.dynamic());
+    }
+    EXPECT_EQ(3, live);
+    EXPECT_EQ(2, dead);
+
+    live = dead = 0;
+    {
+        SmallVector<DestroyCounts, 2> counts1;
+        counts1.emplace_back(live, dead);
+        counts1.emplace_back(live, dead);
+        counts1.emplace_back(live, dead);
+
+        EXPECT_TRUE(counts1.dynamic());
+        EXPECT_EQ(2, dead);
+        dead = 0;
+
+        SmallVector<DestroyCounts, 2> counts2;
+        counts2.emplace_back(live, dead);
+
+        EXPECT_FALSE(counts2.dynamic());
+
+        swap(counts1, counts2);
+
+        EXPECT_FALSE(counts1.dynamic());
+        EXPECT_TRUE(counts2.dynamic());
+
+        EXPECT_EQ(0, live);
+        EXPECT_EQ(1, dead);
+
+        dead = 0;
+    }
+    EXPECT_EQ(4, live);
+    EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
new file mode 100644
index 0000000..dd5ce35
--- /dev/null
+++ b/libs/ftl/StaticVector_test.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/StaticVector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::StaticVector;
+
+// Keep in sync with example usage in header file.
+TEST(StaticVector, Example) {
+    ftl::StaticVector<char, 3> vector;
+    EXPECT_TRUE(vector.empty());
+
+    vector = {'a', 'b'};
+    EXPECT_EQ(vector.size(), 2u);
+
+    vector.push_back('c');
+    EXPECT_TRUE(vector.full());
+
+    EXPECT_FALSE(vector.push_back('d'));
+    EXPECT_EQ(vector.size(), 3u);
+
+    vector.unstable_erase(vector.begin());
+    EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
+
+    vector.pop_back();
+    EXPECT_EQ(vector.back(), 'c');
+
+    const char array[] = "hi";
+    vector = ftl::StaticVector(array);
+    EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+}
+
+TEST(StaticVector, Construct) {
+    {
+        // Default constructor.
+        StaticVector<std::string, 2> vector;
+        EXPECT_TRUE(vector.empty());
+    }
+    {
+        // Array constructor.
+        const float kFloats[] = {.1f, .2f, .3f};
+        StaticVector vector(kFloats);
+        EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
+    }
+    {
+        // Iterator constructor.
+        const char chars[] = "abcdef";
+        std::string string(chars);
+        StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+        EXPECT_STREQ(vector.begin(), chars);
+    }
+    {
+        // Variadic constructor with same types.
+        StaticVector vector = {1, 2, 3};
+
+        static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
+        EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
+    }
+    {
+        // Variadic constructor with different types.
+        const auto copy = "quince"s;
+        auto move = "tart"s;
+        StaticVector vector = {copy, std::move(move)};
+
+        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
+        EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
+    }
+    {
+        // In-place constructor with same types.
+        StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+
+        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+        EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+    }
+    {
+        // In-place constructor with different types.
+        const auto copy = "red"s;
+        auto move = "velvet"s;
+        std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+        StaticVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+
+        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+        EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+    }
+    {
+        struct String {
+            explicit String(const char* str) : str(str) {}
+            explicit String(const char** ptr) : str(*ptr) {}
+            const char* str;
+        };
+
+        const char* kStrings[] = {"a", "b", "c", "d"};
+
+        {
+            // Two iterator-like elements.
+            StaticVector<String, 3> vector(kStrings, kStrings + 3);
+            ASSERT_EQ(vector.size(), 2u);
+
+            EXPECT_STREQ(vector[0].str, "a");
+            EXPECT_STREQ(vector[1].str, "d");
+        }
+        {
+            // Disambiguating iterator constructor.
+            StaticVector<String, 3> vector(ftl::IteratorRange, kStrings, kStrings + 3);
+            ASSERT_EQ(vector.size(), 3u);
+
+            EXPECT_STREQ(vector[0].str, "a");
+            EXPECT_STREQ(vector[1].str, "b");
+            EXPECT_STREQ(vector[2].str, "c");
+        }
+    }
+}
+
+TEST(StaticVector, String) {
+    StaticVector<char, 10> chars;
+    char c = 'a';
+    std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+    chars.back() = '\0';
+
+    EXPECT_STREQ(chars.begin(), "abcdefghi");
+
+    // Constructor takes iterator range.
+    const char kString[] = "123456";
+    StaticVector<char, 10> string(std::begin(kString), std::end(kString));
+
+    EXPECT_STREQ(string.begin(), "123456");
+    EXPECT_EQ(string.size(), 7u);
+
+    // Similar to emplace, but replaces rather than inserts.
+    string.replace(string.begin() + 5, '\0');
+    EXPECT_STREQ(string.begin(), "12345");
+
+    swap(chars, string);
+
+    EXPECT_STREQ(chars.begin(), "12345");
+    EXPECT_STREQ(string.begin(), "abcdefghi");
+}
+
+TEST(StaticVector, CopyableElement) {
+    struct Pair {
+        const int a, b;
+        bool operator==(Pair p) const { return p.a == a && p.b == b; }
+    };
+
+    StaticVector<Pair, 5> pairs;
+
+    EXPECT_TRUE(pairs.empty());
+    EXPECT_EQ(pairs.max_size(), 5u);
+
+    for (size_t i = 0; i < pairs.max_size(); ++i) {
+        EXPECT_EQ(pairs.size(), i);
+
+        const int a = static_cast<int>(i) * 2;
+        const auto it = pairs.emplace_back(a, a + 1);
+        ASSERT_NE(it, pairs.end());
+        EXPECT_EQ(*it, (Pair{a, a + 1}));
+    }
+
+    EXPECT_TRUE(pairs.full());
+    EXPECT_EQ(pairs.size(), 5u);
+
+    // Insertion fails if the vector is full.
+    const auto it = pairs.emplace_back(10, 11);
+    EXPECT_EQ(it, pairs.end());
+
+    EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
+
+    // Constructor takes at most N elements.
+    StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
+    EXPECT_TRUE(sums.full());
+
+    // Random-access iterators comply with standard.
+    std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+    EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
+
+    sums.pop_back();
+    std::reverse(sums.begin(), sums.end());
+
+    EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
+}
+
+TEST(StaticVector, MovableElement) {
+    // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
+    // element type would be deduced from the first element, i.e. const char*.
+    StaticVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+    strings.pop_back();
+
+    EXPECT_EQ(strings.max_size(), 7u);
+    EXPECT_EQ(strings.size(), 6u);
+
+    // Erase "cake" and append a substring copy.
+    {
+        auto it = std::find_if(strings.begin(), strings.end(),
+                               [](const auto& s) { return !s.empty(); });
+        ASSERT_FALSE(it == strings.end());
+        EXPECT_EQ(*it, "cake");
+
+        strings.unstable_erase(it);
+
+        // Construct std::string from first 4 characters of C-style string.
+        it = strings.emplace_back("cakewalk", 4u);
+        ASSERT_NE(it, strings.end());
+        EXPECT_EQ(*it, "cake"s);
+    }
+
+    strings[1] = "quince"s;
+
+    // Replace last empty string with "tart".
+    {
+        const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+        ASSERT_FALSE(rit == strings.rend());
+
+        std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+        strings.replace(rit.base() - 1, list);
+    }
+
+    strings.front().assign("pie");
+
+    EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+}
+
+TEST(StaticVector, Replace) {
+    // Replacing does not require a copy/move assignment operator.
+    struct Word {
+        explicit Word(std::string str) : str(std::move(str)) {}
+        const std::string str;
+    };
+
+    StaticVector words(std::in_place_type<Word>, "red", "velour", "cake");
+
+    // The replaced element can be referenced by the replacement.
+    const auto it = words.begin() + 1;
+    const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
+    EXPECT_EQ(word.str, "velvet");
+}
+
+TEST(StaticVector, ReverseTruncate) {
+    StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
+    EXPECT_FALSE(strings.full());
+
+    for (auto it = strings.begin(); it != strings.end(); ++it) {
+        strings.replace(it, strings.back());
+        strings.pop_back();
+    }
+
+    EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
+}
+
+TEST(StaticVector, Sort) {
+    StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
+    EXPECT_FALSE(strings.full());
+
+    auto sorted = std::move(strings);
+    EXPECT_TRUE(strings.empty());
+
+    std::sort(sorted.begin(), sorted.end());
+    EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+    // Constructor takes array reference.
+    {
+        const char* kStrings[] = {"cake", "lie"};
+        strings = StaticVector(kStrings);
+    }
+
+    EXPECT_GT(sorted, strings);
+    swap(sorted, strings);
+    EXPECT_LT(sorted, strings);
+
+    // Append remaining elements, such that "pie" is the only difference.
+    for (const char* str : {"quince", "red", "tart", "velvet"}) {
+        sorted.emplace_back(str);
+    }
+
+    EXPECT_NE(sorted, strings);
+
+    // Replace second element with "pie".
+    const auto it = sorted.begin() + 1;
+    EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+    EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+    DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+    DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+    DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+    ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+    struct {
+        int& live;
+        int& dead;
+    } counts;
+
+    bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+    std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(StaticVector, Destroy) {
+    int live = 0;
+    int dead = 0;
+
+    { StaticVector<DestroyCounts, 5> counts; }
+    EXPECT_EQ(0, live);
+    EXPECT_EQ(0, dead);
+
+    {
+        StaticVector<DestroyCounts, 5> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+    }
+    EXPECT_EQ(3, live);
+    EXPECT_EQ(0, dead);
+
+    live = 0;
+    {
+        StaticVector<DestroyCounts, 5> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        auto copy = counts;
+    }
+    EXPECT_EQ(6, live);
+    EXPECT_EQ(0, dead);
+
+    live = 0;
+    {
+        StaticVector<DestroyCounts, 5> counts;
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+        counts.emplace_back(live, dead);
+
+        auto move = std::move(counts);
+    }
+    EXPECT_EQ(3, live);
+    EXPECT_EQ(3, dead);
+
+    live = dead = 0;
+    {
+        StaticVector<DestroyCounts, 5> counts1;
+        counts1.emplace_back(live, dead);
+        counts1.emplace_back(live, dead);
+        counts1.emplace_back(live, dead);
+
+        StaticVector<DestroyCounts, 5> counts2;
+        counts2.emplace_back(live, dead);
+
+        swap(counts1, counts2);
+
+        EXPECT_EQ(0, live);
+        EXPECT_EQ(2, dead);
+
+        dead = 0;
+    }
+    EXPECT_EQ(4, live);
+    EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 66fb295..243d7f1 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -21,6 +21,11 @@
         "-Wno-enum-compare",
     ],
     host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
 
     vendor_available: true,
     vndk: {
@@ -38,14 +43,14 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-unstable-ndk_platform",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-unstable-ndk_platform",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
     ],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 53c68b7..e2f072a 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -706,35 +706,35 @@
         return err;
     }
 
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBytes), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.sampleIncrementInBits), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.strideInBytes), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.widthInSamples), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.heightInSamples), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.totalSizeInBytes), output);
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.horizontalSubsampling), output);
     if (err) {
         return err;
     }
-    return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+    return encodeInteger<int64_t>(static_cast<int64_t>(input.verticalSubsampling), output);
 }
 
 status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 8444883..8933dc3 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -2,6 +2,11 @@
     name: "libgralloctypes_fuzzer",
     defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        }
+    },
 
     fuzz_config: {
         cc: ["marissaw@google.com"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index cfc7e98..d54de49 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -39,6 +39,13 @@
 #include <string>
 #include <thread>
 
+// TODO(b/159240322): Extend this to x86 ABI.
+#if defined(__LP64__)
+#define UPDATABLE_DRIVER_ABI "arm64-v8a"
+#else
+#define UPDATABLE_DRIVER_ABI "armeabi-v7a"
+#endif // defined(__LP64__)
+
 // TODO(ianelliott@): Get the following from an ANGLE header:
 #define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
 // Version-2 API:
@@ -459,7 +466,8 @@
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
-                               const std::string developerOptIn, const int rulesFd,
+                               const std::string developerOptIn,
+                               const std::vector<std::string> eglFeatures, const int rulesFd,
                                const long rulesOffset, const long rulesLength) {
     if (mUseAngle != UNKNOWN) {
         // We've already figured out an answer for this app, so just return.
@@ -468,6 +476,8 @@
         return;
     }
 
+    mAngleEglFeatures = std::move(eglFeatures);
+
     ALOGV("setting ANGLE path to '%s'", path.c_str());
     mAnglePath = path;
     ALOGV("setting ANGLE app name to '%s'", appName.c_str());
@@ -513,6 +523,10 @@
     return mAngleAppName;
 }
 
+const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
+    return mAngleEglFeatures;
+}
+
 const std::string& GraphicsEnv::getLayerPaths() {
     return mLayerPaths;
 }
@@ -534,7 +548,7 @@
 }
 
 // Return true if all the required libraries from vndk and sphal namespace are
-// linked to the Game Driver namespace correctly.
+// linked to the updatable gfx driver namespace correctly.
 bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) {
     const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
     if (llndkLibraries.empty()) {
@@ -582,7 +596,28 @@
     }
 
     if (mDriverPath.empty()) {
-        return nullptr;
+        // For an application process, driver path is empty means this application is not opted in
+        // to use updatable driver. Application process doesn't have the ability to set up
+        // environment variables and hence before `getenv` call will return.
+        // For a process that is not an application process, if it's run from an environment,
+        // for example shell, where environment variables can be set, then it can opt into using
+        // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
+        // driver will be used currently.
+        // TODO(b/159240322) Support the production updatable driver.
+        const char* id = getenv("UPDATABLE_GFX_DRIVER");
+        if (id == nullptr || std::strcmp(id, "1")) {
+            return nullptr;
+        }
+        const sp<IGpuService> gpuService = getGpuService();
+        if (!gpuService) {
+            return nullptr;
+        }
+        mDriverPath = gpuService->getUpdatableDriverPath();
+        if (mDriverPath.empty()) {
+            return nullptr;
+        }
+        mDriverPath.append(UPDATABLE_DRIVER_ABI);
+        ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
     }
 
     auto vndkNamespace = android_get_exported_namespace("vndk");
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index de3503b..fa25c55 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,11 +27,11 @@
 public:
     explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
 
-    virtual void setGpuStats(const std::string& driverPackageName,
-                             const std::string& driverVersionName, uint64_t driverVersionCode,
-                             int64_t driverBuildTime, const std::string& appPackageName,
-                             const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
-                             bool isDriverLoaded, int64_t driverLoadingTime) {
+    void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+                     uint64_t driverVersionCode, int64_t driverBuildTime,
+                     const std::string& appPackageName, const int32_t vulkanVersion,
+                     GpuStatsInfo::Driver driver, bool isDriverLoaded,
+                     int64_t driverLoadingTime) override {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
 
@@ -48,8 +48,8 @@
         remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
-    virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                                const GpuStatsInfo::Stats stats, const uint64_t value) {
+    void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
+                        const GpuStatsInfo::Stats stats, const uint64_t value) override {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
 
@@ -60,6 +60,27 @@
 
         remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    void setUpdatableDriverPath(const std::string& driverPath) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+        data.writeUtf8AsUtf16(driverPath);
+
+        remote()->transact(BnGpuService::SET_UPDATABLE_DRIVER_PATH, data, &reply,
+                           IBinder::FLAG_ONEWAY);
+    }
+
+    std::string getUpdatableDriverPath() override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+        status_t error = remote()->transact(BnGpuService::GET_UPDATABLE_DRIVER_PATH, data, &reply);
+        std::string driverPath;
+        if (error == OK) {
+            error = reply.readUtf8FromUtf16(&driverPath);
+        }
+        return driverPath;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -126,6 +147,21 @@
 
             return OK;
         }
+        case SET_UPDATABLE_DRIVER_PATH: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            std::string driverPath;
+            if ((status = data.readUtf8FromUtf16(&driverPath)) != OK) return status;
+
+            setUpdatableDriverPath(driverPath);
+            return OK;
+        }
+        case GET_UPDATABLE_DRIVER_PATH: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            std::string driverPath = getUpdatableDriverPath();
+            return reply->writeUtf8AsUtf16(driverPath);
+        }
         case SHELL_COMMAND_TRANSACTION: {
             int in = data.readFileDescriptor();
             int out = data.readFileDescriptor();
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 22a2332..900fc49 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,12 +97,15 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
-                      const int rulesFd, const long rulesOffset, const long rulesLength);
+                      const std::vector<std::string> eglFeatures, const int rulesFd,
+                      const long rulesOffset, const long rulesLength);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
     // Get the app name for ANGLE debug message.
     std::string& getAngleAppName();
 
+    const std::vector<std::string>& getAngleEglFeatures();
+
     /*
      * Apis for debug layer
      */
@@ -154,6 +157,8 @@
     std::string mAngleAppName;
     // ANGLE developer opt in status.
     std::string mAngleDeveloperOptIn;
+    // ANGLE EGL features;
+    std::vector<std::string> mAngleEglFeatures;
     // ANGLE rules.
     std::vector<char> mRulesBuffer;
     // Use ANGLE flag.
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index c7c6d1e..2d59fa0 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,10 @@
     // set target stats.
     virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
                                 const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
+
+    // setter and getter for updatable driver path.
+    virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
+    virtual std::string getUpdatableDriverPath() = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
@@ -49,6 +53,8 @@
     enum IGpuServiceTag {
         SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
         SET_TARGET_STATS,
+        SET_UPDATABLE_DRIVER_PATH,
+        GET_UPDATABLE_DRIVER_PATH,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4a4510e..a0e9cbf 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -30,6 +30,12 @@
     min_sdk_version: "29",
 }
 
+filegroup {
+    name: "libgui_aidl",
+    srcs: ["aidl/**/*.aidl"],
+    path: "aidl/",
+}
+
 cc_library_shared {
     name: "libgui",
     vendor_available: false,
@@ -42,6 +48,7 @@
 
     srcs: [
         ":framework_native_aidl",
+        ":libgui_aidl",
         ":libgui_bufferqueue_sources",
 
         "BitTube.cpp",
@@ -55,13 +62,13 @@
         "DisplayEventDispatcher.cpp",
         "DisplayEventReceiver.cpp",
         "GLConsumer.cpp",
-        "GuiConfig.cpp",
         "IConsumerListener.cpp",
         "IDisplayEventConnection.cpp",
         "IGraphicBufferConsumer.cpp",
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
         "IRegionSamplingListener.cpp",
+        "IScreenCaptureListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "ITransactionCompletedListener.cpp",
@@ -74,6 +81,7 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
+        "TransactionTracing.cpp",
         "view/Surface.cpp",
         "bufferqueue/1.0/B2HProducerListener.cpp",
         "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
@@ -92,6 +100,7 @@
 
     export_shared_lib_headers: [
         "libbinder",
+        "libinput",
     ],
 
     // bufferhub is not used when building libgui for vendors
@@ -144,6 +153,7 @@
     defaults: ["libgui_bufferqueue-defaults"],
 
     srcs: [
+        ":libgui_aidl",
         ":libgui_bufferqueue_sources",
     ],
 }
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56591bd..678613b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,10 +18,12 @@
 #define LOG_TAG "BLASTBufferQueue"
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
 
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/GLConsumer.h>
+#include <gui/Surface.h>
 
 #include <utils/Trace.h>
 
@@ -29,8 +31,20 @@
 
 using namespace std::chrono_literals;
 
+namespace {
+inline const char* toString(bool b) {
+    return b ? "true" : "false";
+}
+} // namespace
+
 namespace android {
 
+// Macros to include adapter info in log messages
+#define BQA_LOGV(x, ...) \
+    ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+#define BQA_LOGE(x, ...) \
+    ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+
 void BLASTBufferItemConsumer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mPreviouslyConnected = mCurrentlyConnected;
@@ -93,9 +107,10 @@
     if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
 }
 
-BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
-                                   bool enableTripleBuffering)
-      : mSurfaceControl(surface),
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+                                   int width, int height, bool enableTripleBuffering)
+      : mName(name),
+        mSurfaceControl(surface),
         mWidth(width),
         mHeight(height),
         mNextTransaction(nullptr) {
@@ -110,9 +125,9 @@
     mBufferItemConsumer =
         new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
     static int32_t id = 0;
-    auto name = std::string("BLAST Consumer") + std::to_string(id);
+    auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
     id++;
-    mBufferItemConsumer->setName(String8(name.c_str()));
+    mBufferItemConsumer->setName(String8(consumerName.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
     mBufferItemConsumer->setBufferFreedListener(this);
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
@@ -127,7 +142,7 @@
     mPendingReleaseItem.releaseFence = nullptr;
 }
 
-void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) {
     std::unique_lock _lock{mMutex};
     mSurfaceControl = surface;
 
@@ -144,7 +159,7 @@
     if (context == nullptr) {
         return;
     }
-    BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+    sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
     bq->transactionCallback(latchTime, presentFence, stats);
 }
 
@@ -152,6 +167,8 @@
                                            const std::vector<SurfaceControlStats>& stats) {
     std::unique_lock _lock{mMutex};
     ATRACE_CALL();
+    BQA_LOGV("transactionCallback");
+    mInitialCallbackReceived = true;
 
     if (!stats.empty()) {
         mTransformHint = stats[0].transformHint;
@@ -169,7 +186,7 @@
         if (!stats.empty()) {
             mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
         } else {
-            ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+            BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
             mPendingReleaseItem.releaseFence = nullptr;
         }
         mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
@@ -182,7 +199,7 @@
     }
 
     if (mSubmitted.empty()) {
-        ALOGE("ERROR: callback with no corresponding submitted buffer item");
+        BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
     }
     mPendingReleaseItem.item = std::move(mSubmitted.front());
     mSubmitted.pop();
@@ -195,12 +212,17 @@
 
 void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
     ATRACE_CALL();
-    if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+    BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
+
+    // Wait to acquire a buffer if there are no frames available or we have acquired the max
+    // number of buffers.
+    if (mNumFrameAvailable == 0 || maxBuffersAcquired()) {
+        BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
         return;
     }
 
     if (mSurfaceControl == nullptr) {
-        ALOGE("ERROR : surface control is null");
+        BQA_LOGE("ERROR : surface control is null");
         return;
     }
 
@@ -217,6 +239,7 @@
 
     status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
     if (status != OK) {
+        BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
         return;
     }
     auto buffer = bufferItem.mGraphicBuffer;
@@ -224,9 +247,17 @@
 
     if (buffer == nullptr) {
         mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+        BQA_LOGE("Buffer was empty");
         return;
     }
 
+    if (rejectBuffer(bufferItem)) {
+        BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
+                 mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
+        // TODO(b/168917217) temporarily don't reject buffers until we can synchronize buffer size
+        // changes from ViewRootImpl.
+    }
+
     mNumAcquired++;
     mSubmitted.push(bufferItem);
 
@@ -246,15 +277,22 @@
                        bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
-    t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
+    t->setFrame(mSurfaceControl,
+                {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)});
     t->setCrop(mSurfaceControl, computeCrop(bufferItem));
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
     t->setDesiredPresentTime(bufferItem.mTimestamp);
+    t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
 
     if (applyTransaction) {
         t->apply();
     }
+
+    BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+             " applyTransaction=%s mTimestamp=%" PRId64,
+             mWidth, mHeight, bufferItem.mFrameNumber, toString(applyTransaction),
+             bufferItem.mTimestamp);
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -264,23 +302,119 @@
     return item.mCrop;
 }
 
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
 
-    if (mNextTransaction != nullptr) {
-        while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+    const bool nextTransactionSet = mNextTransaction != nullptr;
+    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
+             item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
+
+    if (nextTransactionSet || mFlushShadowQueue) {
+        while (mNumFrameAvailable > 0 || maxBuffersAcquired()) {
+            BQA_LOGV("waiting in onFrameAvailable...");
             mCallbackCV.wait(_lock);
         }
     }
+    mFlushShadowQueue = false;
     // add to shadow queue
     mNumFrameAvailable++;
     processNextBufferLocked(true);
 }
 
+void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
+    BQA_LOGV("onFrameReplaced framenumber=%" PRIu64, item.mFrameNumber);
+    // Do nothing since we are not storing unacquired buffer items locally.
+}
+
 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
     std::lock_guard _lock{mMutex};
     mNextTransaction = t;
 }
 
+bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) const {
+    if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+        // Only reject buffers if scaling mode is freeze.
+        return false;
+    }
+
+    uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+    uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+    // Take the buffer's orientation into account
+    if (item.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    // reject buffers if the buffer size doesn't match.
+    return bufWidth != mWidth || bufHeight != mHeight;
+}
+
+// Check if we have acquired the maximum number of buffers.
+// As a special case, we wait for the first callback before acquiring the second buffer so we
+// can ensure the first buffer is presented if multiple buffers are queued in succession.
+bool BLASTBufferQueue::maxBuffersAcquired() const {
+    return mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 ||
+            (!mInitialCallbackReceived && mNumAcquired == 1);
+}
+
+class BBQSurface : public Surface {
+private:
+    sp<BLASTBufferQueue> mBbq;
+public:
+    BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
+               const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
+          : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
+
+    void allocateBuffers() override {
+        uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
+        uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
+        auto gbp = getIGraphicBufferProducer();
+        std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
+                      reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+            gbp->allocateBuffers(reqWidth, reqHeight,
+                                 reqFormat, reqUsage);
+
+        }).detach();
+    }
+
+    status_t setFrameRate(float frameRate, int8_t compatibility) override {
+        if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+            return BAD_VALUE;
+        }
+        return mBbq->setFrameRate(frameRate, compatibility);
+    }
+
+    status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override {
+        return mBbq->setFrameTimelineVsync(frameTimelineVsyncId);
+    }
+};
+
+// TODO: Can we coalesce this with frame updates? Need to confirm
+// no timing issues.
+status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) {
+    std::unique_lock _lock{mMutex};
+    SurfaceComposerClient::Transaction t;
+
+    return t.setFrameRate(mSurfaceControl, frameRate, compatibility)
+        .apply();
+}
+
+status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
+    std::unique_lock _lock{mMutex};
+    SurfaceComposerClient::Transaction t;
+
+    return t.setFrameTimelineVsync(mSurfaceControl, frameTimelineVsyncId)
+        .apply();
+}
+
+sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
+    std::unique_lock _lock{mMutex};
+    sp<IBinder> scHandle = nullptr;
+    if (includeSurfaceControlHandle && mSurfaceControl) {
+        scHandle = mSurfaceControl->getHandle();
+    }
+    return new BBQSurface(mProducer, true, scHandle, this);
+}
+
 } // namespace android
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index ef7a6f5..351af65 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -86,6 +86,10 @@
     mReceiveFd = std::move(receiveFd);
 }
 
+void BitTube::setSendFd(base::unique_fd&& sendFd) {
+    mSendFd = std::move(sendFd);
+}
+
 ssize_t BitTube::write(void const* vaddr, size_t size) {
     ssize_t err, len;
     do {
@@ -115,6 +119,11 @@
 
     status_t result = reply->writeDupFileDescriptor(mReceiveFd);
     mReceiveFd.reset();
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = reply->writeDupFileDescriptor(mSendFd);
+    mSendFd.reset();
     return result;
 }
 
@@ -126,6 +135,13 @@
         ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
         return -error;
     }
+    mSendFd.reset(dup(parcel->readFileDescriptor()));
+    if (mSendFd < 0) {
+        mSendFd.reset();
+        int error = errno;
+        ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
+        return -error;
+    }
     return NO_ERROR;
 }
 
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 489f3c3..1f71e23 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -696,7 +696,7 @@
     // relationship, thus |getConsumerName| from the producer side does not
     // make any sense.
     ALOGE("BufferHubProducer::getConsumerName not supported.");
-    return String8("BufferHubQueue::DummyConsumer");
+    return String8("BufferHubQueue::StubConsumer");
 }
 
 status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index b33bc9e..abfee61 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -73,7 +73,8 @@
         nsecs_t vsyncTimestamp;
         PhysicalDisplayId vsyncDisplayId;
         uint32_t vsyncCount;
-        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+        VsyncEventData vsyncEventData;
+        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
             ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
                   ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
         }
@@ -89,12 +90,8 @@
     return OK;
 }
 
-void DisplayEventDispatcher::requestLatestConfig() {
-    status_t status = mReceiver.requestLatestConfig();
-    if (status) {
-        ALOGW("Failed enable config events, status=%d", status);
-        return;
-    }
+void DisplayEventDispatcher::injectEvent(const DisplayEventReceiver::Event& event) {
+    mReceiver.sendEvents(&event, 1);
 }
 
 int DisplayEventDispatcher::getFd() const {
@@ -120,12 +117,14 @@
     nsecs_t vsyncTimestamp;
     PhysicalDisplayId vsyncDisplayId;
     uint32_t vsyncCount;
-    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+    VsyncEventData vsyncEventData;
+    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
         ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
-              ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
-              this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+              ", displayId=%s, count=%d, vsyncId=%" PRId64,
+              this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
+              vsyncEventData.id);
         mWaitingForVsync = false;
-        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
     }
 
     return 1; // keep the callback
@@ -133,7 +132,8 @@
 
 bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
                                                   PhysicalDisplayId* outDisplayId,
-                                                  uint32_t* outCount) {
+                                                  uint32_t* outCount,
+                                                  VsyncEventData* outVsyncEventData) {
     bool gotVsync = false;
     DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
     ssize_t n;
@@ -149,6 +149,8 @@
                     *outTimestamp = ev.header.timestamp;
                     *outDisplayId = ev.header.displayId;
                     *outCount = ev.vsync.count;
+                    outVsyncEventData->id = ev.vsync.vsyncId;
+                    outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
@@ -157,6 +159,9 @@
                     dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
                                           ev.config.configId, ev.config.vsyncPeriod);
                     break;
+                case DisplayEventReceiver::DISPLAY_EVENT_NULL:
+                    dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
+                    break;
                 default:
                     ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
                     break;
@@ -168,4 +173,5 @@
     }
     return gotVsync;
 }
+
 } // namespace android
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 1fed509..f2b0962 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,14 +79,6 @@
     return NO_INIT;
 }
 
-status_t DisplayEventReceiver::requestLatestConfig() {
-    if (mEventConnection != nullptr) {
-        mEventConnection->requestLatestConfig();
-        return NO_ERROR;
-    }
-    return NO_INIT;
-}
-
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
     return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
@@ -98,6 +90,10 @@
     return gui::BitTube::recvObjects(dataChannel, events, count);
 }
 
+ssize_t DisplayEventReceiver::sendEvents(Event const* events, size_t count) {
+    return DisplayEventReceiver::sendEvents(mDataChannel.get(), events, count);
+}
+
 ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
         Event const* events, size_t count)
 {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 59f1bcd..30d19e3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -280,7 +280,7 @@
         mCurrentFenceTime = FenceTime::NO_FENCE;
 
         if (mAttached) {
-            // This binds a dummy buffer (mReleasedTexImage).
+            // This binds a buffer placeholder (mReleasedTexImage).
             status_t result = bindTextureImageLocked();
             if (result != NO_ERROR) {
                 return result;
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp
deleted file mode 100644
index 3ec20ee..0000000
--- a/libs/gui/GuiConfig.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/GuiConfig.h>
-
-namespace android {
-
-void appendGuiConfigString(std::string& configStr) {
-    static const char* config =
-            " [libgui"
-#ifdef DONT_USE_FENCE_SYNC
-            " DONT_USE_FENCE_SYNC"
-#endif
-            "]";
-    configStr.append(config);
-}
-
-}; // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index aa74bfd..c0e246f 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,8 +26,7 @@
     STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
     REQUEST_NEXT_VSYNC,
-    REQUEST_LATEST_CONFIG,
-    LAST = REQUEST_LATEST_CONFIG,
+    LAST = REQUEST_NEXT_VSYNC,
 };
 
 } // Anonymous namespace
@@ -54,11 +53,6 @@
         callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
                 Tag::REQUEST_NEXT_VSYNC);
     }
-
-    void requestLatestConfig() override {
-        callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
-                Tag::REQUEST_LATEST_CONFIG);
-    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -80,8 +74,6 @@
             return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
         case Tag::REQUEST_NEXT_VSYNC:
             return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
-        case Tag::REQUEST_LATEST_CONFIG:
-            return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
     }
 }
 
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 5c81b9d..0683087 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,7 +119,7 @@
     return BBinder::onTransact(code, data, reply, flags);
 }
 
-DummyProducerListener::~DummyProducerListener() = default;
+StubProducerListener::~StubProducerListener() = default;
 
 bool BnProducerListener::needsReleaseNotify() {
     return true;
diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp
new file mode 100644
index 0000000..0635e9c
--- /dev/null
+++ b/libs/gui/IScreenCaptureListener.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/IScreenCaptureListener.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION,
+    LAST = ON_SCREEN_CAPTURE_COMPLETE,
+};
+
+} // Anonymous namespace
+
+class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> {
+public:
+    explicit BpScreenCaptureListener(const sp<IBinder>& impl)
+          : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {}
+
+    ~BpScreenCaptureListener() override;
+
+    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor());
+
+        SAFE_PARCEL(captureResults.write, data);
+        return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data,
+                                  &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpScreenCaptureListener::~BpScreenCaptureListener() = default;
+
+IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener");
+
+status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                             uint32_t flags) {
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_SCREEN_CAPTURE_COMPLETE: {
+            CHECK_INTERFACE(IScreenCaptureListener, data, reply);
+            ScreenCaptureResults captureResults;
+            SAFE_PARCEL(captureResults.read, data);
+            return onScreenCaptureComplete(captureResults);
+        }
+        default: {
+            return BBinder::onTransact(code, data, reply, flags);
+        }
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6c9fd07..6f92233 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -20,6 +20,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/gui/ITransactionTraceListener.h>
+
 #include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -66,42 +68,43 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
-    virtual void setTransactionState(const Vector<ComposerState>& state,
-                                     const Vector<DisplayState>& displays, uint32_t flags,
-                                     const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& commands,
-                                     int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-                                     const std::vector<ListenerCallbacks>& listenerCallbacks) {
+    virtual status_t setTransactionState(
+            int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            const InputWindowCommands& commands, int64_t desiredPresentTime,
+            const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
-        data.writeUint32(static_cast<uint32_t>(state.size()));
+        SAFE_PARCEL(data.writeInt64, frameTimelineVsyncId);
+        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
         for (const auto& s : state) {
-            s.write(data);
+            SAFE_PARCEL(s.write, data);
         }
 
-        data.writeUint32(static_cast<uint32_t>(displays.size()));
+        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
         for (const auto& d : displays) {
-            d.write(data);
+            SAFE_PARCEL(d.write, data);
         }
 
-        data.writeUint32(flags);
-        data.writeStrongBinder(applyToken);
-        commands.write(data);
-        data.writeInt64(desiredPresentTime);
-        data.writeStrongBinder(uncacheBuffer.token.promote());
-        data.writeUint64(uncacheBuffer.id);
-        data.writeBool(hasListenerCallbacks);
+        SAFE_PARCEL(data.writeUint32, flags);
+        SAFE_PARCEL(data.writeStrongBinder, applyToken);
+        SAFE_PARCEL(commands.write, data);
+        SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+        SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+        SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
 
-        if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
-            for (const auto& [listener, callbackIds] : listenerCallbacks) {
-                data.writeStrongBinder(listener);
-                data.writeInt64Vector(callbackIds);
-            }
+        SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
+        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+            SAFE_PARCEL(data.writeStrongBinder, listener);
+            SAFE_PARCEL(data.writeInt64Vector, callbackIds);
         }
 
-        remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+        SAFE_PARCEL(data.writeUint64, transactionId);
+
+        return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
     virtual void bootFinished()
@@ -111,95 +114,34 @@
         remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
     }
 
-    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
-                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ui::Rotation rotation, bool captureSecureLayers) {
+    virtual status_t captureDisplay(const DisplayCaptureArgs& args,
+                                    const sp<IScreenCaptureListener>& captureListener) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeStrongBinder(display);
-        data.writeInt32(static_cast<int32_t>(reqDataspace));
-        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
-        data.write(sourceCrop);
-        data.writeUint32(reqWidth);
-        data.writeUint32(reqHeight);
-        data.writeInt32(static_cast<int32_t>(useIdentityTransform));
-        data.writeInt32(static_cast<int32_t>(rotation));
-        data.writeInt32(static_cast<int32_t>(captureSecureLayers));
-        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("captureScreen failed to transact: %d", result);
-            return result;
-        }
-        result = reply.readInt32();
-        if (result != NO_ERROR) {
-            ALOGE("captureScreen failed to readInt32: %d", result);
-            return result;
-        }
+        SAFE_PARCEL(args.write, data);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
 
-        *outBuffer = new GraphicBuffer();
-        reply.read(**outBuffer);
-        outCapturedSecureLayers = reply.readBool();
-
-        return result;
+        return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
     }
 
-    virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
-                                   sp<GraphicBuffer>* outBuffer) {
+    virtual status_t captureDisplay(uint64_t displayOrLayerStack,
+                                    const sp<IScreenCaptureListener>& captureListener) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeUint64(displayOrLayerStack);
-        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("captureScreen failed to transact: %d", result);
-            return result;
-        }
-        result = reply.readInt32();
-        if (result != NO_ERROR) {
-            ALOGE("captureScreen failed to readInt32: %d", result);
-            return result;
-        }
+        SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
 
-        *outDataspace = static_cast<ui::Dataspace>(reply.readInt32());
-        *outBuffer = new GraphicBuffer();
-        reply.read(**outBuffer);
-        return result;
+        return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
     }
 
-    virtual status_t captureLayers(
-            const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
-            const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
-            const Rect& sourceCrop,
-            const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale,
-            bool childrenOnly) {
+    virtual status_t captureLayers(const LayerCaptureArgs& args,
+                                   const sp<IScreenCaptureListener>& captureListener) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeStrongBinder(layerHandleBinder);
-        data.writeInt32(static_cast<int32_t>(reqDataspace));
-        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
-        data.write(sourceCrop);
-        data.writeInt32(excludeLayers.size());
-        for (auto el : excludeLayers) {
-            data.writeStrongBinder(el);
-        }
-        data.writeFloat(frameScale);
-        data.writeBool(childrenOnly);
-        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("captureLayers failed to transact: %d", result);
-            return result;
-        }
-        result = reply.readInt32();
-        if (result != NO_ERROR) {
-            ALOGE("captureLayers failed to readInt32: %d", result);
-            return result;
-        }
+        SAFE_PARCEL(args.write, data);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
 
-        *outBuffer = new GraphicBuffer();
-        reply.read(**outBuffer);
-
-        return result;
+        return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
     }
 
     virtual bool authenticateSurfaceTexture(
@@ -308,10 +250,25 @@
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeString8(displayName);
-        data.writeInt32(secure ? 1 : 0);
-        remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
-        return reply.readStrongBinder();
+        status_t status = data.writeString8(displayName);
+        if (status) {
+            return nullptr;
+        }
+        status = data.writeBool(secure);
+        if (status) {
+            return nullptr;
+        }
+
+        status = remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
+        if (status) {
+            return nullptr;
+        }
+        sp<IBinder> display;
+        status = reply.readNullableStrongBinder(&display);
+        if (status) {
+            return nullptr;
+        }
+        return display;
     }
 
     virtual void destroyDisplay(const sp<IBinder>& display)
@@ -327,8 +284,11 @@
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) ==
             NO_ERROR) {
-            std::vector<PhysicalDisplayId> displayIds;
-            if (reply.readUint64Vector(&displayIds) == NO_ERROR) {
+            std::vector<uint64_t> rawIds;
+            if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
+                std::vector<PhysicalDisplayId> displayIds(rawIds.size());
+                std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
+                               [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
                 return displayIds;
             }
         }
@@ -339,7 +299,7 @@
     virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeUint64(displayId);
+        data.writeUint64(displayId.value);
         remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply);
         return reply.readStrongBinder();
     }
@@ -689,8 +649,7 @@
         return result;
     }
 
-    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
-    {
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
         if (!outLayers) {
             return UNEXPECTED_NULL;
         }
@@ -929,7 +888,7 @@
     }
 
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultConfig,
+                                                  int32_t defaultConfig, bool allowGroupSwitching,
                                                   float primaryRefreshRateMin,
                                                   float primaryRefreshRateMax,
                                                   float appRequestRefreshRateMin,
@@ -950,6 +909,11 @@
             ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
             return result;
         }
+        result = data.writeBool(allowGroupSwitching);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write allowGroupSwitching: %d", result);
+            return result;
+        }
         result = data.writeFloat(primaryRefreshRateMin);
         if (result != NO_ERROR) {
             ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
@@ -984,12 +948,14 @@
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                   int32_t* outDefaultConfig,
+                                                  bool* outAllowGroupSwitching,
                                                   float* outPrimaryRefreshRateMin,
                                                   float* outPrimaryRefreshRateMax,
                                                   float* outAppRequestRefreshRateMin,
                                                   float* outAppRequestRefreshRateMax) {
-        if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax ||
-            !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+        if (!outDefaultConfig || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
+            !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin ||
+            !outAppRequestRefreshRateMax) {
             return BAD_VALUE;
         }
         Parcel data, reply;
@@ -1014,6 +980,11 @@
             ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
             return result;
         }
+        result = reply.readBool(outAllowGroupSwitching);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read allowGroupSwitching: %d", result);
+            return result;
+        }
         result = reply.readFloat(outPrimaryRefreshRateMin);
         if (result != NO_ERROR) {
             ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
@@ -1212,6 +1183,47 @@
 
         return NO_ERROR;
     }
+
+    virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+                                           int64_t frameTimelineVsyncId) {
+        Parcel data, reply;
+        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            ALOGE("setFrameTimelineVsync: failed writing interface token: %s (%d)", strerror(-err),
+                  -err);
+            return err;
+        }
+
+        err = data.writeStrongBinder(IInterface::asBinder(surface));
+        if (err != NO_ERROR) {
+            ALOGE("setFrameTimelineVsync: failed writing strong binder: %s (%d)", strerror(-err),
+                  -err);
+            return err;
+        }
+
+        err = data.writeInt64(frameTimelineVsyncId);
+        if (err != NO_ERROR) {
+            ALOGE("setFrameTimelineVsync: failed writing int64_t: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_VSYNC, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("setFrameTimelineVsync: failed to transact: %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        return reply.readInt32();
+    }
+
+    virtual status_t addTransactionTraceListener(
+            const sp<gui::ITransactionTraceListener>& listener) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+        return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1235,135 +1247,95 @@
         case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
 
-            size_t count = data.readUint32();
-            if (count > data.dataSize()) {
-                return BAD_VALUE;
-            }
+            int64_t frameTimelineVsyncId;
+            SAFE_PARCEL(data.readInt64, &frameTimelineVsyncId);
+            uint32_t count = 0;
+            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
             Vector<ComposerState> state;
             state.setCapacity(count);
             for (size_t i = 0; i < count; i++) {
                 ComposerState s;
-                if (s.read(data) == BAD_VALUE) {
-                    return BAD_VALUE;
-                }
+                SAFE_PARCEL(s.read, data);
                 state.add(s);
             }
 
-            count = data.readUint32();
-            if (count > data.dataSize()) {
-                return BAD_VALUE;
-            }
+            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
             DisplayState d;
             Vector<DisplayState> displays;
             displays.setCapacity(count);
             for (size_t i = 0; i < count; i++) {
-                if (d.read(data) == BAD_VALUE) {
-                    return BAD_VALUE;
-                }
+                SAFE_PARCEL(d.read, data);
                 displays.add(d);
             }
 
-            uint32_t stateFlags = data.readUint32();
-            sp<IBinder> applyToken = data.readStrongBinder();
+            uint32_t stateFlags = 0;
+            SAFE_PARCEL(data.readUint32, &stateFlags);
+            sp<IBinder> applyToken;
+            SAFE_PARCEL(data.readStrongBinder, &applyToken);
             InputWindowCommands inputWindowCommands;
-            inputWindowCommands.read(data);
+            SAFE_PARCEL(inputWindowCommands.read, data);
 
-            int64_t desiredPresentTime = data.readInt64();
+            int64_t desiredPresentTime = 0;
+            SAFE_PARCEL(data.readInt64, &desiredPresentTime);
 
             client_cache_t uncachedBuffer;
-            uncachedBuffer.token = data.readStrongBinder();
-            uncachedBuffer.id = data.readUint64();
+            sp<IBinder> tmpBinder;
+            SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+            uncachedBuffer.token = tmpBinder;
+            SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
 
-            bool hasListenerCallbacks = data.readBool();
+            bool hasListenerCallbacks = false;
+            SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
 
             std::vector<ListenerCallbacks> listenerCallbacks;
-            int32_t listenersSize = data.readInt32();
+            int32_t listenersSize = 0;
+            SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
             for (int32_t i = 0; i < listenersSize; i++) {
-                auto listener = data.readStrongBinder();
+                SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
                 std::vector<CallbackId> callbackIds;
-                data.readInt64Vector(&callbackIds);
-                listenerCallbacks.emplace_back(listener, callbackIds);
+                SAFE_PARCEL(data.readInt64Vector, &callbackIds);
+                listenerCallbacks.emplace_back(tmpBinder, callbackIds);
             }
-            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
-                                desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
-                                listenerCallbacks);
-            return NO_ERROR;
+
+            uint64_t transactionId = -1;
+            SAFE_PARCEL(data.readUint64, &transactionId);
+
+            return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
+                                       applyToken, inputWindowCommands, desiredPresentTime,
+                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+                                       transactionId);
         }
         case BOOT_FINISHED: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             bootFinished();
             return NO_ERROR;
         }
-        case CAPTURE_SCREEN: {
+        case CAPTURE_DISPLAY: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> display = data.readStrongBinder();
-            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
-            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
-            sp<GraphicBuffer> outBuffer;
-            Rect sourceCrop(Rect::EMPTY_RECT);
-            data.read(sourceCrop);
-            uint32_t reqWidth = data.readUint32();
-            uint32_t reqHeight = data.readUint32();
-            bool useIdentityTransform = static_cast<bool>(data.readInt32());
-            int32_t rotation = data.readInt32();
-            bool captureSecureLayers = static_cast<bool>(data.readInt32());
+            DisplayCaptureArgs args;
+            sp<IScreenCaptureListener> captureListener;
+            SAFE_PARCEL(args.read, data);
+            SAFE_PARCEL(data.readStrongBinder, &captureListener);
 
-            bool capturedSecureLayers = false;
-            status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
-                                         reqPixelFormat, sourceCrop, reqWidth, reqHeight,
-                                         useIdentityTransform, ui::toRotation(rotation),
-                                         captureSecureLayers);
-
-            reply->writeInt32(res);
-            if (res == NO_ERROR) {
-                reply->write(*outBuffer);
-                reply->writeBool(capturedSecureLayers);
-            }
-            return NO_ERROR;
+            return captureDisplay(args, captureListener);
         }
-        case CAPTURE_SCREEN_BY_ID: {
+        case CAPTURE_DISPLAY_BY_ID: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            uint64_t displayOrLayerStack = data.readUint64();
-            ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB;
-            sp<GraphicBuffer> outBuffer;
-            status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer);
-            reply->writeInt32(res);
-            if (res == NO_ERROR) {
-                reply->writeInt32(static_cast<int32_t>(outDataspace));
-                reply->write(*outBuffer);
-            }
-            return NO_ERROR;
+            uint64_t displayOrLayerStack = 0;
+            sp<IScreenCaptureListener> captureListener;
+            SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
+            SAFE_PARCEL(data.readStrongBinder, &captureListener);
+
+            return captureDisplay(displayOrLayerStack, captureListener);
         }
         case CAPTURE_LAYERS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> layerHandleBinder = data.readStrongBinder();
-            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
-            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
-            sp<GraphicBuffer> outBuffer;
-            Rect sourceCrop(Rect::EMPTY_RECT);
-            data.read(sourceCrop);
+            LayerCaptureArgs args;
+            sp<IScreenCaptureListener> captureListener;
+            SAFE_PARCEL(args.read, data);
+            SAFE_PARCEL(data.readStrongBinder, &captureListener);
 
-            std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
-            int numExcludeHandles = data.readInt32();
-            if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
-                return BAD_VALUE;
-            }
-            excludeHandles.reserve(numExcludeHandles);
-            for (int i = 0; i < numExcludeHandles; i++) {
-                excludeHandles.emplace(data.readStrongBinder());
-            }
-
-            float frameScale = data.readFloat();
-            bool childrenOnly = data.readBool();
-
-            status_t res =
-                    captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat,
-                                  sourceCrop, excludeHandles, frameScale, childrenOnly);
-            reply->writeInt32(res);
-            if (res == NO_ERROR) {
-                reply->write(*outBuffer);
-            }
-            return NO_ERROR;
+            return captureLayers(args, captureListener);
         }
         case AUTHENTICATE_SURFACE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1404,10 +1376,12 @@
         }
         case CREATE_DISPLAY: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            String8 displayName = data.readString8();
-            bool secure = bool(data.readInt32());
-            sp<IBinder> display(createDisplay(displayName, secure));
-            reply->writeStrongBinder(display);
+            String8 displayName;
+            SAFE_PARCEL(data.readString8, &displayName);
+            bool secure = false;
+            SAFE_PARCEL(data.readBool, &secure);
+            sp<IBinder> display = createDisplay(displayName, secure);
+            SAFE_PARCEL(reply->writeStrongBinder, display);
             return NO_ERROR;
         }
         case DESTROY_DISPLAY: {
@@ -1418,7 +1392,7 @@
         }
         case GET_PHYSICAL_DISPLAY_TOKEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            PhysicalDisplayId displayId = data.readUint64();
+            PhysicalDisplayId displayId(data.readUint64());
             sp<IBinder> display = getPhysicalDisplayToken(displayId);
             reply->writeStrongBinder(display);
             return NO_ERROR;
@@ -1820,7 +1794,11 @@
         }
         case GET_PHYSICAL_DISPLAY_IDS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            return reply->writeUint64Vector(getPhysicalDisplayIds());
+            std::vector<PhysicalDisplayId> ids = getPhysicalDisplayIds();
+            std::vector<uint64_t> rawIds(ids.size());
+            std::transform(ids.begin(), ids.end(), rawIds.begin(),
+                           [](PhysicalDisplayId id) { return id.value; });
+            return reply->writeUint64Vector(rawIds);
         }
         case ADD_REGION_SAMPLING_LISTENER: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1863,6 +1841,13 @@
                 ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
                 return result;
             }
+            bool allowGroupSwitching;
+            result = data.readBool(&allowGroupSwitching);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read allowGroupSwitching: %d",
+                      result);
+                return result;
+            }
             float primaryRefreshRateMin;
             result = data.readFloat(&primaryRefreshRateMin);
             if (result != NO_ERROR) {
@@ -1891,10 +1876,10 @@
                       result);
                 return result;
             }
-            result =
-                    setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
-                                                 primaryRefreshRateMax, appRequestRefreshRateMin,
-                                                 appRequestRefreshRateMax);
+            result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
+                                                  primaryRefreshRateMin, primaryRefreshRateMax,
+                                                  appRequestRefreshRateMin,
+                                                  appRequestRefreshRateMax);
             if (result != NO_ERROR) {
                 ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
                       "%d",
@@ -1908,13 +1893,14 @@
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
             int32_t defaultConfig;
+            bool allowGroupSwitching;
             float primaryRefreshRateMin;
             float primaryRefreshRateMax;
             float appRequestRefreshRateMin;
             float appRequestRefreshRateMax;
 
             status_t result =
-                    getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
+                    getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, &allowGroupSwitching,
                                                  &primaryRefreshRateMin, &primaryRefreshRateMax,
                                                  &appRequestRefreshRateMin,
                                                  &appRequestRefreshRateMax);
@@ -1930,6 +1916,12 @@
                 ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
                 return result;
             }
+            result = reply->writeBool(allowGroupSwitching);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write allowGroupSwitching: %d",
+                      result);
+                return result;
+            }
             result = reply->writeFloat(primaryRefreshRateMin);
             if (result != NO_ERROR) {
                 ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
@@ -2055,6 +2047,40 @@
             }
             return NO_ERROR;
         }
+        case SET_FRAME_TIMELINE_VSYNC: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> binder;
+            status_t err = data.readStrongBinder(&binder);
+            if (err != NO_ERROR) {
+                ALOGE("setFrameTimelineVsync: failed to read strong binder: %s (%d)",
+                      strerror(-err), -err);
+                return err;
+            }
+            sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
+            if (!surface) {
+                ALOGE("setFrameTimelineVsync: failed to cast to IGraphicBufferProducer: %s (%d)",
+                      strerror(-err), -err);
+                return err;
+            }
+            int64_t frameTimelineVsyncId;
+            err = data.readInt64(&frameTimelineVsyncId);
+            if (err != NO_ERROR) {
+                ALOGE("setFrameTimelineVsync: failed to read int64_t: %s (%d)", strerror(-err),
+                      -err);
+                return err;
+            }
+
+            status_t result = setFrameTimelineVsync(surface, frameTimelineVsyncId);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case ADD_TRANSACTION_TRACE_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<gui::ITransactionTraceListener> listener;
+            SAFE_PARCEL(data.readStrongBinder, &listener);
+
+            return addTransactionTraceListener(listener);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 621cf59..5e7a7ec 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -50,12 +50,12 @@
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
                            uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
                            sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
-                           uint32_t* outTransformHint) override {
+                           int32_t* outLayerId, uint32_t* outTransformHint) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
                                                                             std::move(metadata),
-                                                                            handle, gbp,
+                                                                            handle, gbp, outLayerId,
                                                                             outTransformHint);
     }
 
@@ -63,14 +63,14 @@
                                      PixelFormat format, uint32_t flags,
                                      const sp<IGraphicBufferProducer>& parent,
                                      LayerMetadata metadata, sp<IBinder>* handle,
-                                     sp<IGraphicBufferProducer>* gbp,
+                                     sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                      uint32_t* outTransformHint) override {
         return callRemote<decltype(
                 &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
                                                                    name, width, height, format,
                                                                    flags, parent,
                                                                    std::move(metadata), handle, gbp,
-                                                                   outTransformHint);
+                                                                   outLayerId, outTransformHint);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -85,10 +85,11 @@
                                                               outStats);
     }
 
-    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+                           int32_t* outLayerId) override {
         return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
                                                                             mirrorFromHandle,
-                                                                            outHandle);
+                                                                            outHandle, outLayerId);
     }
 };
 
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index b3eb994..30c9b37 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -122,6 +122,8 @@
             return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
         case view::LayerMetadataKey::METADATA_TASK_ID:
             return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+        case view::LayerMetadataKey::METADATA_OWNER_PID:
+            return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
         default:
             return StringPrintf("%d%s%dbytes", key, separator,
                                 static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e43446a..9722f36 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -18,188 +18,272 @@
 
 #include <inttypes.h>
 
-#include <utils/Errors.h>
 #include <binder/Parcel.h>
-#include <gui/ISurfaceComposerClient.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
+#include <utils/Errors.h>
 
 #include <cmath>
 
 namespace android {
 
+layer_state_t::layer_state_t()
+      : what(0),
+        x(0),
+        y(0),
+        z(0),
+        w(0),
+        h(0),
+        layerStack(0),
+        alpha(0),
+        flags(0),
+        mask(0),
+        reserved(0),
+        crop_legacy(Rect::INVALID_RECT),
+        cornerRadius(0.0f),
+        backgroundBlurRadius(0),
+        barrierFrameNumber(0),
+        transform(0),
+        transformToDisplayInverse(false),
+        crop(Rect::INVALID_RECT),
+        orientedDisplaySpaceRect(Rect::INVALID_RECT),
+        dataspace(ui::Dataspace::UNKNOWN),
+        surfaceDamageRegion(),
+        api(-1),
+        colorTransform(mat4()),
+        bgColorAlpha(0),
+        bgColorDataspace(ui::Dataspace::UNKNOWN),
+        colorSpaceAgnostic(false),
+        shadowRadius(0.0f),
+        frameRateSelectionPriority(-1),
+        frameRate(0.0f),
+        frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+        fixedTransformHint(ui::Transform::ROT_INVALID),
+        frameNumber(0) {
+    matrix.dsdx = matrix.dtdy = 1.0f;
+    matrix.dsdy = matrix.dtdx = 0.0f;
+    hdrMetadata.validTypes = 0;
+}
+
 status_t layer_state_t::write(Parcel& output) const
 {
-    output.writeStrongBinder(surface);
-    output.writeUint64(what);
-    output.writeFloat(x);
-    output.writeFloat(y);
-    output.writeInt32(z);
-    output.writeUint32(w);
-    output.writeUint32(h);
-    output.writeUint32(layerStack);
-    output.writeFloat(alpha);
-    output.writeUint32(flags);
-    output.writeUint32(mask);
-    *reinterpret_cast<layer_state_t::matrix22_t *>(
-            output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
-    output.write(crop_legacy);
-    output.writeStrongBinder(barrierHandle_legacy);
-    output.writeStrongBinder(reparentHandle);
-    output.writeUint64(frameNumber_legacy);
-    output.writeInt32(overrideScalingMode);
-    output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy));
-    output.writeStrongBinder(relativeLayerHandle);
-    output.writeStrongBinder(parentHandleForChild);
-    output.writeFloat(color.r);
-    output.writeFloat(color.g);
-    output.writeFloat(color.b);
+    SAFE_PARCEL(output.writeStrongBinder, surface);
+    SAFE_PARCEL(output.writeInt32, layerId);
+    SAFE_PARCEL(output.writeUint64, what);
+    SAFE_PARCEL(output.writeFloat, x);
+    SAFE_PARCEL(output.writeFloat, y);
+    SAFE_PARCEL(output.writeInt32, z);
+    SAFE_PARCEL(output.writeUint32, w);
+    SAFE_PARCEL(output.writeUint32, h);
+    SAFE_PARCEL(output.writeUint32, layerStack);
+    SAFE_PARCEL(output.writeFloat, alpha);
+    SAFE_PARCEL(output.writeUint32, flags);
+    SAFE_PARCEL(output.writeUint32, mask);
+    SAFE_PARCEL(matrix.write, output);
+    SAFE_PARCEL(output.write, crop_legacy);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
+    SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
+    SAFE_PARCEL(output.writeFloat, color.r);
+    SAFE_PARCEL(output.writeFloat, color.g);
+    SAFE_PARCEL(output.writeFloat, color.b);
 #ifndef NO_INPUT
-    inputInfo.write(output);
+    SAFE_PARCEL(inputHandle->writeToParcel, &output);
 #endif
-    output.write(transparentRegion);
-    output.writeUint32(transform);
-    output.writeBool(transformToDisplayInverse);
-    output.write(crop);
-    output.write(frame);
+    SAFE_PARCEL(output.write, transparentRegion);
+    SAFE_PARCEL(output.writeUint32, transform);
+    SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
+    SAFE_PARCEL(output.write, crop);
+    SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+
     if (buffer) {
-        output.writeBool(true);
-        output.write(*buffer);
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.write, *buffer);
     } else {
-        output.writeBool(false);
+        SAFE_PARCEL(output.writeBool, false);
     }
+
     if (acquireFence) {
-        output.writeBool(true);
-        output.write(*acquireFence);
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.write, *acquireFence);
     } else {
-        output.writeBool(false);
+        SAFE_PARCEL(output.writeBool, false);
     }
-    output.writeUint32(static_cast<uint32_t>(dataspace));
-    output.write(hdrMetadata);
-    output.write(surfaceDamageRegion);
-    output.writeInt32(api);
+
+    SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
+    SAFE_PARCEL(output.write, hdrMetadata);
+    SAFE_PARCEL(output.write, surfaceDamageRegion);
+    SAFE_PARCEL(output.writeInt32, api);
+
     if (sidebandStream) {
-        output.writeBool(true);
-        output.writeNativeHandle(sidebandStream->handle());
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.writeNativeHandle, sidebandStream->handle());
     } else {
-        output.writeBool(false);
+        SAFE_PARCEL(output.writeBool, false);
     }
 
-    memcpy(output.writeInplace(16 * sizeof(float)),
-           colorTransform.asArray(), 16 * sizeof(float));
-    output.writeFloat(cornerRadius);
-    output.writeUint32(backgroundBlurRadius);
-    output.writeStrongBinder(cachedBuffer.token.promote());
-    output.writeUint64(cachedBuffer.id);
-    output.writeParcelable(metadata);
-
-    output.writeFloat(bgColorAlpha);
-    output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
-    output.writeBool(colorSpaceAgnostic);
-
-    auto err = output.writeVectorSize(listeners);
-    if (err) {
-        return err;
-    }
+    SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
+    SAFE_PARCEL(output.writeFloat, cornerRadius);
+    SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
+    SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
+    SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+    SAFE_PARCEL(output.writeParcelable, metadata);
+    SAFE_PARCEL(output.writeFloat, bgColorAlpha);
+    SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
+    SAFE_PARCEL(output.writeBool, colorSpaceAgnostic);
+    SAFE_PARCEL(output.writeVectorSize, listeners);
 
     for (auto listener : listeners) {
-        err = output.writeStrongBinder(listener.transactionCompletedListener);
-        if (err) {
-            return err;
-        }
-        err = output.writeInt64Vector(listener.callbackIds);
-        if (err) {
-            return err;
-        }
+        SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
+        SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds);
     }
-    output.writeFloat(shadowRadius);
-    output.writeInt32(frameRateSelectionPriority);
-    output.writeFloat(frameRate);
-    output.writeByte(frameRateCompatibility);
-    output.writeUint32(fixedTransformHint);
+    SAFE_PARCEL(output.writeFloat, shadowRadius);
+    SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
+    SAFE_PARCEL(output.writeFloat, frameRate);
+    SAFE_PARCEL(output.writeByte, frameRateCompatibility);
+    SAFE_PARCEL(output.writeUint32, fixedTransformHint);
+    SAFE_PARCEL(output.writeUint64, frameNumber);
+    SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
+
+    SAFE_PARCEL(output.writeUint32, blurRegions.size());
+    for (auto region : blurRegions) {
+        SAFE_PARCEL(output.writeUint32, region.blurRadius);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR);
+        SAFE_PARCEL(output.writeFloat, region.alpha);
+        SAFE_PARCEL(output.writeInt32, region.left);
+        SAFE_PARCEL(output.writeInt32, region.top);
+        SAFE_PARCEL(output.writeInt32, region.right);
+        SAFE_PARCEL(output.writeInt32, region.bottom);
+    }
     return NO_ERROR;
 }
 
 status_t layer_state_t::read(const Parcel& input)
 {
-    surface = input.readStrongBinder();
-    what = input.readUint64();
-    x = input.readFloat();
-    y = input.readFloat();
-    z = input.readInt32();
-    w = input.readUint32();
-    h = input.readUint32();
-    layerStack = input.readUint32();
-    alpha = input.readFloat();
-    flags = static_cast<uint8_t>(input.readUint32());
-    mask = static_cast<uint8_t>(input.readUint32());
-    const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t));
-    if (matrix_data) {
-        matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(matrix_data);
-    } else {
-        return BAD_VALUE;
-    }
-    input.read(crop_legacy);
-    barrierHandle_legacy = input.readStrongBinder();
-    reparentHandle = input.readStrongBinder();
-    frameNumber_legacy = input.readUint64();
-    overrideScalingMode = input.readInt32();
-    barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
-    relativeLayerHandle = input.readStrongBinder();
-    parentHandleForChild = input.readStrongBinder();
-    color.r = input.readFloat();
-    color.g = input.readFloat();
-    color.b = input.readFloat();
+    SAFE_PARCEL(input.readNullableStrongBinder, &surface);
+    SAFE_PARCEL(input.readInt32, &layerId);
+    SAFE_PARCEL(input.readUint64, &what);
+    SAFE_PARCEL(input.readFloat, &x);
+    SAFE_PARCEL(input.readFloat, &y);
+    SAFE_PARCEL(input.readInt32, &z);
+    SAFE_PARCEL(input.readUint32, &w);
+    SAFE_PARCEL(input.readUint32, &h);
+    SAFE_PARCEL(input.readUint32, &layerStack);
+    SAFE_PARCEL(input.readFloat, &alpha);
 
+    uint32_t tmpUint32 = 0;
+    SAFE_PARCEL(input.readUint32, &tmpUint32);
+    flags = static_cast<uint8_t>(tmpUint32);
+
+    SAFE_PARCEL(input.readUint32, &tmpUint32);
+    mask = static_cast<uint8_t>(tmpUint32);
+
+    SAFE_PARCEL(matrix.read, input);
+    SAFE_PARCEL(input.read, crop_legacy);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
+    SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
+
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+
+    float tmpFloat = 0;
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    color.r = tmpFloat;
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    color.g = tmpFloat;
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    color.b = tmpFloat;
 #ifndef NO_INPUT
-    inputInfo = InputWindowInfo::read(input);
+    SAFE_PARCEL(inputHandle->readFromParcel, &input);
 #endif
 
-    input.read(transparentRegion);
-    transform = input.readUint32();
-    transformToDisplayInverse = input.readBool();
-    input.read(crop);
-    input.read(frame);
-    buffer = new GraphicBuffer();
-    if (input.readBool()) {
-        input.read(*buffer);
+    SAFE_PARCEL(input.read, transparentRegion);
+    SAFE_PARCEL(input.readUint32, &transform);
+    SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
+    SAFE_PARCEL(input.read, crop);
+    SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+
+    bool tmpBool = false;
+    SAFE_PARCEL(input.readBool, &tmpBool);
+    if (tmpBool) {
+        buffer = new GraphicBuffer();
+        SAFE_PARCEL(input.read, *buffer);
     }
-    acquireFence = new Fence();
-    if (input.readBool()) {
-        input.read(*acquireFence);
+
+    SAFE_PARCEL(input.readBool, &tmpBool);
+    if (tmpBool) {
+        acquireFence = new Fence();
+        SAFE_PARCEL(input.read, *acquireFence);
     }
-    dataspace = static_cast<ui::Dataspace>(input.readUint32());
-    input.read(hdrMetadata);
-    input.read(surfaceDamageRegion);
-    api = input.readInt32();
-    if (input.readBool()) {
+
+    SAFE_PARCEL(input.readUint32, &tmpUint32);
+    dataspace = static_cast<ui::Dataspace>(tmpUint32);
+
+    SAFE_PARCEL(input.read, hdrMetadata);
+    SAFE_PARCEL(input.read, surfaceDamageRegion);
+    SAFE_PARCEL(input.readInt32, &api);
+    SAFE_PARCEL(input.readBool, &tmpBool);
+    if (tmpBool) {
         sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
     }
 
-    colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
-    cornerRadius = input.readFloat();
-    backgroundBlurRadius = input.readUint32();
-    cachedBuffer.token = input.readStrongBinder();
-    cachedBuffer.id = input.readUint64();
-    input.readParcelable(&metadata);
+    SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
+    SAFE_PARCEL(input.readFloat, &cornerRadius);
+    SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
+    sp<IBinder> tmpBinder;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    cachedBuffer.token = tmpBinder;
+    SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+    SAFE_PARCEL(input.readParcelable, &metadata);
 
-    bgColorAlpha = input.readFloat();
-    bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
-    colorSpaceAgnostic = input.readBool();
+    SAFE_PARCEL(input.readFloat, &bgColorAlpha);
+    SAFE_PARCEL(input.readUint32, &tmpUint32);
+    bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32);
+    SAFE_PARCEL(input.readBool, &colorSpaceAgnostic);
 
-    int32_t numListeners = input.readInt32();
+    int32_t numListeners = 0;
+    SAFE_PARCEL_READ_SIZE(input.readInt32, &numListeners, input.dataSize());
     listeners.clear();
     for (int i = 0; i < numListeners; i++) {
-        auto listener = input.readStrongBinder();
+        sp<IBinder> listener;
         std::vector<CallbackId> callbackIds;
-        input.readInt64Vector(&callbackIds);
+        SAFE_PARCEL(input.readNullableStrongBinder, &listener);
+        SAFE_PARCEL(input.readInt64Vector, &callbackIds);
         listeners.emplace_back(listener, callbackIds);
     }
-    shadowRadius = input.readFloat();
-    frameRateSelectionPriority = input.readInt32();
-    frameRate = input.readFloat();
-    frameRateCompatibility = input.readByte();
-    fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
+    SAFE_PARCEL(input.readFloat, &shadowRadius);
+    SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
+    SAFE_PARCEL(input.readFloat, &frameRate);
+    SAFE_PARCEL(input.readByte, &frameRateCompatibility);
+    SAFE_PARCEL(input.readUint32, &tmpUint32);
+    fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
+    SAFE_PARCEL(input.readUint64, &frameNumber);
+    SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId);
+
+    uint32_t numRegions = 0;
+    SAFE_PARCEL(input.readUint32, &numRegions);
+    blurRegions.clear();
+    for (uint32_t i = 0; i < numRegions; i++) {
+        BlurRegion region;
+        SAFE_PARCEL(input.readUint32, &region.blurRadius);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTR);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBR);
+        SAFE_PARCEL(input.readFloat, &region.alpha);
+        SAFE_PARCEL(input.readInt32, &region.left);
+        SAFE_PARCEL(input.readInt32, &region.top);
+        SAFE_PARCEL(input.readInt32, &region.right);
+        SAFE_PARCEL(input.readInt32, &region.bottom);
+        blurRegions.push_back(region);
+    }
     return NO_ERROR;
 }
 
@@ -211,39 +295,43 @@
     return state.read(input);
 }
 
-
-DisplayState::DisplayState() :
-    what(0),
-    layerStack(0),
-    viewport(Rect::EMPTY_RECT),
-    frame(Rect::EMPTY_RECT),
-    width(0),
-    height(0) {
-}
+DisplayState::DisplayState()
+      : what(0),
+        layerStack(0),
+        layerStackSpaceRect(Rect::EMPTY_RECT),
+        orientedDisplaySpaceRect(Rect::EMPTY_RECT),
+        width(0),
+        height(0) {}
 
 status_t DisplayState::write(Parcel& output) const {
-    output.writeStrongBinder(token);
-    output.writeStrongBinder(IInterface::asBinder(surface));
-    output.writeUint32(what);
-    output.writeUint32(layerStack);
-    output.writeUint32(toRotationInt(orientation));
-    output.write(viewport);
-    output.write(frame);
-    output.writeUint32(width);
-    output.writeUint32(height);
+    SAFE_PARCEL(output.writeStrongBinder, token);
+    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
+    SAFE_PARCEL(output.writeUint32, what);
+    SAFE_PARCEL(output.writeUint32, layerStack);
+    SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
+    SAFE_PARCEL(output.write, layerStackSpaceRect);
+    SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+    SAFE_PARCEL(output.writeUint32, width);
+    SAFE_PARCEL(output.writeUint32, height);
     return NO_ERROR;
 }
 
 status_t DisplayState::read(const Parcel& input) {
-    token = input.readStrongBinder();
-    surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
-    what = input.readUint32();
-    layerStack = input.readUint32();
-    orientation = ui::toRotation(input.readUint32());
-    input.read(viewport);
-    input.read(frame);
-    width = input.readUint32();
-    height = input.readUint32();
+    SAFE_PARCEL(input.readStrongBinder, &token);
+    sp<IBinder> tmpBinder;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
+
+    SAFE_PARCEL(input.readUint32, &what);
+    SAFE_PARCEL(input.readUint32, &layerStack);
+    uint32_t tmpUint = 0;
+    SAFE_PARCEL(input.readUint32, &tmpUint);
+    orientation = ui::toRotation(tmpUint);
+
+    SAFE_PARCEL(input.read, layerStackSpaceRect);
+    SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+    SAFE_PARCEL(input.readUint32, &width);
+    SAFE_PARCEL(input.readUint32, &height);
     return NO_ERROR;
 }
 
@@ -259,8 +347,8 @@
     if (other.what & eDisplayProjectionChanged) {
         what |= eDisplayProjectionChanged;
         orientation = other.orientation;
-        viewport = other.viewport;
-        frame = other.frame;
+        layerStackSpaceRect = other.layerStackSpaceRect;
+        orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
     }
     if (other.what & eDisplaySizeChanged) {
         what |= eDisplaySizeChanged;
@@ -319,19 +407,18 @@
         what |= eBackgroundBlurRadiusChanged;
         backgroundBlurRadius = other.backgroundBlurRadius;
     }
+    if (other.what & eBlurRegionsChanged) {
+        what |= eBlurRegionsChanged;
+        blurRegions = other.blurRegions;
+    }
     if (other.what & eDeferTransaction_legacy) {
         what |= eDeferTransaction_legacy;
-        barrierHandle_legacy = other.barrierHandle_legacy;
-        barrierGbp_legacy = other.barrierGbp_legacy;
-        frameNumber_legacy = other.frameNumber_legacy;
-    }
-    if (other.what & eOverrideScalingModeChanged) {
-        what |= eOverrideScalingModeChanged;
-        overrideScalingMode = other.overrideScalingMode;
+        barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
+        barrierFrameNumber = other.barrierFrameNumber;
     }
     if (other.what & eReparentChildren) {
         what |= eReparentChildren;
-        reparentHandle = other.reparentHandle;
+        reparentSurfaceControl = other.reparentSurfaceControl;
     }
     if (other.what & eDetachChildren) {
         what |= eDetachChildren;
@@ -340,11 +427,11 @@
         what |= eRelativeLayerChanged;
         what &= ~eLayerChanged;
         z = other.z;
-        relativeLayerHandle = other.relativeLayerHandle;
+        relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
     }
     if (other.what & eReparent) {
         what |= eReparent;
-        parentHandleForChild = other.parentHandleForChild;
+        parentSurfaceControlForChild = other.parentSurfaceControlForChild;
     }
     if (other.what & eDestroySurface) {
         what |= eDestroySurface;
@@ -363,7 +450,7 @@
     }
     if (other.what & eFrameChanged) {
         what |= eFrameChanged;
-        frame = other.frame;
+        orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
     }
     if (other.what & eBufferChanged) {
         what |= eBufferChanged;
@@ -404,7 +491,7 @@
 #ifndef NO_INPUT
     if (other.what & eInputInfoChanged) {
         what |= eInputInfoChanged;
-        inputInfo = other.inputInfo;
+        inputHandle = new InputWindowHandle(*other.inputHandle);
     }
 #endif
 
@@ -439,6 +526,10 @@
         what |= eFixedTransformHintChanged;
         fixedTransformHint = other.fixedTransformHint;
     }
+    if (other.what & eFrameNumberChanged) {
+        what |= eFrameNumberChanged;
+        frameNumber = other.frameNumber;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -446,22 +537,65 @@
     }
 }
 
+status_t layer_state_t::matrix22_t::write(Parcel& output) const {
+    SAFE_PARCEL(output.writeFloat, dsdx);
+    SAFE_PARCEL(output.writeFloat, dtdx);
+    SAFE_PARCEL(output.writeFloat, dtdy);
+    SAFE_PARCEL(output.writeFloat, dsdy);
+    return NO_ERROR;
+}
+
+status_t layer_state_t::matrix22_t::read(const Parcel& input) {
+    SAFE_PARCEL(input.readFloat, &dsdx);
+    SAFE_PARCEL(input.readFloat, &dtdx);
+    SAFE_PARCEL(input.readFloat, &dtdy);
+    SAFE_PARCEL(input.readFloat, &dsdy);
+    return NO_ERROR;
+}
+
 // ------------------------------- InputWindowCommands ----------------------------------------
 
-void InputWindowCommands::merge(const InputWindowCommands& other) {
+bool InputWindowCommands::merge(const InputWindowCommands& other) {
+    bool changes = false;
+#ifndef NO_INPUT
+    changes |= !other.focusRequests.empty();
+    focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
+                         std::make_move_iterator(other.focusRequests.end()));
+#endif
+    changes |= other.syncInputWindows && !syncInputWindows;
     syncInputWindows |= other.syncInputWindows;
+    return changes;
+}
+
+bool InputWindowCommands::empty() const {
+    bool empty = true;
+#ifndef NO_INPUT
+    empty = focusRequests.empty() && !syncInputWindows;
+#endif
+    return empty;
 }
 
 void InputWindowCommands::clear() {
+#ifndef NO_INPUT
+    focusRequests.clear();
+#endif
     syncInputWindows = false;
 }
 
-void InputWindowCommands::write(Parcel& output) const {
-    output.writeBool(syncInputWindows);
+status_t InputWindowCommands::write(Parcel& output) const {
+#ifndef NO_INPUT
+    SAFE_PARCEL(output.writeParcelableVector, focusRequests);
+#endif
+    SAFE_PARCEL(output.writeBool, syncInputWindows);
+    return NO_ERROR;
 }
 
-void InputWindowCommands::read(const Parcel& input) {
-    syncInputWindows = input.readBool();
+status_t InputWindowCommands::read(const Parcel& input) {
+#ifndef NO_INPUT
+    SAFE_PARCEL(input.readParcelableVector, &focusRequests);
+#endif
+    SAFE_PARCEL(input.readBool, &syncInputWindows);
+    return NO_ERROR;
 }
 
 bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
@@ -481,4 +615,110 @@
     return true;
 }
 
+// ----------------------------------------------------------------------------
+
+status_t CaptureArgs::write(Parcel& output) const {
+    SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat));
+    SAFE_PARCEL(output.write, sourceCrop);
+    SAFE_PARCEL(output.writeFloat, frameScale);
+    SAFE_PARCEL(output.writeBool, captureSecureLayers);
+    SAFE_PARCEL(output.writeInt32, uid);
+    SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
+    SAFE_PARCEL(output.writeBool, allowProtected);
+    return NO_ERROR;
+}
+
+status_t CaptureArgs::read(const Parcel& input) {
+    int32_t value = 0;
+    SAFE_PARCEL(input.readInt32, &value);
+    pixelFormat = static_cast<ui::PixelFormat>(value);
+    SAFE_PARCEL(input.read, sourceCrop);
+    SAFE_PARCEL(input.readFloat, &frameScale);
+    SAFE_PARCEL(input.readBool, &captureSecureLayers);
+    SAFE_PARCEL(input.readInt32, &uid);
+    SAFE_PARCEL(input.readInt32, &value);
+    dataspace = static_cast<ui::Dataspace>(value);
+    SAFE_PARCEL(input.readBool, &allowProtected);
+    return NO_ERROR;
+}
+
+status_t DisplayCaptureArgs::write(Parcel& output) const {
+    SAFE_PARCEL(CaptureArgs::write, output);
+
+    SAFE_PARCEL(output.writeStrongBinder, displayToken);
+    SAFE_PARCEL(output.writeUint32, width);
+    SAFE_PARCEL(output.writeUint32, height);
+    SAFE_PARCEL(output.writeBool, useIdentityTransform);
+    return NO_ERROR;
+}
+
+status_t DisplayCaptureArgs::read(const Parcel& input) {
+    SAFE_PARCEL(CaptureArgs::read, input);
+
+    SAFE_PARCEL(input.readStrongBinder, &displayToken);
+    SAFE_PARCEL(input.readUint32, &width);
+    SAFE_PARCEL(input.readUint32, &height);
+    SAFE_PARCEL(input.readBool, &useIdentityTransform);
+    return NO_ERROR;
+}
+
+status_t LayerCaptureArgs::write(Parcel& output) const {
+    SAFE_PARCEL(CaptureArgs::write, output);
+
+    SAFE_PARCEL(output.writeStrongBinder, layerHandle);
+    SAFE_PARCEL(output.writeInt32, excludeHandles.size());
+    for (auto el : excludeHandles) {
+        SAFE_PARCEL(output.writeStrongBinder, el);
+    }
+    SAFE_PARCEL(output.writeBool, childrenOnly);
+    return NO_ERROR;
+}
+
+status_t LayerCaptureArgs::read(const Parcel& input) {
+    SAFE_PARCEL(CaptureArgs::read, input);
+
+    SAFE_PARCEL(input.readStrongBinder, &layerHandle);
+
+    int32_t numExcludeHandles = 0;
+    SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize());
+    excludeHandles.reserve(numExcludeHandles);
+    for (int i = 0; i < numExcludeHandles; i++) {
+        sp<IBinder> binder;
+        SAFE_PARCEL(input.readStrongBinder, &binder);
+        excludeHandles.emplace(binder);
+    }
+
+    SAFE_PARCEL(input.readBool, &childrenOnly);
+    return NO_ERROR;
+}
+
+status_t ScreenCaptureResults::write(Parcel& output) const {
+    if (buffer != nullptr) {
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.write, *buffer);
+    } else {
+        SAFE_PARCEL(output.writeBool, false);
+    }
+    SAFE_PARCEL(output.writeBool, capturedSecureLayers);
+    SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace));
+    SAFE_PARCEL(output.writeInt32, result);
+    return NO_ERROR;
+}
+
+status_t ScreenCaptureResults::read(const Parcel& input) {
+    bool hasGraphicBuffer;
+    SAFE_PARCEL(input.readBool, &hasGraphicBuffer);
+    if (hasGraphicBuffer) {
+        buffer = new GraphicBuffer();
+        SAFE_PARCEL(input.read, *buffer);
+    }
+
+    SAFE_PARCEL(input.readBool, &capturedSecureLayers);
+    uint32_t dataspace = 0;
+    SAFE_PARCEL(input.readUint32, &dataspace);
+    capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+    SAFE_PARCEL(input.readInt32, &result);
+    return NO_ERROR;
+}
+
 }; // namespace android
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index cbb4b97..ecccf29 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -13,3 +13,13 @@
 vishnun@google.com
 
 per-file EndToEndNativeInputTest.cpp = svv@google.com
+
+# BufferQueue is feature-frozen
+per-file BufferQueue* = set noparent
+per-file BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file IGraphicBuffer* = set noparent
+per-file IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/BufferQueue* = set noparent
+per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/IGraphicBuffer* = set noparent
+per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
\ No newline at end of file
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1efd98b..c1155ab 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -63,7 +63,8 @@
 
 } // namespace
 
-Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
+Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,
+                 const sp<IBinder>& surfaceControlHandle)
       : mGraphicBufferProducer(bufferProducer),
         mCrop(Rect::EMPTY_RECT),
         mBufferAge(0),
@@ -110,7 +111,8 @@
     mConnectedToCpu = false;
     mProducerControlledByApp = controlledByApp;
     mSwapIntervalZero = false;
-    mMaxBufferCount = 0;
+    mMaxBufferCount = NUM_BUFFER_SLOTS;
+    mSurfaceControlHandle = surfaceControlHandle;
 }
 
 Surface::~Surface() {
@@ -732,6 +734,8 @@
         mSharedBufferHasBeenQueued = false;
     }
 
+    mDequeuedSlots.insert(buf);
+
     return OK;
 }
 
@@ -760,6 +764,8 @@
         mSharedBufferHasBeenQueued = true;
     }
 
+    mDequeuedSlots.erase(i);
+
     return OK;
 }
 
@@ -895,6 +901,8 @@
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
 
+    mDequeuedSlots.erase(i);
+
     if (mEnableFrameTimestamps) {
         mFrameEventHistory->applyDelta(output.frameTimestamps);
         // Update timestamps with the local acquire fence.
@@ -1201,6 +1209,9 @@
     case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
         res = dispatchGetLastQueuedBuffer(args);
         break;
+    case NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC:
+        res = dispatchSetFrameTimelineVsync(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1507,13 +1518,21 @@
     return result;
 }
 
+int Surface::dispatchSetFrameTimelineVsync(va_list args) {
+    ATRACE_CALL();
+    auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
+
+    ALOGV("Surface::dispatchSetFrameTimelineVsync");
+    return setFrameTimelineVsync(frameTimelineVsyncId);
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
 }
 
 int Surface::connect(int api) {
-    static sp<IProducerListener> listener = new DummyProducerListener();
+    static sp<IProducerListener> listener = new StubProducerListener();
     return connect(api, listener);
 }
 
@@ -1585,6 +1604,7 @@
         mStickyTransform = 0;
         mAutoPrerotation = false;
         mEnableFrameTimestamps = false;
+        mMaxBufferCount = NUM_BUFFER_SLOTS;
 
         if (api == NATIVE_WINDOW_API_CPU) {
             mConnectedToCpu = false;
@@ -1659,6 +1679,7 @@
         mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
     }
     mSlots[attachedSlot].buffer = graphicBuffer;
+    mDequeuedSlots.insert(attachedSlot);
 
     return NO_ERROR;
 }
@@ -1925,6 +1946,10 @@
 }
 
 void Surface::freeAllBuffers() {
+    if (!mDequeuedSlots.empty()) {
+        ALOGE("%s: %zu buffers were freed while being dequeued!",
+                __FUNCTION__, mDequeuedSlots.size());
+    }
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         mSlots[i].buffer = nullptr;
     }
@@ -1946,6 +1971,10 @@
             ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i);
             continue;
         }
+        // Don't flush currently dequeued buffers
+        if (mDequeuedSlots.count(i) > 0) {
+            continue;
+        }
         outBuffers->push_back(mSlots[i].buffer);
         mSlots[i].buffer = nullptr;
     }
@@ -2261,4 +2290,9 @@
     return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
 }
 
+status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
+    return composerService()->setFrameTimelineVsync(mGraphicBufferProducer,
+        frameTimelineVsyncId);
+}
+
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 88232fa..039e900 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -348,13 +348,25 @@
 
 // ---------------------------------------------------------------------------
 
+// Initialize transaction id counter used to generate transaction ids
+// Transactions will start counting at 1, 0 is used for invalid transactions
+std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
+
+SurfaceComposerClient::Transaction::Transaction() {
+    mId = generateId();
+}
+
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
-      : mForceSynchronous(other.mForceSynchronous),
+      : mId(other.mId),
+        mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeup(other.mEarlyWakeup),
+        mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+        mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
-        mDesiredPresentTime(other.mDesiredPresentTime) {
+        mDesiredPresentTime(other.mDesiredPresentTime),
+        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -370,13 +382,20 @@
     return nullptr;
 }
 
+int64_t SurfaceComposerClient::Transaction::generateId() {
+    return (((int64_t)getpid()) << 32) | idCounter++;
+}
+
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
     const uint32_t forceSynchronous = parcel->readUint32();
     const uint32_t transactionNestCount = parcel->readUint32();
     const bool animation = parcel->readBool();
     const bool earlyWakeup = parcel->readBool();
+    const bool explicitEarlyWakeupStart = parcel->readBool();
+    const bool explicitEarlyWakeupEnd = parcel->readBool();
     const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
+    const int64_t frameTimelineVsyncId = parcel->readInt64();
 
     size_t count = static_cast<size_t>(parcel->readUint32());
     if (count > parcel->dataSize()) {
@@ -414,7 +433,7 @@
         }
         for (size_t j = 0; j < numSurfaces; j++) {
             sp<SurfaceControl> surface;
-            surface = SurfaceControl::readFromParcel(parcel);
+            SAFE_PARCEL(SurfaceControl::readFromParcel, *parcel, &surface);
             listenerCallbacks[listener].surfaceControls.insert(surface);
         }
     }
@@ -426,12 +445,14 @@
     std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
     composerStates.reserve(count);
     for (size_t i = 0; i < count; i++) {
-        sp<IBinder> surfaceControlHandle = parcel->readStrongBinder();
+        sp<IBinder> surfaceControlHandle;
+        SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle);
 
         ComposerState composerState;
         if (composerState.read(*parcel) == BAD_VALUE) {
             return BAD_VALUE;
         }
+
         composerStates[surfaceControlHandle] = composerState;
     }
 
@@ -443,8 +464,11 @@
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
     mEarlyWakeup = earlyWakeup;
+    mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+    mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
     mDisplayStates = displayStates;
     mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
@@ -470,8 +494,11 @@
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
     parcel->writeBool(mEarlyWakeup);
+    parcel->writeBool(mExplicitEarlyWakeupStart);
+    parcel->writeBool(mExplicitEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
+    parcel->writeInt64(mFrameTimelineVsyncId);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
     for (auto const& displayState : mDisplayStates) {
         displayState.write(*parcel);
@@ -486,13 +513,13 @@
         }
         parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
         for (auto surfaceControl : callbackInfo.surfaceControls) {
-            surfaceControl->writeToParcel(parcel);
+            SAFE_PARCEL(surfaceControl->writeToParcel, *parcel);
         }
     }
 
     parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
-    for (auto const& [surfaceHandle, composerState] : mComposerStates) {
-        parcel->writeStrongBinder(surfaceHandle);
+    for (auto const& [handle, composerState] : mComposerStates) {
+        SAFE_PARCEL(parcel->writeStrongBinder, handle);
         composerState.write(*parcel);
     }
 
@@ -501,11 +528,11 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
-    for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
-        if (mComposerStates.count(surfaceHandle) == 0) {
-            mComposerStates[surfaceHandle] = composerState;
+    for (auto const& [handle, composerState] : other.mComposerStates) {
+        if (mComposerStates.count(handle) == 0) {
+            mComposerStates[handle] = composerState;
         } else {
-            mComposerStates[surfaceHandle].state.merge(composerState.state);
+            mComposerStates[handle].state.merge(composerState.state);
         }
     }
 
@@ -545,6 +572,17 @@
 
     mContainsBuffer |= other.mContainsBuffer;
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
+    mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+    mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+
+    // When merging vsync Ids we take the oldest one
+    if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
+        other.mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+        mFrameTimelineVsyncId = std::max(mFrameTimelineVsyncId, other.mFrameTimelineVsyncId);
+    } else if (mFrameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID) {
+        mFrameTimelineVsyncId = other.mFrameTimelineVsyncId;
+    }
+
     other.clear();
     return *this;
 }
@@ -559,7 +597,10 @@
     mTransactionNestCount = 0;
     mAnimation = false;
     mEarlyWakeup = false;
+    mExplicitEarlyWakeupStart = false;
+    mExplicitEarlyWakeupEnd = false;
     mDesiredPresentTime = -1;
+    mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -570,7 +611,8 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
+    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
+                            uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -580,7 +622,7 @@
 
     size_t count = 0;
     for (auto& [handle, cs] : mComposerStates) {
-        layer_state_t* s = getLayerState(handle);
+        layer_state_t* s = &(mComposerStates[handle].state);
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
         } else if (s->what & layer_state_t::eCachedBufferChanged) {
@@ -653,8 +695,6 @@
         }
     }
 
-    mListenerCallbacks.clear();
-
     cacheBuffers();
 
     Vector<ComposerState> composerStates;
@@ -667,10 +707,7 @@
         composerStates.add(kv.second);
     }
 
-    mComposerStates.clear();
-
-    displayStates = mDisplayStates;
-    mDisplayStates.clear();
+    displayStates = std::move(mDisplayStates);
 
     if (mForceSynchronous) {
         flags |= ISurfaceComposer::eSynchronous;
@@ -682,16 +719,25 @@
         flags |= ISurfaceComposer::eEarlyWakeup;
     }
 
-    mForceSynchronous = false;
-    mAnimation = false;
-    mEarlyWakeup = false;
+    // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+    // it is equivalent for none
+    if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+        flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+    }
+    if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+        flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+    }
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
-                            mDesiredPresentTime,
+    sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
+                            mInputWindowCommands, mDesiredPresentTime,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
-                            hasListenerCallbacks, listenerCallbacks);
-    mInputWindowCommands.clear();
+                            hasListenerCallbacks, listenerCallbacks, mId);
+    mId = generateId();
+
+    // Clear the current states and flags
+    clear();
+
     mStatus = NO_ERROR;
     return NO_ERROR;
 }
@@ -731,11 +777,24 @@
     mEarlyWakeup = true;
 }
 
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+    mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+    mExplicitEarlyWakeupEnd = true;
+}
+
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
+    auto handle = sc->getHandle();
+
     if (mComposerStates.count(handle) == 0) {
         // we don't have it, add an initialized layer_state to our list
         ComposerState s;
+
         s.state.surface = handle;
+        s.state.layerId = sc->getLayerId();
+
         mComposerStates[handle] = s;
     }
 
@@ -806,8 +865,8 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(const sp<SurfaceControl>& sc, const sp<IBinder>& relativeTo,
-        int32_t z) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(
+        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -815,7 +874,7 @@
     }
     s->what |= layer_state_t::eRelativeLayerChanged;
     s->what &= ~layer_state_t::eLayerChanged;
-    s->relativeLayerHandle = relativeTo;
+    s->relativeLayerSurfaceControl = relativeTo;
     s->z = z;
 
     registerSurfaceControlForCallback(sc);
@@ -830,9 +889,8 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    if ((mask & layer_state_t::eLayerOpaque) ||
-            (mask & layer_state_t::eLayerHidden) ||
-            (mask & layer_state_t::eLayerSecure)) {
+    if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
+        (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot)) {
         s->what |= layer_state_t::eFlagsChanged;
     }
     s->flags &= ~mask;
@@ -959,65 +1017,58 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
-                                                                 const sp<IBinder>& handle,
-                                                                 uint64_t frameNumber) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions(
+        const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eDeferTransaction_legacy;
-    s->barrierHandle_legacy = handle;
-    s->frameNumber_legacy = frameNumber;
-
-    registerSurfaceControlForCallback(sc);
+    s->what |= layer_state_t::eBlurRegionsChanged;
+    s->blurRegions = blurRegions;
     return *this;
 }
 
 SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
-                                                                 const sp<Surface>& barrierSurface,
-                                                                 uint64_t frameNumber) {
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
+        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
+        uint64_t frameNumber) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
     s->what |= layer_state_t::eDeferTransaction_legacy;
-    s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
-    s->frameNumber_legacy = frameNumber;
+    s->barrierSurfaceControl_legacy = barrierSurfaceControl;
+    s->barrierFrameNumber = frameNumber;
 
     registerSurfaceControlForCallback(sc);
     return *this;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren(
-        const sp<SurfaceControl>& sc,
-        const sp<IBinder>& newParentHandle) {
+        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
     s->what |= layer_state_t::eReparentChildren;
-    s->reparentHandle = newParentHandle;
+    s->reparentSurfaceControl = newParent;
 
     registerSurfaceControlForCallback(sc);
     return *this;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent(
-        const sp<SurfaceControl>& sc,
-        const sp<IBinder>& newParentHandle) {
+        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
     s->what |= layer_state_t::eReparent;
-    s->parentHandleForChild = newParentHandle;
+    s->parentSurfaceControlForChild = newParent;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1106,7 +1157,7 @@
         return *this;
     }
     s->what |= layer_state_t::eFrameChanged;
-    s->frame = frame;
+    s->orientedDisplaySpaceRect = frame;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1277,6 +1328,20 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
+        const sp<SurfaceControl>& sc, uint64_t frameNumber) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eFrameNumberChanged;
+    s->frameNumber = frameNumber;
+
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
         const sp<SurfaceControl>& sc) {
     layer_state_t* s = getLayerState(sc);
@@ -1290,35 +1355,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverrideScalingMode(
-        const sp<SurfaceControl>& sc, int32_t overrideScalingMode) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-
-    switch (overrideScalingMode) {
-        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
-        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
-        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
-        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
-        case -1:
-            break;
-        default:
-            ALOGE("unknown scaling mode: %d",
-                    overrideScalingMode);
-            mStatus = BAD_VALUE;
-            return *this;
-    }
-
-    s->what |= layer_state_t::eOverrideScalingModeChanged;
-    s->overrideScalingMode = overrideScalingMode;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 #ifndef NO_INPUT
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
         const sp<SurfaceControl>& sc,
@@ -1328,11 +1364,28 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->inputInfo = info;
+    s->inputHandle = new InputWindowHandle(info);
     s->what |= layer_state_t::eInputInfoChanged;
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+        const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos,
+        int32_t displayId) {
+    FocusRequest request;
+    request.token = token;
+    request.focusedToken = focusedToken;
+    request.timestamp = timestampNanos;
+    request.displayId = displayId;
+    return setFocusedWindow(request);
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+        const FocusRequest& request) {
+    mInputWindowCommands.focusRequests.push_back(request);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
     mInputWindowCommands.syncInputWindows = true;
     return *this;
@@ -1453,6 +1506,25 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync(
+        int64_t frameTimelineVsyncId) {
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync(
+        const sp<SurfaceControl>& sc, int64_t frameTimelineVsyncId) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eFrameTimelineVsyncChanged;
+    s->frameTimelineVsyncId = frameTimelineVsyncId;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1499,8 +1571,8 @@
                                                               const Rect& displayRect) {
     DisplayState& s(getDisplayState(token));
     s.orientation = orientation;
-    s.viewport = layerStackRect;
-    s.frame = displayRect;
+    s.layerStackSpaceRect = layerStackRect;
+    s.orientedDisplaySpaceRect = displayRect;
     s.what |= DisplayState::eDisplayProjectionChanged;
     mForceSynchronous = true; // TODO: do we actually still need this?
 }
@@ -1568,11 +1640,11 @@
 
 sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
                                                         PixelFormat format, uint32_t flags,
-                                                        SurfaceControl* parent,
+                                                        const sp<IBinder>& parentHandle,
                                                         LayerMetadata metadata,
                                                         uint32_t* outTransformHint) {
     sp<SurfaceControl> s;
-    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+    createSurfaceChecked(name, w, h, format, &s, flags, parentHandle, std::move(metadata),
                          outTransformHint);
     return s;
 }
@@ -1591,14 +1663,16 @@
         sp<IGraphicBufferProducer> gbp;
 
         uint32_t transformHint = 0;
+        int32_t id = -1;
         err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
-                                               std::move(metadata), &handle, &gbp, &transformHint);
+                                               std::move(metadata), &handle, &gbp, &id,
+                                               &transformHint);
         if (outTransformHint) {
             *outTransformHint = transformHint;
         }
         ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
         if (err == NO_ERROR) {
-            return new SurfaceControl(this, handle, gbp, transformHint);
+            return new SurfaceControl(this, handle, gbp, id, transformHint);
         }
     }
     return nullptr;
@@ -1607,29 +1681,27 @@
 status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                      PixelFormat format,
                                                      sp<SurfaceControl>* outSurface, uint32_t flags,
-                                                     SurfaceControl* parent, LayerMetadata metadata,
+                                                     const sp<IBinder>& parentHandle,
+                                                     LayerMetadata metadata,
                                                      uint32_t* outTransformHint) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
     if (mStatus == NO_ERROR) {
         sp<IBinder> handle;
-        sp<IBinder> parentHandle;
         sp<IGraphicBufferProducer> gbp;
 
-        if (parent != nullptr) {
-            parentHandle = parent->getHandle();
-        }
-
         uint32_t transformHint = 0;
+        int32_t id = -1;
         err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
-                                     &handle, &gbp, &transformHint);
+                                     &handle, &gbp, &id, &transformHint);
+
         if (outTransformHint) {
             *outTransformHint = transformHint;
         }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, handle, gbp, transformHint);
+            *outSurface = new SurfaceControl(this, handle, gbp, id, transformHint);
         }
     }
     return err;
@@ -1642,9 +1714,10 @@
 
     sp<IBinder> handle;
     sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
-    status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+    int32_t layer_id = -1;
+    status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, handle, nullptr, true /* owned */);
+        return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */);
     }
     return nullptr;
 }
@@ -1712,27 +1785,24 @@
     return ComposerService::getComposerService()->getActiveConfig(display);
 }
 
-status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                             int32_t defaultConfig,
-                                                             float primaryRefreshRateMin,
-                                                             float primaryRefreshRateMax,
-                                                             float appRequestRefreshRateMin,
-                                                             float appRequestRefreshRateMax) {
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(
+        const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
+        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+        float appRequestRefreshRateMax) {
     return ComposerService::getComposerService()
-            ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
-                                           primaryRefreshRateMax, appRequestRefreshRateMin,
-                                           appRequestRefreshRateMax);
+            ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
+                                           primaryRefreshRateMin, primaryRefreshRateMax,
+                                           appRequestRefreshRateMin, appRequestRefreshRateMax);
 }
 
-status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                             int32_t* outDefaultConfig,
-                                                             float* outPrimaryRefreshRateMin,
-                                                             float* outPrimaryRefreshRateMax,
-                                                             float* outAppRequestRefreshRateMin,
-                                                             float* outAppRequestRefreshRateMax) {
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(
+        const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+        float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
+        float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
     return ComposerService::getComposerService()
-            ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin,
-                                           outPrimaryRefreshRateMax, outAppRequestRefreshRateMin,
+            ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outAllowGroupSwitching,
+                                           outPrimaryRefreshRateMin, outPrimaryRefreshRateMax,
+                                           outAppRequestRefreshRateMin,
                                            outAppRequestRefreshRateMax);
 }
 
@@ -1876,59 +1946,28 @@
 
 // ----------------------------------------------------------------------------
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
-                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ui::Rotation rotation, bool captureSecureLayers,
-                                   sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
+status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
+                                          const sp<IScreenCaptureListener>& captureListener) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
-                                    reqPixelFormat, sourceCrop, reqWidth, reqHeight,
-                                    useIdentityTransform, rotation, captureSecureLayers);
-    if (ret != NO_ERROR) {
-        return ret;
-    }
-    return ret;
+
+    return s->captureDisplay(captureArgs, captureListener);
 }
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
-                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) {
-    bool ignored;
-    return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
-                   useIdentityTransform, rotation, false, outBuffer, ignored);
-}
-
-status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
-                                   sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+                                          const sp<IScreenCaptureListener>& captureListener) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
+
+    return s->captureDisplay(displayOrLayerStack, captureListener);
 }
 
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
-                                         ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                         float frameScale, sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
+                                         const sp<IScreenCaptureListener>& captureListener) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
-                                    sourceCrop, {}, frameScale, false /* childrenOnly */);
-    return ret;
-}
 
-status_t ScreenshotClient::captureChildLayers(
-        const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat,
-        const Rect& sourceCrop,
-        const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
-        float frameScale, sp<GraphicBuffer>* outBuffer) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (s == nullptr) return NO_INIT;
-    status_t ret =
-            s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
-                             excludeHandles, frameScale, true /* childrenOnly */);
-    return ret;
+    return s->captureLayers(captureArgs, captureListener);
 }
 
 } // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a332a1f..e842382 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -24,6 +24,7 @@
 #include <android/native_window.h>
 
 #include <utils/Errors.h>
+#include <utils/KeyedVector.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
 
@@ -46,11 +47,12 @@
 // ============================================================================
 
 SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                               const sp<IGraphicBufferProducer>& gbp,
+                               const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
                                uint32_t transform)
       : mClient(client),
         mHandle(handle),
         mGraphicBufferProducer(gbp),
+        mLayerId(layerId),
         mTransformHint(transform) {}
 
 SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
@@ -58,6 +60,7 @@
     mHandle = other->mHandle;
     mGraphicBufferProducer = other->mGraphicBufferProducer;
     mTransformHint = other->mTransformHint;
+    mLayerId = other->mLayerId;
 }
 
 SurfaceControl::~SurfaceControl()
@@ -148,6 +151,10 @@
     return mHandle;
 }
 
+int32_t SurfaceControl::getLayerId() const {
+    return mLayerId;
+}
+
 sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
 {
     Mutex::Autolock _l(mLock);
@@ -169,31 +176,60 @@
     mTransformHint = hint;
 }
 
-void SurfaceControl::writeToParcel(Parcel* parcel)
-{
-    parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
-    parcel->writeStrongBinder(mHandle);
-    parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
-    parcel->writeUint32(mTransformHint);
+status_t SurfaceControl::writeToParcel(Parcel& parcel) {
+    SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
+    SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
+    SAFE_PARCEL(parcel.writeStrongBinder, IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+    SAFE_PARCEL(parcel.writeInt32, mLayerId);
+    SAFE_PARCEL(parcel.writeUint32, mTransformHint);
+
+    return NO_ERROR;
 }
 
-sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
-    sp<IBinder> client = parcel->readStrongBinder();
-    sp<IBinder> handle = parcel->readStrongBinder();
-    if (client == nullptr || handle == nullptr)
-    {
-        ALOGE("Invalid parcel");
-        return nullptr;
-    }
+status_t SurfaceControl::readFromParcel(const Parcel& parcel,
+                                        sp<SurfaceControl>* outSurfaceControl) {
+    sp<IBinder> client;
+    sp<IBinder> handle;
     sp<IBinder> gbp;
-    parcel->readNullableStrongBinder(&gbp);
+    int32_t layerId;
+    uint32_t transformHint;
 
-    uint32_t transformHint = parcel->readUint32();
+    SAFE_PARCEL(parcel.readStrongBinder, &client);
+    SAFE_PARCEL(parcel.readStrongBinder, &handle);
+    SAFE_PARCEL(parcel.readNullableStrongBinder, &gbp);
+    SAFE_PARCEL(parcel.readInt32, &layerId);
+    SAFE_PARCEL(parcel.readUint32, &transformHint);
+
     // We aren't the original owner of the surface.
-    return new SurfaceControl(new SurfaceComposerClient(
-                                      interface_cast<ISurfaceComposerClient>(client)),
-                              handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+    *outSurfaceControl =
+            new SurfaceControl(new SurfaceComposerClient(
+                                       interface_cast<ISurfaceComposerClient>(client)),
+                               handle.get(), interface_cast<IGraphicBufferProducer>(gbp), layerId,
                                transformHint);
+
+    return NO_ERROR;
+}
+
+status_t SurfaceControl::readNullableFromParcel(const Parcel& parcel,
+                                                sp<SurfaceControl>* outSurfaceControl) {
+    bool isNotNull;
+    SAFE_PARCEL(parcel.readBool, &isNotNull);
+    if (isNotNull) {
+        SAFE_PARCEL(SurfaceControl::readFromParcel, parcel, outSurfaceControl);
+    }
+
+    return NO_ERROR;
+}
+
+status_t SurfaceControl::writeNullableToParcel(Parcel& parcel,
+                                               const sp<SurfaceControl>& surfaceControl) {
+    auto isNotNull = surfaceControl != nullptr;
+    SAFE_PARCEL(parcel.writeBool, isNotNull);
+    if (isNotNull) {
+        SAFE_PARCEL(surfaceControl->writeToParcel, parcel);
+    }
+
+    return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index fcae05c..1a8fc1a 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -27,8 +27,6 @@
 
 #include <private/gui/SyncFeatures.h>
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
 namespace android {
 
 ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures);
@@ -40,8 +38,8 @@
     EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     // This can only be called after EGL has been initialized; otherwise the
     // check below will abort.
-    const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
-    LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
+    const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+    LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
     if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
         // This makes GLConsumer use the EGL_ANDROID_native_fence_sync
         // extension to create Android native fences to signal when all
@@ -73,15 +71,7 @@
     return mHasNativeFenceSync;
 }
 bool SyncFeatures::useFenceSync() const {
-#ifdef DONT_USE_FENCE_SYNC
-    // on some devices it's better to not use EGL_KHR_fence_sync
-    // even if they have it
-    return false;
-#else
-    // currently we shall only attempt to use EGL_KHR_fence_sync if
-    // USE_FENCE_SYNC is set in our makefile
     return !mHasNativeFenceSync && mHasFenceSync;
-#endif
 }
 bool SyncFeatures::useWaitSync() const {
     return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync;
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
new file mode 100644
index 0000000..eedc3df
--- /dev/null
+++ b/libs/gui/TransactionTracing.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gui/TransactionTracing.h"
+#include "gui/ISurfaceComposer.h"
+
+#include <private/gui/ComposerService.h>
+
+namespace android {
+
+sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr;
+std::mutex TransactionTraceListener::sMutex;
+
+TransactionTraceListener::TransactionTraceListener() {}
+
+sp<TransactionTraceListener> TransactionTraceListener::getInstance() {
+    const std::lock_guard<std::mutex> lock(sMutex);
+
+    if (sInstance == nullptr) {
+        sInstance = new TransactionTraceListener;
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        sf->addTransactionTraceListener(sInstance);
+    }
+
+    return sInstance;
+}
+
+binder::Status TransactionTraceListener::onToggled(bool enabled) {
+    ALOGD("TransactionTraceListener: onToggled listener called");
+    mTracingEnabled = enabled;
+
+    return binder::Status::ok();
+}
+
+bool TransactionTraceListener::isTracingEnabled() {
+    return mTracingEnabled;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
new file mode 100644
index 0000000..5cd12fd
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
@@ -0,0 +1,6 @@
+package android.gui;
+
+/** @hide */
+interface ITransactionTraceListener {
+   void onToggled(boolean enabled);
+}
\ No newline at end of file
diff --git a/libs/gui/bufferqueue/OWNERS b/libs/gui/bufferqueue/OWNERS
index cbe9317..615dd79 100644
--- a/libs/gui/bufferqueue/OWNERS
+++ b/libs/gui/bufferqueue/OWNERS
@@ -1,5 +1,4 @@
-chz@google.com
-lajos@google.com
-pawin@google.com
-taklee@google.com
-wonsik@google.com
+# BufferQueue is feature-frozen
+jreck@google.com
+sumir@google.com
+alecmouri@google.com
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2320771..2300e81 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -66,22 +66,27 @@
     : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
 {
 public:
-    BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
-                     bool enableTripleBuffering = true);
+    BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+                     int height, bool enableTripleBuffering = true);
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
         return mProducer;
     }
+    sp<Surface> getSurface(bool includeSurfaceControlHandle);
 
     void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
-    void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);}
+    void onFrameReplaced(const BufferItem& item) override;
     void onFrameAvailable(const BufferItem& item) override;
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
 
-    void update(const sp<SurfaceControl>& surface, int width, int height);
+    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
+    void flushShadowQueue() { mFlushShadowQueue = true; }
+
+    status_t setFrameRate(float frameRate, int8_t compatibility);
+    status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
 
     virtual ~BLASTBufferQueue() = default;
 
@@ -93,8 +98,12 @@
     BLASTBufferQueue(const BLASTBufferQueue& rhs);
 
     void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
-    Rect computeCrop(const BufferItem& item);
+    Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
+    // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
+    bool rejectBuffer(const BufferItem& item) const REQUIRES(mMutex);
+    bool maxBuffersAcquired() const REQUIRES(mMutex);
 
+    std::string mName;
     sp<SurfaceControl> mSurfaceControl;
 
     std::mutex mMutex;
@@ -106,17 +115,19 @@
 
     int32_t mNumFrameAvailable GUARDED_BY(mMutex);
     int32_t mNumAcquired GUARDED_BY(mMutex);
-
+    bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
     struct PendingReleaseItem {
         BufferItem item;
         sp<Fence> releaseFence;
     };
 
     std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+    // Keep a reference to the currently presented buffer so we can release it when the next buffer
+    // is ready to be presented.
     PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
 
-    int mWidth GUARDED_BY(mMutex);
-    int mHeight GUARDED_BY(mMutex);
+    uint32_t mWidth GUARDED_BY(mMutex);
+    uint32_t mHeight GUARDED_BY(mMutex);
 
     uint32_t mTransformHint GUARDED_BY(mMutex);
 
@@ -125,6 +136,9 @@
     sp<BLASTBufferItemConsumer> mBufferItemConsumer;
 
     SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+    // If set to true, the next queue buffer will wait until the shadow queue has been processed by
+    // the adapter.
+    bool mFlushShadowQueue = false;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f210c34..c65618b 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -20,6 +20,17 @@
 
 namespace android {
 
+struct VsyncEventData {
+    // The Vsync Id corresponsing to this vsync event. This will be used to
+    // populate ISurfaceComposer::setFrameTimelineVsync and
+    // SurfaceComposerClient::setFrameTimelineVsync
+    int64_t id = ISurfaceComposer::INVALID_VSYNC_ID;
+
+    // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+    // frame by (both on the CPU and the GPU)
+    int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+};
+
 class DisplayEventDispatcher : public LooperCallback {
 public:
     explicit DisplayEventDispatcher(
@@ -31,7 +42,7 @@
     status_t initialize();
     void dispose();
     status_t scheduleVsync();
-    void requestLatestConfig();
+    void injectEvent(const DisplayEventReceiver::Event& event);
     int getFd() const;
     virtual int handleEvent(int receiveFd, int events, void* data);
 
@@ -43,13 +54,17 @@
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
 
-    virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+    virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+                               VsyncEventData vsyncEventData) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
                                  bool connected) = 0;
     virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
                                        int32_t configId, nsecs_t vsyncPeriod) = 0;
+    // AChoreographer-specific hook for processing null-events so that looper
+    // can be properly poked.
+    virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
 
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
-                              uint32_t* outCount);
+                              uint32_t* outCount, VsyncEventData* outVsyncEventData);
 };
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 8d49184..df3118f 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -53,19 +53,27 @@
         DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
         DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
         DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
+        DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
     };
 
     struct Event {
+        // We add __attribute__((aligned(8))) for nsecs_t fields because
+        // we need to make sure all fields are aligned the same with x86
+        // and x64 (long long has different default alignment):
+        //
+        // https://en.wikipedia.org/wiki/Data_structure_alignment
 
         struct Header {
             uint32_t type;
-            PhysicalDisplayId displayId;
+            PhysicalDisplayId displayId __attribute__((aligned(8)));
             nsecs_t timestamp __attribute__((aligned(8)));
         };
 
         struct VSync {
             uint32_t count;
-            nsecs_t expectedVSyncTimestamp;
+            nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+            nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+            int64_t vsyncId;
         };
 
         struct Hotplug {
@@ -74,7 +82,7 @@
 
         struct Config {
             int32_t configId;
-            nsecs_t vsyncPeriod;
+            nsecs_t vsyncPeriod __attribute__((aligned(8)));
         };
 
         Header header;
@@ -130,6 +138,7 @@
      * sendEvents write events to the queue and returns how many events were
      * written.
      */
+    ssize_t sendEvents(Event const* events, size_t count);
     static ssize_t sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count);
 
     /*
@@ -146,12 +155,6 @@
      */
     status_t requestNextVsync();
 
-    /*
-     * requestLatestConfig() force-requests the current config for the primary
-     * display.
-     */
-    status_t requestLatestConfig();
-
 private:
     sp<IDisplayEventConnection> mEventConnection;
     std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ddd868d..2f538ff 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -499,7 +499,7 @@
     // protects static initialization
     static Mutex sStaticInitLock;
 
-    // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+    // mReleasedTexImageBuffer is a buffer placeholder used when in single buffer
     // mode and releaseTexImage() has been called
     static sp<GraphicBuffer> sReleasedTexImageBuffer;
     sp<EglImage> mReleasedTexImage;
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
deleted file mode 100644
index 7aa5432..0000000
--- a/libs/gui/include/gui/GuiConfig.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_CONFIG_H
-#define ANDROID_GUI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libgui configuration details to configStr.
-void appendGuiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_GUI_CONFIG_H*/
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index 674aafd..cff22a3 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -51,11 +51,6 @@
      * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
      */
     virtual void requestNextVsync() = 0; // Asynchronous
-
-    /*
-     * requestLatestConfig() requests the config for the primary display.
-     */
-    virtual void requestLatestConfig() = 0; // Asynchronous
 };
 
 class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index d7f3492..45e0a13 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -458,7 +458,7 @@
     // the producer wants to be notified when the consumer releases a buffer
     // back to the BufferQueue. It is also used to detect the death of the
     // producer. If only the latter functionality is desired, there is a
-    // DummyProducerListener class in IProducerListener.h that can be used.
+    // StubProducerListener class in IProducerListener.h that can be used.
     //
     // The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
     //
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 0b1f4b5..f7ffbb9 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -80,10 +80,9 @@
 class BnProducerListener : public IProducerListener {
 };
 #endif
-class DummyProducerListener : public BnProducerListener
-{
+class StubProducerListener : public BnProducerListener {
 public:
-    virtual ~DummyProducerListener();
+    virtual ~StubProducerListener();
     virtual void onBufferReleased() {}
     virtual bool needsReleaseNotify() { return false; }
 };
diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h
new file mode 100644
index 0000000..a2ddc9f
--- /dev/null
+++ b/libs/gui/include/gui/IScreenCaptureListener.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Binder.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+struct ScreenCaptureResults;
+
+// TODO(b/166271443): Convert to AIDL
+class IScreenCaptureListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(ScreenCaptureListener)
+
+    virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0;
+};
+
+class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> {
+public:
+    BnScreenCaptureListener()
+          : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 21ad2b2..5cd9356 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,16 +22,18 @@
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 
+#include <android/gui/ITransactionTraceListener.h>
+#include <gui/IScreenCaptureListener.h>
 #include <gui/ITransactionCompletedListener.h>
 
 #include <math/vec4.h>
 
 #include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayId.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
-#include <ui/PhysicalDisplayId.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rotation.h>
 
@@ -48,11 +50,14 @@
 
 struct client_cache_t;
 struct ComposerState;
+struct DisplayCaptureArgs;
 struct DisplayConfig;
 struct DisplayInfo;
 struct DisplayStatInfo;
 struct DisplayState;
 struct InputWindowCommands;
+struct LayerCaptureArgs;
+struct ScreenCaptureResults;
 class LayerDebugInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
@@ -81,12 +86,20 @@
     // flags for setTransactionState()
     enum {
         eSynchronous = 0x01,
-        eAnimation   = 0x02,
+        eAnimation = 0x02,
 
-        // Indicates that this transaction will likely result in a lot of layers being composed, and
-        // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
-        // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
-        eEarlyWakeup = 0x04
+        // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
+        eEarlyWakeup = 0x04,
+
+        // Explicit indication that this transaction and others to follow will likely result in a
+        // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
+        // missing frame deadlines. In this case SurfaceFlinger will wake up at
+        // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
+        // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+        // expected to be used by WindowManager only and are guarded by
+        // android.permission.ACCESS_SURFACE_FLINGER
+        eExplicitEarlyWakeupStart = 0x08,
+        eExplicitEarlyWakeupEnd = 0x10,
     };
 
     enum VsyncSource {
@@ -96,6 +109,9 @@
 
     enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
 
+    // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
+    static constexpr int64_t INVALID_VSYNC_ID = -1;
+
     /*
      * Create a connection with SurfaceFlinger.
      */
@@ -139,13 +155,12 @@
     }
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
-    virtual void setTransactionState(const Vector<ComposerState>& state,
-                                     const Vector<DisplayState>& displays, uint32_t flags,
-                                     const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& inputWindowCommands,
-                                     int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-                                     const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
+    virtual status_t setTransactionState(
+            int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+            const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -238,65 +253,17 @@
     /**
      * Capture the specified screen. This requires READ_FRAME_BUFFER
      * permission.  This function will fail if there is a secure window on
-     * screen.
+     * screen and DisplayCaptureArgs.captureSecureLayers is false.
      *
      * This function can capture a subregion (the source crop) of the screen.
      * The subregion can be optionally rotated.  It will also be scaled to
      * match the size of the output buffer.
-     *
-     * reqDataspace and reqPixelFormat specify the data space and pixel format
-     * of the buffer. The caller should pick the data space and pixel format
-     * that it can consume.
-     *
-     * sourceCrop is the crop on the logical display.
-     *
-     * reqWidth and reqHeight specifies the size of the buffer.  When either
-     * of them is 0, they are set to the size of the logical display viewport.
-     *
-     * When useIdentityTransform is true, layer transformations are disabled.
-     *
-     * rotation specifies the rotation of the source crop (and the pixels in
-     * it) around its center.
      */
-    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
-                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ui::Rotation rotation = ui::ROTATION_0,
-                                   bool captureSecureLayers = false) = 0;
-    /**
-     * Capture the specified screen. This requires READ_FRAME_BUFFER
-     * permission.  This function will fail if there is a secure window on
-     * screen.
-     *
-     * This function can capture a subregion (the source crop) of the screen
-     * into an sRGB buffer with RGBA_8888 pixel format.
-     * The subregion can be optionally rotated.  It will also be scaled to
-     * match the size of the output buffer.
-     *
-     * At the moment, sourceCrop is ignored and is always set to the visible
-     * region (projected display viewport) of the screen.
-     *
-     * reqWidth and reqHeight specifies the size of the buffer.  When either
-     * of them is 0, they are set to the size of the logical display viewport.
-     *
-     * When useIdentityTransform is true, layer transformations are disabled.
-     *
-     * rotation specifies the rotation of the source crop (and the pixels in
-     * it) around its center.
-     */
-    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   bool useIdentityTransform,
-                                   ui::Rotation rotation = ui::ROTATION_0) {
-        bool outIgnored;
-        return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
-                             ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
-                             useIdentityTransform, rotation);
-    }
+    virtual status_t captureDisplay(const DisplayCaptureArgs& args,
+                                    const sp<IScreenCaptureListener>& captureListener) = 0;
 
-    virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
-                                   sp<GraphicBuffer>* outBuffer) = 0;
+    virtual status_t captureDisplay(uint64_t displayOrLayerStack,
+                                    const sp<IScreenCaptureListener>& captureListener) = 0;
 
     template <class AA>
     struct SpHash {
@@ -305,27 +272,11 @@
 
     /**
      * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
-     *
-     * reqDataspace and reqPixelFormat specify the data space and pixel format
-     * of the buffer. The caller should pick the data space and pixel format
-     * that it can consume.
+     * This requires READ_FRAME_BUFFER permission. This function will fail if there
+     * is a secure window on screen
      */
-    virtual status_t captureLayers(
-            const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
-            ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-            const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
-            float frameScale = 1.0, bool childrenOnly = false) = 0;
-
-    /**
-     * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
-     * potentially ignoring the root node.
-     */
-    status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
-                           const Rect& sourceCrop, float frameScale = 1.0,
-                           bool childrenOnly = false) {
-        return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
-                             ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly);
-    }
+    virtual status_t captureLayers(const LayerCaptureArgs& args,
+                                   const sp<IScreenCaptureListener>& captureListener) = 0;
 
     /* Clears the frame statistics for animations.
      *
@@ -354,7 +305,7 @@
      *
      * Requires the ACCESS_SURFACE_FLINGER permission.
      */
-    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) = 0;
 
     virtual status_t getColorManagement(bool* outGetColorManagement) const = 0;
 
@@ -444,7 +395,7 @@
      * returned from getDisplayConfigs().
      */
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultConfig,
+                                                  int32_t defaultConfig, bool allowGroupSwitching,
                                                   float primaryRefreshRateMin,
                                                   float primaryRefreshRateMax,
                                                   float appRequestRefreshRateMin,
@@ -452,6 +403,7 @@
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                   int32_t* outDefaultConfig,
+                                                  bool* outAllowGroupSwitching,
                                                   float* outPrimaryRefreshRateMin,
                                                   float* outPrimaryRefreshRateMax,
                                                   float* outAppRequestRefreshRateMin,
@@ -532,6 +484,19 @@
      * for tests. Release the token by releasing the returned IBinder reference.
      */
     virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
+
+    /*
+     * Sets the frame timeline vsync id received from choreographer that corresponds to next
+     * buffer submitted on that surface.
+     */
+    virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+                                           int64_t frameTimelineVsyncId) = 0;
+
+    /*
+     * Adds a TransactionTraceListener to listen for transaction tracing state updates.
+     */
+    virtual status_t addTransactionTraceListener(
+            const sp<gui::ITransactionTraceListener>& listener) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -554,7 +519,7 @@
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
         GET_DISPLAY_STATE,
-        CAPTURE_SCREEN,
+        CAPTURE_DISPLAY,
         CAPTURE_LAYERS,
         CLEAR_ANIMATION_FRAME_STATS,
         GET_ANIMATION_FRAME_STATS,
@@ -582,7 +547,7 @@
         GET_DESIRED_DISPLAY_CONFIG_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
-        CAPTURE_SCREEN_BY_ID,
+        CAPTURE_DISPLAY_BY_ID,
         NOTIFY_POWER_BOOST,
         SET_GLOBAL_SHADOW_SETTINGS,
         GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
@@ -591,6 +556,8 @@
         SET_GAME_CONTENT_TYPE,
         SET_FRAME_RATE,
         ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
+        SET_FRAME_TIMELINE_VSYNC,
+        ADD_TRANSACTION_TRACE_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 3afbabf..9e9e191 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -36,6 +36,7 @@
     enum { // (keep in sync with SurfaceControl.java)
         eHidden = 0x00000004,
         eDestroyBackbuffer = 0x00000020,
+        eSkipScreenshot = 0x00000040,
         eSecure = 0x00000080,
         eNonPremultiplied = 0x00000100,
         eOpaque = 0x00000400,
@@ -51,13 +52,15 @@
         eFXSurfaceMask = 0x000F0000,
     };
 
+    // TODO(b/172002646):  Clean up the Surface Creation Arguments
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
+                                   sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+                                   uint32_t* outTransformHint) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -66,7 +69,7 @@
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp,
+                                             sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                              uint32_t* outTransformHint) = 0;
 
     /*
@@ -79,7 +82,8 @@
      */
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
 
-    virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
+    virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+                                   int32_t* outLayerId) = 0;
 };
 
 class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index d58e019..ac48aef 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -27,6 +27,8 @@
     METADATA_WINDOW_TYPE = 2,
     METADATA_TASK_ID = 3,
     METADATA_MOUSE_CURSOR = 4,
+    METADATA_ACCESSIBILITY_ID = 5,
+    METADATA_OWNER_PID = 6,
 };
 
 struct LayerMetadata : public Parcelable {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e60f677..a73d9a6 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -16,6 +16,23 @@
 
 #ifndef ANDROID_SF_LAYER_STATE_H
 #define ANDROID_SF_LAYER_STATE_H
+#define SAFE_PARCEL(FUNC, ...)                                                            \
+    {                                                                                     \
+        status_t error = FUNC(__VA_ARGS__);                                               \
+        if (error) {                                                                      \
+            ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
+            return error;                                                                 \
+        }                                                                                 \
+    }
+
+#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE)                             \
+    {                                                                        \
+        SAFE_PARCEL(FUNC, COUNT);                                            \
+        if (static_cast<unsigned int>(*COUNT) > SIZE) {                      \
+            ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \
+            return BAD_VALUE;                                                \
+        }                                                                    \
+    }
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -26,11 +43,15 @@
 #include <math/mat4.h>
 
 #ifndef NO_INPUT
+#include <android/FocusRequest.h>
 #include <input/InputWindow.h>
 #endif
 
+#include <gui/ISurfaceComposer.h>
 #include <gui/LayerMetadata.h>
+#include <gui/SurfaceControl.h>
 #include <math/vec3.h>
+#include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -57,9 +78,10 @@
  */
 struct layer_state_t {
     enum {
-        eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
-        eLayerOpaque = 0x02, // SURFACE_OPAQUE
-        eLayerSecure = 0x80, // SECURE
+        eLayerHidden = 0x01,         // SURFACE_HIDDEN in SurfaceControl.java
+        eLayerOpaque = 0x02,         // SURFACE_OPAQUE
+        eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT
+        eLayerSecure = 0x80,         // SECURE
     };
 
     enum {
@@ -73,7 +95,7 @@
         eLayerStackChanged = 0x00000080,
         eCropChanged_legacy = 0x00000100,
         eDeferTransaction_legacy = 0x00000200,
-        eOverrideScalingModeChanged = 0x00000400,
+        /* was ScalingModeChanged, now available 0x00000400, */
         eShadowRadiusChanged = 0x00000800,
         eReparentChildren = 0x00001000,
         eDetachChildren = 0x00002000,
@@ -105,45 +127,12 @@
         eBackgroundBlurRadiusChanged = 0x80'00000000,
         eProducerDisconnect = 0x100'00000000,
         eFixedTransformHintChanged = 0x200'00000000,
+        eFrameNumberChanged = 0x400'00000000,
+        eFrameTimelineVsyncChanged = 0x800'00000000,
+        eBlurRegionsChanged = 0x1000'00000000,
     };
 
-    layer_state_t()
-          : what(0),
-            x(0),
-            y(0),
-            z(0),
-            w(0),
-            h(0),
-            layerStack(0),
-            alpha(0),
-            flags(0),
-            mask(0),
-            reserved(0),
-            crop_legacy(Rect::INVALID_RECT),
-            cornerRadius(0.0f),
-            backgroundBlurRadius(0),
-            frameNumber_legacy(0),
-            overrideScalingMode(-1),
-            transform(0),
-            transformToDisplayInverse(false),
-            crop(Rect::INVALID_RECT),
-            frame(Rect::INVALID_RECT),
-            dataspace(ui::Dataspace::UNKNOWN),
-            surfaceDamageRegion(),
-            api(-1),
-            colorTransform(mat4()),
-            bgColorAlpha(0),
-            bgColorDataspace(ui::Dataspace::UNKNOWN),
-            colorSpaceAgnostic(false),
-            shadowRadius(0.0f),
-            frameRateSelectionPriority(-1),
-            frameRate(0.0f),
-            frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
-            fixedTransformHint(ui::Transform::ROT_INVALID) {
-        matrix.dsdx = matrix.dtdy = 1.0f;
-        matrix.dsdy = matrix.dtdx = 0.0f;
-        hdrMetadata.validTypes = 0;
-    }
+    layer_state_t();
 
     void merge(const layer_state_t& other);
     status_t write(Parcel& output) const;
@@ -154,8 +143,11 @@
         float dtdx{0};
         float dtdy{0};
         float dsdy{0};
+        status_t write(Parcel& output) const;
+        status_t read(const Parcel& input);
     };
     sp<IBinder> surface;
+    int32_t layerId;
     uint64_t what;
     float x;
     float y;
@@ -171,16 +163,13 @@
     Rect crop_legacy;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
-    sp<IBinder> barrierHandle_legacy;
-    sp<IBinder> reparentHandle;
-    uint64_t frameNumber_legacy;
-    int32_t overrideScalingMode;
+    sp<SurfaceControl> barrierSurfaceControl_legacy;
+    sp<SurfaceControl> reparentSurfaceControl;
+    uint64_t barrierFrameNumber;
 
-    sp<IGraphicBufferProducer> barrierGbp_legacy;
+    sp<SurfaceControl> relativeLayerSurfaceControl;
 
-    sp<IBinder> relativeLayerHandle;
-
-    sp<IBinder> parentHandleForChild;
+    sp<SurfaceControl> parentSurfaceControlForChild;
 
     half3 color;
 
@@ -190,7 +179,7 @@
     uint32_t transform;
     bool transformToDisplayInverse;
     Rect crop;
-    Rect frame;
+    Rect orientedDisplaySpaceRect;
     sp<GraphicBuffer> buffer;
     sp<Fence> acquireFence;
     ui::Dataspace dataspace;
@@ -199,9 +188,10 @@
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
+    std::vector<BlurRegion> blurRegions;
 
 #ifndef NO_INPUT
-    InputWindowInfo inputInfo;
+    sp<InputWindowHandle> inputHandle = new InputWindowHandle();
 #endif
 
     client_cache_t cachedBuffer;
@@ -237,6 +227,12 @@
     // a buffer of a different size. -1 means the transform hint is not set,
     // otherwise the value will be a valid ui::Rotation.
     ui::Transform::RotationFlags fixedTransformHint;
+
+    // Used by BlastBufferQueue to forward the framenumber generated by the
+    // graphics producer.
+    uint64_t frameNumber;
+
+    int64_t frameTimelineVsyncId;
 };
 
 struct ComposerState {
@@ -263,18 +259,18 @@
 
     // These states define how layers are projected onto the physical display.
     //
-    // Layers are first clipped to `viewport'.  They are then translated and
-    // scaled from `viewport' to `frame'.  Finally, they are rotated according
-    // to `orientation', `width', and `height'.
+    // Layers are first clipped to `layerStackSpaceRect'.  They are then translated and
+    // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'.  Finally, they are rotated
+    // according to `orientation', `width', and `height'.
     //
-    // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
-    // 10, 420, 210), and the size of the display is WxH.  When orientation is
-    // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
-    // When orientation is 1, layers will be additionally rotated by 90
-    // degrees around the origin clockwise and translated by (W, 0).
+    // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is
+    // Rect(20, 10, 420, 210), and the size of the display is WxH.  When orientation is 0, layers
+    // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers
+    // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
+    // 0).
     ui::Rotation orientation = ui::ROTATION_0;
-    Rect viewport;
-    Rect frame;
+    Rect layerStackSpaceRect;
+    Rect orientedDisplaySpaceRect;
 
     uint32_t width, height;
 
@@ -283,12 +279,17 @@
 };
 
 struct InputWindowCommands {
+#ifndef NO_INPUT
+    std::vector<FocusRequest> focusRequests;
+#endif
     bool syncInputWindows{false};
 
-    void merge(const InputWindowCommands& other);
+    // Merges the passed in commands and returns true if there were any changes.
+    bool merge(const InputWindowCommands& other);
+    bool empty() const;
     void clear();
-    void write(Parcel& output) const;
-    void read(const Parcel& input);
+    status_t write(Parcel& output) const;
+    status_t read(const Parcel& input);
 };
 
 static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
@@ -307,6 +308,63 @@
 // functionName can be null.
 bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
 
+struct CaptureArgs {
+    const static int32_t UNSET_UID = -1;
+    virtual ~CaptureArgs() = default;
+
+    ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
+    Rect sourceCrop;
+    float frameScale{1};
+    bool captureSecureLayers{false};
+    int32_t uid{UNSET_UID};
+    // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+    // result will be in the display's colorspace.
+    // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+    // different from SRGB (byte per color), and failed when checking colors in tests.
+    // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+    ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+
+    // The receiver of the capture can handle protected buffer. A protected buffer has
+    // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+    // Any read/write access from unprotected context will result in undefined behaviour.
+    // Protected contents are typically DRM contents. This has no direct implication to the
+    // secure property of the surface, which is specified by the application explicitly to avoid
+    // the contents being accessed/captured by screenshot or unsecure display.
+    bool allowProtected = false;
+
+    virtual status_t write(Parcel& output) const;
+    virtual status_t read(const Parcel& input);
+};
+
+struct DisplayCaptureArgs : CaptureArgs {
+    sp<IBinder> displayToken;
+    uint32_t width{0};
+    uint32_t height{0};
+    bool useIdentityTransform{false};
+
+    status_t write(Parcel& output) const override;
+    status_t read(const Parcel& input) override;
+};
+
+struct LayerCaptureArgs : CaptureArgs {
+    sp<IBinder> layerHandle;
+    std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles;
+    bool childrenOnly{false};
+
+    status_t write(Parcel& output) const override;
+    status_t read(const Parcel& input) override;
+};
+
+struct ScreenCaptureResults {
+    sp<GraphicBuffer> buffer;
+    bool capturedSecureLayers{false};
+    ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+    status_t result = OK;
+
+    status_t write(Parcel& output) const;
+    status_t read(const Parcel& input);
+};
+
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 49c83da..4aa076e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -30,6 +30,7 @@
 #include <utils/RefBase.h>
 
 #include <shared_mutex>
+#include <unordered_set>
 
 namespace android {
 
@@ -67,7 +68,6 @@
     : public ANativeObjectBase<ANativeWindow, Surface, RefBase>
 {
 public:
-
     /*
      * creates a Surface from the given IGraphicBufferProducer (which concrete
      * implementation is a BufferQueue).
@@ -82,9 +82,15 @@
      *
      * the controlledByApp flag indicates that this Surface (producer) is
      * controlled by the application. This flag is used at connect time.
+     *
+     * Pass in the SurfaceControlHandle to store a weak reference to the layer
+     * that the Surface was created from. This handle can be used to create a
+     * child surface without using the IGBP to identify the layer. This is used
+     * for surfaces created by the BlastBufferQueue whose IGBP is created on the
+     * client and cannot be verified in SF.
      */
-    explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
-            bool controlledByApp = false);
+    explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false,
+                     const sp<IBinder>& surfaceControlHandle = nullptr);
 
     /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
      * Surface was created with. Usually it's an error to use the
@@ -92,6 +98,8 @@
      */
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
 
+    sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+
     /* convenience function to check that the given surface is non NULL as
      * well as its IGraphicBufferProducer */
     static bool isValid(const sp<Surface>& surface) {
@@ -119,7 +127,7 @@
      * delay during dequeueBuffer. If there are already the maximum number of
      * buffers allocated, this function has no effect.
      */
-    void allocateBuffers();
+    virtual void allocateBuffers();
 
     /* Sets the generation number on the IGraphicBufferProducer and updates the
      * generation number on any buffers attached to the Surface after this call.
@@ -178,7 +186,8 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    status_t setFrameRate(float frameRate, int8_t compatibility);
+    virtual status_t setFrameRate(float frameRate, int8_t compatibility);
+    virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
 
 protected:
     virtual ~Surface();
@@ -264,6 +273,7 @@
     int dispatchAddQueueInterceptor(va_list args);
     int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
+    int dispatchSetFrameTimelineVsync(va_list args);
     bool transformToDisplayInverse();
 
 protected:
@@ -538,13 +548,25 @@
     bool mEnableFrameTimestamps = false;
     std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
 
+    // Reference to the SurfaceFlinger layer that was used to create this
+    // surface. This is only populated when the Surface is created from
+    // a BlastBufferQueue.
+    sp<IBinder> mSurfaceControlHandle;
+
     bool mReportRemovedBuffers = false;
     std::vector<sp<GraphicBuffer>> mRemovedBuffers;
     int mMaxBufferCount;
 
     sp<IProducerListener> mListenerProxy;
+
+    // Get and flush the buffers of given slots, if the buffer in the slot
+    // is currently dequeued then it won't be flushed and won't be returned
+    // in outBuffers.
     status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
             std::vector<sp<GraphicBuffer>>* outBuffers);
+
+    // Buffers that are successfully dequeued/attached and handed to clients
+    std::unordered_set<int> mDequeuedSlots;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 32c0bab..73909a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -29,6 +29,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/BlurRegion.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
@@ -120,13 +121,15 @@
 
     // Sets the refresh rate boundaries for the display.
     static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t defaultConfig, float primaryRefreshRateMin,
+                                                 int32_t defaultConfig, bool allowGroupSwitching,
+                                                 float primaryRefreshRateMin,
                                                  float primaryRefreshRateMax,
                                                  float appRequestRefreshRateMin,
                                                  float appRequestRefreshRateMax);
     // Gets the refresh rate boundaries for the display.
     static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                  int32_t* outDefaultConfig,
+                                                 bool* outAllowGroupSwitching,
                                                  float* outPrimaryRefreshRateMin,
                                                  float* outPrimaryRefreshRateMax,
                                                  float* outAppRequestRefreshRateMin,
@@ -253,13 +256,13 @@
     static sp<SurfaceComposerClient> getDefault();
 
     //! Create a surface
-    sp<SurfaceControl> createSurface(const String8& name,              // name of the surface
-                                     uint32_t w,                       // width in pixel
-                                     uint32_t h,                       // height in pixel
-                                     PixelFormat format,               // pixel-format desired
-                                     uint32_t flags = 0,               // usage flags
-                                     SurfaceControl* parent = nullptr, // parent
-                                     LayerMetadata metadata = LayerMetadata(), // metadata
+    sp<SurfaceControl> createSurface(const String8& name, // name of the surface
+                                     uint32_t w,          // width in pixel
+                                     uint32_t h,          // height in pixel
+                                     PixelFormat format,  // pixel-format desired
+                                     uint32_t flags = 0,  // usage flags
+                                     const sp<IBinder>& parentHandle = nullptr, // parentHandle
+                                     LayerMetadata metadata = LayerMetadata(),  // metadata
                                      uint32_t* outTransformHint = nullptr);
 
     status_t createSurfaceChecked(const String8& name, // name of the surface
@@ -267,9 +270,9 @@
                                   uint32_t h,          // height in pixel
                                   PixelFormat format,  // pixel-format desired
                                   sp<SurfaceControl>* outSurface,
-                                  uint32_t flags = 0,                       // usage flags
-                                  SurfaceControl* parent = nullptr,         // parent
-                                  LayerMetadata metadata = LayerMetadata(), // metadata
+                                  uint32_t flags = 0,                        // usage flags
+                                  const sp<IBinder>& parentHandle = nullptr, // parentHandle
+                                  LayerMetadata metadata = LayerMetadata(),  // metadata
                                   uint32_t* outTransformHint = nullptr);
 
     //! Create a surface
@@ -339,16 +342,24 @@
     };
 
     class Transaction : public Parcelable {
+    private:
+        static std::atomic<uint32_t> idCounter;
+        int64_t generateId();
+
     protected:
         std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
-        SortedVector<DisplayState > mDisplayStates;
+        SortedVector<DisplayState> mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
 
-        uint32_t                    mForceSynchronous = 0;
-        uint32_t                    mTransactionNestCount = 0;
-        bool                        mAnimation = false;
-        bool                        mEarlyWakeup = false;
+        uint64_t mId;
+
+        uint32_t mForceSynchronous = 0;
+        uint32_t mTransactionNestCount = 0;
+        bool mAnimation = false;
+        bool mEarlyWakeup = false;
+        bool mExplicitEarlyWakeupStart = false;
+        bool mExplicitEarlyWakeupEnd = false;
 
         // Indicates that the Transaction contains a buffer that should be cached
         bool mContainsBuffer = false;
@@ -365,20 +376,20 @@
         // The desired present time does not affect this ordering.
         int64_t mDesiredPresentTime = -1;
 
+        // The vsync Id provided by Choreographer.getVsyncId
+        int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
-        layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
-        layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
-            return getLayerState(sc->getHandle());
-        }
+        layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
 
     public:
-        Transaction() = default;
+        Transaction();
         virtual ~Transaction() = default;
         Transaction(Transaction const& other);
 
@@ -416,7 +427,7 @@
         // If the relative is removed, the Surface will have no layer and be
         // invisible, until the next time set(Relative)Layer is called.
         Transaction& setRelativeLayer(const sp<SurfaceControl>& sc,
-                const sp<IBinder>& relativeTo, int32_t z);
+                                      const sp<SurfaceControl>& relativeTo, int32_t z);
         Transaction& setFlags(const sp<SurfaceControl>& sc,
                 uint32_t flags, uint32_t mask);
         Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc,
@@ -429,6 +440,8 @@
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
+        Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
+                                    const std::vector<BlurRegion>& regions);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
         Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
         // Defers applying any changes made in this transaction until the Layer
@@ -436,22 +449,16 @@
         // by handle is removed, then we will apply this transaction regardless of
         // what frame number has been reached.
         Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
-                                                  const sp<IBinder>& handle, uint64_t frameNumber);
-        // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by
-        // Surface instead of Handle. Useful for clients which may not have the
-        // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
-        Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
-                                                  const sp<Surface>& barrierSurface,
+                                                  const sp<SurfaceControl>& barrierSurfaceControl,
                                                   uint64_t frameNumber);
         // Reparents all children of this layer to the new parent handle.
         Transaction& reparentChildren(const sp<SurfaceControl>& sc,
-                const sp<IBinder>& newParentHandle);
+                                      const sp<SurfaceControl>& newParent);
 
         /// Reparents the current layer to the new parent handle. The new parent must not be null.
         // This can be used instead of reparentChildren if the caller wants to
         // only re-parent a specific child.
-        Transaction& reparent(const sp<SurfaceControl>& sc,
-                const sp<IBinder>& newParentHandle);
+        Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent);
 
         Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
 
@@ -485,6 +492,8 @@
 
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+        // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
+        Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
 
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.
@@ -497,14 +506,12 @@
         // Sometimes the WindowManager needs to extend their lifetime slightly
         // in order to perform an exit animation or prevent flicker.
         Transaction& detachChildren(const sp<SurfaceControl>& sc);
-        // Set an override scaling mode as documented in <system/window.h>
-        // the override scaling mode will take precedence over any client
-        // specified scaling mode. -1 will clear the override scaling mode.
-        Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc,
-                int32_t overrideScalingMode);
 
 #ifndef NO_INPUT
         Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
+        Transaction& setFocusedWindow(const sp<IBinder>& token, const sp<IBinder>& focusedToken,
+                                      nsecs_t timestampNanos, int32_t displayId);
+        Transaction& setFocusedWindow(const FocusRequest& request);
         Transaction& syncInputWindows();
 #endif
 
@@ -527,6 +534,13 @@
         // a buffer of a different size.
         Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
 
+        // Sets the frame timeline vsync id received from choreographer that corresponds
+        // to the transaction.
+        Transaction& setFrameTimelineVsync(int64_t frameTimelineVsyncId);
+        // Variant that only applies to a specific SurfaceControl.
+        Transaction& setFrameTimelineVsync(const sp<SurfaceControl>& sc,
+                int64_t frameTimelineVsyncId);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
@@ -547,6 +561,8 @@
         void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
         void setAnimationTransaction();
         void setEarlyWakeup();
+        void setExplicitEarlyWakeupStart();
+        void setExplicitEarlyWakeupEnd();
     };
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
@@ -588,28 +604,12 @@
 
 class ScreenshotClient {
 public:
-    // if cropping isn't required, callers may pass in a default Rect, e.g.:
-    //   capture(display, producer, Rect(), reqWidth, ...);
-    static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
-                            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                            uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                            ui::Rotation rotation, bool captureSecureLayers,
-                            sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers);
-    static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
-                            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                            uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                            ui::Rotation rotation, sp<GraphicBuffer>* outBuffer);
-    static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
-                            sp<GraphicBuffer>* outBuffer);
-    static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
-                                  ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                  float frameScale, sp<GraphicBuffer>* outBuffer);
-    static status_t captureChildLayers(
-            const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
-            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-            const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
-                    excludeHandles,
-            float frameScale, sp<GraphicBuffer>* outBuffer);
+    static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
+                                   const sp<IScreenCaptureListener>& captureListener);
+    static status_t captureDisplay(uint64_t displayOrLayerStack,
+                                   const sp<IScreenCaptureListener>& captureListener);
+    static status_t captureLayers(const LayerCaptureArgs& captureArgs,
+                                  const sp<IScreenCaptureListener>& captureListener);
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index ac2bbcc..35bdfc1 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -20,7 +20,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 
@@ -44,8 +43,12 @@
 class SurfaceControl : public RefBase
 {
 public:
-    static sp<SurfaceControl> readFromParcel(const Parcel* parcel);
-    void writeToParcel(Parcel* parcel);
+    static status_t readFromParcel(const Parcel& parcel, sp<SurfaceControl>* outSurfaceControl);
+    status_t writeToParcel(Parcel& parcel);
+
+    static status_t readNullableFromParcel(const Parcel& parcel,
+                                           sp<SurfaceControl>* outSurfaceControl);
+    static status_t writeNullableToParcel(Parcel& parcel, const sp<SurfaceControl>& surfaceControl);
 
     static bool isValid(const sp<SurfaceControl>& surface) {
         return (surface != nullptr) && surface->isValid();
@@ -70,6 +73,7 @@
     sp<Surface> getSurface() const;
     sp<Surface> createSurface() const;
     sp<IBinder> getHandle() const;
+    int32_t getLayerId() const;
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
 
@@ -85,7 +89,8 @@
     explicit SurfaceControl(const sp<SurfaceControl>& other);
 
     SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                   const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0);
+                   const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
+                   uint32_t transformHint = 0);
 
 private:
     // can't be copied
@@ -105,6 +110,7 @@
     sp<IGraphicBufferProducer>  mGraphicBufferProducer;
     mutable Mutex               mLock;
     mutable sp<Surface>         mSurfaceData;
+    int32_t mLayerId;
     uint32_t mTransformHint;
 };
 
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
new file mode 100644
index 0000000..2857996
--- /dev/null
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/SurfaceComposerClient.h>
+#include <future>
+
+namespace android {
+
+class SyncScreenCaptureListener : public BnScreenCaptureListener {
+public:
+    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+        resultsPromise.set_value(captureResults);
+        return NO_ERROR;
+    }
+
+    ScreenCaptureResults waitForResults() {
+        std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
+        return resultsFuture.get();
+    }
+
+private:
+    std::promise<ScreenCaptureResults> resultsPromise;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
new file mode 100644
index 0000000..9efba47
--- /dev/null
+++ b/libs/gui/include/gui/TransactionTracing.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/BnTransactionTraceListener.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class TransactionTraceListener : public gui::BnTransactionTraceListener {
+    static std::mutex sMutex;
+    static sp<TransactionTraceListener> sInstance;
+
+    TransactionTraceListener();
+
+public:
+    static sp<TransactionTraceListener> getInstance();
+
+    binder::Status onToggled(bool enabled) override;
+
+    bool isTracingEnabled();
+
+private:
+    bool mTracingEnabled = false;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 99ab085..004d875 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -51,12 +51,14 @@
 typedef ::android::IProducerListener BProducerListener;
 
 #ifndef LOG
-struct LOG_dummy {
+struct LOG_stub {
     template <typename T>
-    LOG_dummy& operator<< (const T&) { return *this; }
+    LOG_stub& operator<<(const T&) {
+        return *this;
+    }
 };
 
-#define LOG(x)  LOG_dummy()
+#define LOG(x) LOG_stub()
 #endif
 
 // Instantiate only if HGraphicBufferProducer is base of BASE.
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index cc64fd4..f7dcbc6 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -21,6 +21,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/String16.h>
 
+#include <binder/IBinder.h>
 #include <binder/Parcelable.h>
 
 namespace android {
@@ -43,6 +44,7 @@
 
     String16 name;
     sp<IGraphicBufferProducer> graphicBufferProducer;
+    sp<IBinder> surfaceControlHandle;
 
     virtual status_t writeToParcel(Parcel* parcel) const override;
     virtual status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h
index 13c0162..8048518 100644
--- a/libs/gui/include/private/gui/BitTube.h
+++ b/libs/gui/include/private/gui/BitTube.h
@@ -58,6 +58,9 @@
     // resets this BitTube's receive file descriptor to receiveFd
     void setReceiveFd(base::unique_fd&& receiveFd);
 
+    // resets this BitTube's send file descriptor to sendFd
+    void setSendFd(base::unique_fd&& sendFd);
+
     // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
     template <typename T>
     static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
@@ -85,7 +88,7 @@
     // the message, excess data is silently discarded.
     ssize_t read(void* vaddr, size_t size);
 
-    base::unique_fd mSendFd;
+    mutable base::unique_fd mSendFd;
     mutable base::unique_fd mReceiveFd;
 
     static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index a6bcd10..53c13c8 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -14,7 +14,7 @@
 
     srcs: [
         "BLASTBufferQueue_test.cpp",
-	"BufferItemConsumer_test.cpp",
+        "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
         "EndToEndNativeInputTest.cpp",
@@ -58,6 +58,30 @@
     header_libs: ["libsurfaceflinger_headers"],
 }
 
+// Build the tests that need to run with both 32bit and 64bit.
+cc_test {
+    name: "libgui_multilib_test",
+    test_suites: ["device-tests"],
+
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: [
+        "DisplayEventStructLayout_test.cpp",
+    ],
+
+    shared_libs: [
+        "libgui",
+    ],
+
+    compile_multilib: "both",
+
+    header_libs: ["libsurfaceflinger_headers"],
+}
+
 // Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
 // This test has a main method, and requires a separate binary to be built.
 // To add move tests like this, just add additional cc_test statements,
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index d929a59..f3559fa 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -25,6 +25,7 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerService.h>
 #include <ui/DisplayConfig.h>
 #include <ui/GraphicBuffer.h>
@@ -43,7 +44,7 @@
 class BLASTBufferQueueHelper {
 public:
     BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
-        mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+        mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height);
     }
 
     void update(const sp<SurfaceControl>& sc, int width, int height) {
@@ -120,6 +121,9 @@
                 .show(mSurfaceControl)
                 .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
                 .apply();
+
+        mCaptureArgs.displayToken = mDisplayToken;
+        mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
     }
 
     void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
@@ -128,7 +132,7 @@
         ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
         ASSERT_EQ(NO_ERROR,
-                  igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+                  igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
                                        &qbOutput));
         ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
         producer = igbProducer;
@@ -165,18 +169,20 @@
 
     void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
                             bool outsideRegion = false) {
+        sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer;
         const auto epsilon = 3;
-        const auto width = mScreenCaptureBuf->getWidth();
-        const auto height = mScreenCaptureBuf->getHeight();
-        const auto stride = mScreenCaptureBuf->getStride();
+        const auto width = captureBuf->getWidth();
+        const auto height = captureBuf->getHeight();
+        const auto stride = captureBuf->getStride();
 
         uint32_t* bufData;
-        mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
-                                reinterpret_cast<void**>(&bufData));
+        captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+                         reinterpret_cast<void**>(&bufData));
 
         for (uint32_t row = 0; row < height; row++) {
             for (uint32_t col = 0; col < width; col++) {
                 uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+                ASSERT_NE(nullptr, pixel);
                 bool inRegion;
                 if (!outsideRegion) {
                     inRegion = row >= region.top + border && row < region.bottom - border &&
@@ -196,20 +202,36 @@
                 }
             }
         }
-        mScreenCaptureBuf->unlock();
+        captureBuf->unlock();
         ASSERT_EQ(false, ::testing::Test::HasFailure());
     }
 
+    static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+                                   ScreenCaptureResults& captureResults) {
+        const auto sf = ComposerService::getComposerService();
+        SurfaceComposerClient::Transaction().apply(true);
+
+        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        status_t status = sf->captureDisplay(captureArgs, captureListener);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        captureResults = captureListener->waitForResults();
+        return captureResults.result;
+    }
+
     sp<SurfaceComposerClient> mClient;
     sp<ISurfaceComposer> mComposer;
 
     sp<IBinder> mDisplayToken;
 
     sp<SurfaceControl> mSurfaceControl;
-    sp<GraphicBuffer> mScreenCaptureBuf;
 
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
+
+    DisplayCaptureArgs mCaptureArgs;
+    ScreenCaptureResults mCaptureResults;
 };
 
 TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
@@ -301,12 +323,7 @@
     adapter.waitForCallbacks();
 
     // capture screen and verify that it is red
-    bool capturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
-                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
-                                       mDisplayWidth, mDisplayHeight,
-                                       /*useIdentityTransform*/ false));
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -383,12 +400,8 @@
 
     adapter.waitForCallbacks();
     // capture screen and verify that it is red
-    bool capturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
-                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
-                                       mDisplayWidth, mDisplayHeight,
-                                       /*useIdentityTransform*/ false));
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -444,12 +457,8 @@
 
     adapter.waitForCallbacks();
     // capture screen and verify that it is red
-    bool capturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
-                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
-                                       mDisplayWidth, mDisplayHeight,
-                                       /*useIdentityTransform*/ false));
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b,
                                {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
@@ -483,18 +492,14 @@
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
         IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
                                                        Rect(bufWidth, bufHeight),
-                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
-                                                       Fence::NO_FENCE);
+                                                       NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                                                       tr, Fence::NO_FENCE);
         igbProducer->queueBuffer(slot, input, &qbOutput);
         ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
 
         adapter.waitForCallbacks();
-        bool capturedSecureLayers;
-        ASSERT_EQ(NO_ERROR,
-                  mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
-                                           ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
-                                           Rect(), mDisplayWidth, mDisplayHeight,
-                                           /*useIdentityTransform*/ false));
+        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
         switch (tr) {
             case ui::Transform::ROT_0:
                 ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index b87cbbd..fc6551c 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -51,7 +51,7 @@
         mBFL = new BufferFreedListener(this);
         mBIC->setBufferFreedListener(mBFL);
 
-        sp<IProducerListener> producerListener = new DummyProducerListener();
+        sp<IProducerListener> producerListener = new StubProducerListener();
         IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         ASSERT_EQ(NO_ERROR,
                   mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -131,7 +131,7 @@
 // Test that detaching buffer from consumer side triggers onBufferFreed.
 TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
     int slot;
-    // Producer: generate a dummy buffer.
+    // Producer: generate a placeholder buffer.
     DequeueBuffer(&slot);
     QueueBuffer(slot);
 
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 6d7b6bb..d1208ee 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "BufferQueue_test"
 //#define LOG_NDEBUG 0
 
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
@@ -134,8 +134,8 @@
     mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
     EXPECT_TRUE(mConsumer != nullptr);
 
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
     ASSERT_EQ(OK,
             mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
@@ -171,23 +171,22 @@
 
 TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
     int bufferCount = 50;
     mConsumer->setMaxBufferCount(bufferCount);
 
     IGraphicBufferProducer::QueueBufferOutput output;
-    mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
+    mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
     ASSERT_EQ(output.maxBufferCount, bufferCount);
 }
 
 TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
     IGraphicBufferProducer::QueueBufferOutput qbo;
-    mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
-            &qbo);
+    mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
     mProducer->setMaxDequeuedBufferCount(3);
 
     int slot;
@@ -219,15 +218,14 @@
 
 TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
 
     EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
     EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(10));
 
     IGraphicBufferProducer::QueueBufferOutput qbo;
-    mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
-            &qbo);
+    mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
     mProducer->setMaxDequeuedBufferCount(3);
 
     int minBufferCount;
@@ -263,12 +261,11 @@
 
 TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
 
     IGraphicBufferProducer::QueueBufferOutput qbo;
-    mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
-            &qbo);
+    mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
     mProducer->setMaxDequeuedBufferCount(2);
 
     int minBufferCount;
@@ -310,8 +307,8 @@
 
 TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
 
     // Test shared buffer mode
     EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
@@ -319,8 +316,8 @@
 
 TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    mConsumer->consumerConnect(dc, false);
+    sp<MockConsumer> mc(new MockConsumer);
+    mConsumer->consumerConnect(mc, false);
 
     EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(0));
     EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(
@@ -332,11 +329,11 @@
 
 TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
 
     ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
     ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
@@ -386,11 +383,11 @@
 
 TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
 
     int slot;
     sp<Fence> fence;
@@ -445,11 +442,11 @@
 
 TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
 
     int slot;
     sp<Fence> fence;
@@ -488,11 +485,11 @@
 
 TEST_F(BufferQueueTest, TestDisallowingAllocation) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     static const uint32_t WIDTH = 320;
     static const uint32_t HEIGHT = 240;
@@ -526,11 +523,11 @@
 
 TEST_F(BufferQueueTest, TestGenerationNumbers) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     ASSERT_EQ(OK, mProducer->setGenerationNumber(1));
 
@@ -568,11 +565,11 @@
 
 TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
 
@@ -618,11 +615,11 @@
 
 TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
     ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
@@ -687,11 +684,11 @@
 
 TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     // Dequeue a buffer
     int sharedSlot;
@@ -738,11 +735,11 @@
 
 TEST_F(BufferQueueTest, TestTimeouts) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     // Fill up the queue. Since the controlledByApp flags are set to true, this
     // queue should be in non-blocking mode, and we should be recycling the same
@@ -800,11 +797,11 @@
 
 TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> sourceFence;
@@ -822,11 +819,11 @@
 
 TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
 
     // Dequeue and queue the first buffer, storing the handle
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -876,11 +873,11 @@
 
 TEST_F(BufferQueueTest, TestOccupancyHistory) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
 
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> fence = Fence::NO_FENCE;
@@ -1030,8 +1027,8 @@
 
 TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
     sp<BufferDiscardedListener> pl(new BufferDiscardedListener);
     ASSERT_EQ(OK, mProducer->connect(pl,
@@ -1115,11 +1112,11 @@
 
 TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK,
+              mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
     ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
 
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -1156,12 +1153,11 @@
 
 TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    sp<IProducerListener> dummyListener(new DummyProducerListener);
-    ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
-            true, &output));
+    sp<IProducerListener> fakeListener(new StubProducerListener);
+    ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
 
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> fence = Fence::NO_FENCE;
@@ -1215,15 +1211,13 @@
 
 TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
     createBufferQueue();
-    sp<DummyConsumer> dc(new DummyConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    sp<MockConsumer> mc(new MockConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
     IGraphicBufferProducer::QueueBufferOutput output;
-    sp<IProducerListener> dummyListener(new DummyProducerListener);
+    sp<IProducerListener> fakeListener(new StubProducerListener);
     ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
-    ASSERT_EQ(OK, mProducer->connect(
-            dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
-    ASSERT_EQ(BAD_VALUE, mProducer->connect(
-            dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+    ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(BAD_VALUE, mProducer->connect(fakeListener, NATIVE_WINDOW_API_MEDIA, true, &output));
 
     ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
     ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
new file mode 100644
index 0000000..7210910
--- /dev/null
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
+
+namespace android::test {
+
+#define CHECK_OFFSET(type, member, expected_offset) \
+    static_assert((offsetof(type, member) == (expected_offset)), "")
+
+TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
+    CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event, config, 24);
+
+    CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0);
+    CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8);
+    CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16);
+
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 24);
+
+    CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
+
+    CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
+    CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5188a09..c39b0b5 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -36,18 +36,18 @@
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
 
-#include <input/InputWindow.h>
-#include <input/IInputFlinger.h>
-#include <input/InputTransport.h>
+#include <android/os/IInputFlinger.h>
 #include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
 
 #include <ui/DisplayConfig.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+using android::os::IInputFlinger;
 
-namespace android {
-namespace test {
+namespace android::test {
 
 using Transaction = SurfaceComposerClient::Transaction;
 
@@ -62,16 +62,16 @@
 
 // We use the top 10 layers as a way to haphazardly place ourselves above anything else.
 static const int LAYER_BASE = INT32_MAX - 10;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
 
 class InputSurface {
 public:
     InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
         mSurfaceControl = sc;
 
-        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-
         mInputFlinger = getInputFlinger();
-        mInputFlinger->registerInputChannel(mServerChannel);
+        mClientChannel = std::make_shared<InputChannel>();
+        mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
 
         populateInputInfo(width, height);
 
@@ -104,6 +104,15 @@
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
+    static std::unique_ptr<InputSurface> makeCursorInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
+                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eCursorWindow);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
     InputEvent* consumeEvent() {
         waitForEventAvailable();
 
@@ -134,18 +143,36 @@
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
         EXPECT_EQ(x, mev->getX(0));
         EXPECT_EQ(y, mev->getY(0));
+        EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
         ASSERT_NE(ev, nullptr);
         ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
         mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+        EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
     }
 
-    ~InputSurface() {
-        mInputFlinger->unregisterInputChannel(mServerChannel);
+    void expectTapWithFlag(int x, int y, int32_t flags) {
+        InputEvent *ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+        MotionEvent *mev = static_cast<MotionEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+        EXPECT_EQ(x, mev->getX(0));
+        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_EQ(flags, mev->getFlags() & flags);
+
+        ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+        mev = static_cast<MotionEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+        EXPECT_EQ(flags, mev->getFlags() & flags);
     }
 
+    ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
+
     void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
                     const sp<SurfaceControl>&)> transactionBody) {
         SurfaceComposerClient::Transaction t;
@@ -164,6 +191,13 @@
         t.apply(true);
     }
 
+    void requestFocus() {
+        SurfaceComposerClient::Transaction t;
+        t.setFocusedWindow(mInputInfo.token, nullptr, systemTime(SYSTEM_TIME_MONOTONIC),
+                           0 /* displayId */);
+        t.apply(true);
+    }
+
 private:
     void waitForEventAvailable() {
         struct pollfd fd;
@@ -174,14 +208,13 @@
     }
 
     void populateInputInfo(int width, int height) {
-        mInputInfo.token = mServerChannel->getConnectionToken();
+        mInputInfo.token = mClientChannel->getConnectionToken();
         mInputInfo.name = "Test info";
-        mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
-        mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
-        mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+        mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+        mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
+        mInputInfo.dispatchingTimeout = 5s;
         mInputInfo.globalScaleFactor = 1.0;
-        mInputInfo.canReceiveKeys = true;
-        mInputInfo.hasFocus = true;
+        mInputInfo.focusable = true;
         mInputInfo.hasWallpaper = false;
         mInputInfo.paused = false;
 
@@ -190,19 +223,19 @@
         // TODO: Fill in from SF?
         mInputInfo.ownerPid = 11111;
         mInputInfo.ownerUid = 11111;
-        mInputInfo.inputFeatures = 0;
         mInputInfo.displayId = 0;
 
         InputApplicationInfo aInfo;
         aInfo.token = new BBinder();
         aInfo.name = "Test app info";
-        aInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+        aInfo.dispatchingTimeoutMillis =
+                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
 
         mInputInfo.applicationInfo = aInfo;
     }
 public:
     sp<SurfaceControl> mSurfaceControl;
-    sp<InputChannel> mServerChannel, mClientChannel;
+    std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
     InputWindowInfo mInputInfo;
@@ -269,7 +302,6 @@
 TEST_F(InputSurfacesTest, can_receive_input) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(100, 100);
-    surface->assertFocusChange(true);
 
     injectTap(101, 101);
 
@@ -285,12 +317,9 @@
 TEST_F(InputSurfacesTest, input_respects_positioning) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(100, 100);
-    surface->assertFocusChange(true);
 
     std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
     surface2->showAt(200, 200);
-    surface->assertFocusChange(false);
-    surface2->assertFocusChange(true);
 
     injectTap(201, 201);
     surface2->expectTap(1, 1);
@@ -317,16 +346,11 @@
     std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
 
     surface->showAt(10, 10);
-    surface->assertFocusChange(true);
     surface2->showAt(10, 10);
-    surface->assertFocusChange(false);
-    surface2->assertFocusChange(true);
 
     surface->doTransaction([](auto &t, auto &sc) {
          t.setLayer(sc, LAYER_BASE + 1);
     });
-    surface2->assertFocusChange(false);
-    surface->assertFocusChange(true);
 
     injectTap(11, 11);
     surface->expectTap(1, 1);
@@ -334,8 +358,6 @@
     surface2->doTransaction([](auto &t, auto &sc) {
          t.setLayer(sc, LAYER_BASE + 1);
     });
-    surface2->assertFocusChange(true);
-    surface->assertFocusChange(false);
 
     injectTap(11, 11);
     surface2->expectTap(1, 1);
@@ -343,8 +365,6 @@
     surface2->doTransaction([](auto &t, auto &sc) {
          t.hide(sc);
     });
-    surface2->assertFocusChange(false);
-    surface->assertFocusChange(true);
 
     injectTap(11, 11);
     surface->expectTap(1, 1);
@@ -357,12 +377,9 @@
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
-    bgSurface->assertFocusChange(true);
 
     fgSurface->mInputInfo.surfaceInset = 5;
     fgSurface->showAt(100, 100);
-    fgSurface->assertFocusChange(true);
-    bgSurface->assertFocusChange(false);
 
     injectTap(106, 106);
     fgSurface->expectTap(1, 1);
@@ -376,16 +393,13 @@
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
     parentSurface->showAt(100, 100);
-    parentSurface->assertFocusChange(true);
 
     childSurface->mInputInfo.surfaceInset = 10;
     childSurface->showAt(100, 100);
-    childSurface->assertFocusChange(true);
-    parentSurface->assertFocusChange(false);
 
     childSurface->doTransaction([&](auto &t, auto &sc) {
         t.setPosition(sc, -5, -5);
-        t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+        t.reparent(sc, parentSurface->mSurfaceControl);
     });
 
     injectTap(106, 106);
@@ -400,12 +414,9 @@
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
-    bgSurface->assertFocusChange(true);
 
     fgSurface->mInputInfo.surfaceInset = 5;
     fgSurface->showAt(100, 100);
-    bgSurface->assertFocusChange(false);
-    fgSurface->assertFocusChange(true);
 
     fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
 
@@ -422,7 +433,6 @@
     // In case we pass the very big inset without any checking.
     fgSurface->mInputInfo.surfaceInset = INT32_MAX;
     fgSurface->showAt(100, 100);
-    fgSurface->assertFocusChange(true);
 
     fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
 
@@ -439,7 +449,6 @@
         t.setTransparentRegionHint(sc, transparentRegion);
     });
     surface->showAt(100, 100);
-    surface->assertFocusChange(true);
     injectTap(101, 101);
     surface->expectTap(1, 1);
 }
@@ -454,10 +463,7 @@
             InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
 
     bgSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(true);
     bufferSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(false);
-    bufferSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
@@ -474,10 +480,7 @@
     postBuffer(bufferSurface->mSurfaceControl);
 
     bgSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(true);
     bufferSurface->showAt(10, 10);
-    bufferSurface->assertFocusChange(true);
-    bgSurface->assertFocusChange(false);
 
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
@@ -493,10 +496,7 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
 
     bgSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(true);
     fgSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(false);
-    fgSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     fgSurface->expectTap(1, 1);
@@ -513,17 +513,12 @@
             InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
 
     bgSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(true);
     containerSurface->showAt(10, 10);
-    bgSurface->assertFocusChange(false);
-    containerSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     containerSurface->expectTap(1, 1);
 
     containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
-    containerSurface->assertFocusChange(false);
-    bgSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     bgSurface->expectTap(1, 1);
@@ -532,10 +527,161 @@
 TEST_F(InputSurfacesTest, input_respects_outscreen) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(-1, -1);
-    surface->assertFocusChange(true);
 
     injectTap(0, 0);
     surface->expectTap(1, 1);
 }
+
+TEST_F(InputSurfacesTest, input_ignores_cursor_layer) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> cursorSurface =
+            InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
+
+    surface->showAt(10, 10);
+    cursorSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    surface->expectTap(1, 1);
 }
+
+TEST_F(InputSurfacesTest, can_be_focused) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+    surface->requestFocus();
+
+    surface->assertFocusChange(true);
 }
+
+TEST_F(InputSurfacesTest, rotate_surface) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(10, 10);
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees
+    });
+    injectTap(8, 11);
+    surface->expectTap(1, 2);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees
+    });
+    injectTap(9, 8);
+    surface->expectTap(1, 2);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees
+    });
+    injectTap(12, 9);
+    surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(10, 10);
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+    });
+    injectTap(2, 12);
+    surface->expectTap(1, 2);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+    });
+    injectTap(8, 2);
+    surface->expectTap(1, 2);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+    });
+    injectTap(18, 8);
+    surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->mInputInfo.surfaceInset = 5;
+    surface->showAt(100, 100);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+    });
+    injectTap(40, 120);
+    surface->expectTap(5, 10);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+    });
+    injectTap(80, 40);
+    surface->expectTap(5, 10);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+        t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+    });
+    injectTap(160, 80);
+    surface->expectTap(5, 10);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_obscured) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    // Add non touchable window to fully cover touchable window. Window behind gets touch, but
+    // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
+    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.ownerUid = 22222;
+    nonTouchableSurface->showAt(100, 100);
+
+    injectTap(190, 199);
+    surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    // Add non touchable window to cover touchable window, but parent is cropped to not cover area
+    // that will be tapped. Window behind gets touch, but with flag
+    // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.ownerUid = 22222;
+    parentSurface->mInputInfo.ownerUid = 22222;
+    nonTouchableSurface->showAt(0, 0);
+    parentSurface->showAt(100, 100);
+
+    nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.reparent(sc, parentSurface->mSurfaceControl);
+    });
+
+    injectTap(190, 199);
+    surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    // Add non touchable window to cover touchable window, but parent is cropped to avoid covering
+    // the touchable window. Window behind gets touch with no obscured flags.
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.ownerUid = 22222;
+    parentSurface->mInputInfo.ownerUid = 22222;
+    nonTouchableSurface->showAt(0, 0);
+    parentSurface->showAt(50, 50);
+
+    nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.reparent(sc, parentSurface->mSurfaceControl);
+    });
+
+    injectTap(101, 110);
+    surface->expectTap(1, 10);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 103f775..15bd32d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "IGraphicBufferProducer_test"
 //#define LOG_NDEBUG 0
 
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
 
 #include <gtest/gtest.h>
 
@@ -89,7 +89,7 @@
         ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
                 testInfo->name());
 
-        mDC = new DummyConsumer;
+        mMC = new MockConsumer;
 
         switch (GetParam()) {
             case USE_BUFFER_QUEUE_PRODUCER: {
@@ -114,7 +114,7 @@
         }
 
         // Must connect consumer before producer connects will succeed.
-        ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false));
+        ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false));
     }
 
     virtual void TearDown() {
@@ -249,7 +249,7 @@
     }
 
 private: // hide from test body
-    sp<DummyConsumer> mDC;
+    sp<MockConsumer> mMC;
 
 protected: // accessible from test body
     sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index acd4297..58d7cc6 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -129,7 +129,7 @@
     int32_t mExpectedSlot = 0;
 };
 
-class DummyListener : public BnConsumerListener {
+class FakeListener : public BnConsumerListener {
 public:
     void onFrameAvailable(const BufferItem&) override {}
     void onBuffersReleased() override {}
@@ -140,7 +140,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    sp<IConsumerListener> listener = new DummyListener;
+    sp<IConsumerListener> listener = new FakeListener;
     consumer->consumerConnect(listener, false);
 
     sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/MockConsumer.h
similarity index 94%
rename from libs/gui/tests/DummyConsumer.h
rename to libs/gui/tests/MockConsumer.h
index 502bdf9..4a6c51c 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/MockConsumer.h
@@ -18,7 +18,7 @@
 
 namespace android {
 
-struct DummyConsumer : public BnConsumerListener {
+struct MockConsumer : public BnConsumerListener {
     void onFrameAvailable(const BufferItem& /* item */) override {}
     void onBuffersReleased() override {}
     void onSidebandStreamChanged() override {}
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index ad6e051..b65cdda 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -48,7 +48,7 @@
     }
 };
 
-struct DummyListener : public BnConsumerListener {
+struct FakeListener : public BnConsumerListener {
     virtual void onFrameAvailable(const BufferItem& /* item */) {}
     virtual void onBuffersReleased() {}
     virtual void onSidebandStreamChanged() {}
@@ -64,7 +64,7 @@
     sp<IGraphicBufferProducer> outputProducer;
     sp<IGraphicBufferConsumer> outputConsumer;
     BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
-    ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+    ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
 
     sp<StreamSplitter> splitter;
     status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -75,8 +75,9 @@
     ASSERT_EQ(OK, outputProducer->allowAllocation(false));
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &qbOutput));
+    ASSERT_EQ(OK,
+              inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                     &qbOutput));
 
     int slot;
     sp<Fence> fence;
@@ -132,8 +133,7 @@
     for (int output = 0; output < NUM_OUTPUTS; ++output) {
         BufferQueue::createBufferQueue(&outputProducers[output],
                 &outputConsumers[output]);
-        ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(
-                    new DummyListener, false));
+        ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(new FakeListener, false));
     }
 
     sp<StreamSplitter> splitter;
@@ -147,8 +147,9 @@
     }
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &qbOutput));
+    ASSERT_EQ(OK,
+              inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                     &qbOutput));
 
     int slot;
     sp<Fence> fence;
@@ -203,7 +204,7 @@
     sp<IGraphicBufferProducer> outputProducer;
     sp<IGraphicBufferConsumer> outputConsumer;
     BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
-    ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+    ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
 
     sp<StreamSplitter> splitter;
     status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -211,8 +212,9 @@
     ASSERT_EQ(OK, splitter->addOutput(outputProducer));
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
-            NATIVE_WINDOW_API_CPU, false, &qbOutput));
+    ASSERT_EQ(OK,
+              inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                     &qbOutput));
 
     int slot;
     sp<Fence> fence;
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index c85e844..c7458a3 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -54,7 +54,7 @@
         mANW = mSTC;
 
         // We need a valid GL context so we can test updateTexImage()
-        // This initializes EGL and create a dummy GL context with a
+        // This initializes EGL and create a GL context placeholder with a
         // pbuffer render target.
         mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3b80945..0cd3962 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
 
 #include <gtest/gtest.h>
 
@@ -28,8 +28,10 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
 #include <inttypes.h>
 #include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 
@@ -55,12 +57,11 @@
 
 static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
 
-class DummySurfaceListener : public SurfaceListener {
+class FakeSurfaceListener : public SurfaceListener {
 public:
-    DummySurfaceListener(bool enableReleasedCb = false) :
-            mEnableReleaseCb(enableReleasedCb),
-            mBuffersReleased(0) {}
-    virtual ~DummySurfaceListener() = default;
+    FakeSurfaceListener(bool enableReleasedCb = false)
+          : mEnableReleaseCb(enableReleasedCb), mBuffersReleased(0) {}
+    virtual ~FakeSurfaceListener() = default;
 
     virtual void onBufferReleased() {
         mBuffersReleased++;
@@ -123,15 +124,15 @@
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&producer, &consumer);
 
-        sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-        consumer->consumerConnect(dummyConsumer, false);
+        sp<MockConsumer> mockConsumer(new MockConsumer);
+        consumer->consumerConnect(mockConsumer, false);
         consumer->setConsumerName(String8("TestConsumer"));
 
         sp<Surface> surface = new Surface(producer);
         sp<ANativeWindow> window(surface);
-        sp<DummySurfaceListener> listener;
+        sp<FakeSurfaceListener> listener;
         if (hasSurfaceListener) {
-            listener = new DummySurfaceListener(enableReleasedCb);
+            listener = new FakeSurfaceListener(enableReleasedCb);
         }
         ASSERT_EQ(OK, surface->connect(
                 NATIVE_WINDOW_API_CPU,
@@ -197,6 +198,20 @@
         ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
     }
 
+    static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+                                   ScreenCaptureResults& captureResults) {
+        const auto sf = ComposerService::getComposerService();
+        SurfaceComposerClient::Transaction().apply(true);
+
+        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        status_t status = sf->captureDisplay(captureArgs, captureListener);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        captureResults = captureListener->waitForResults();
+        return captureResults.result;
+    }
+
     sp<Surface> mSurface;
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -244,11 +259,13 @@
     const sp<IBinder> display = sf->getInternalDisplayToken();
     ASSERT_FALSE(display == nullptr);
 
-    sp<GraphicBuffer> outBuffer;
-    bool ignored;
-    ASSERT_EQ(NO_ERROR,
-              sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
-                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+    DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = display;
+    captureArgs.width = 64;
+    captureArgs.height = 64;
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
 
     ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
             NATIVE_WINDOW_API_CPU));
@@ -278,9 +295,7 @@
                 &buf));
         ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
     }
-    ASSERT_EQ(NO_ERROR,
-              sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
-                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+    ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
 }
 
 TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -380,8 +395,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
@@ -396,8 +411,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
@@ -427,8 +442,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
@@ -451,8 +466,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
@@ -496,8 +511,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
@@ -522,13 +537,13 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setConsumerName(String8("TestConsumer"));
 
     sp<Surface> surface = new Surface(producer);
     sp<ANativeWindow> window(surface);
-    sp<DummyProducerListener> listener = new DummyProducerListener();
+    sp<StubProducerListener> listener = new StubProducerListener();
     ASSERT_EQ(OK, surface->connect(
             NATIVE_WINDOW_API_CPU,
             /*listener*/listener,
@@ -662,8 +677,7 @@
     NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
 };
 
-
-class FakeSurfaceComposer : public ISurfaceComposer{
+class FakeSurfaceComposer : public ISurfaceComposer {
 public:
     ~FakeSurfaceComposer() override {}
 
@@ -681,13 +695,17 @@
     void destroyDisplay(const sp<IBinder>& /*display */) override {}
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
-    void setTransactionState(const Vector<ComposerState>& /*state*/,
-                             const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
-                             const sp<IBinder>& /*applyToken*/,
-                             const InputWindowCommands& /*inputWindowCommands*/,
-                             int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
-                             bool /*hasListenerCallbacks*/,
-                             const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
+    status_t setTransactionState(int64_t /*frameTimelineVsyncId*/,
+                                 const Vector<ComposerState>& /*state*/,
+                                 const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+                                 const sp<IBinder>& /*applyToken*/,
+                                 const InputWindowCommands& /*inputWindowCommands*/,
+                                 int64_t /*desiredPresentTime*/,
+                                 const client_cache_t& /*cachedBuffer*/,
+                                 bool /*hasListenerCallbacks*/,
+                                 const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
+                                 uint64_t /*transactionId*/) override {
+        return NO_ERROR;
     }
 
     void bootFinished() override {}
@@ -742,12 +760,8 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
-    status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
-                           bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/,
-                           ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/,
-                           uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
-                           bool /*useIdentityTransform*/, ui::Rotation,
-                           bool /*captureSecureLayers*/) override {
+    status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
+                            const sp<IScreenCaptureListener>& /* captureListener */) override {
         return NO_ERROR;
     }
     status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
@@ -760,17 +774,13 @@
         return NO_ERROR;
     }
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
-    status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
-                           sp<GraphicBuffer>* /*outBuffer*/) override {
+    status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
+                            const sp<IScreenCaptureListener>& /* captureListener */) override {
         return NO_ERROR;
     }
     virtual status_t captureLayers(
-            const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
-            ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/,
-            const Rect& /*sourceCrop*/,
-            const std::unordered_set<sp<IBinder>,
-                                     ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
-            float /*frameScale*/, bool /*childrenOnly*/) override {
+            const LayerCaptureArgs& /* captureArgs */,
+            const sp<IScreenCaptureListener>& /* captureListener */) override {
         return NO_ERROR;
     }
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -785,7 +795,7 @@
         return NO_ERROR;
     }
     status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
-    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) override {
         return NO_ERROR;
     }
     status_t getCompositionPreference(
@@ -834,7 +844,7 @@
         return NO_ERROR;
     }
     status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t /*defaultConfig*/,
+                                          int32_t /*defaultConfig*/, bool /*allowGroupSwitching*/,
                                           float /*primaryRefreshRateMin*/,
                                           float /*primaryRefreshRateMax*/,
                                           float /*appRequestRefreshRateMin*/,
@@ -843,6 +853,7 @@
     }
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
                                           int32_t* /*outDefaultConfig*/,
+                                          bool* /*outAllowGroupSwitching*/,
                                           float* /*outPrimaryRefreshRateMin*/,
                                           float* /*outPrimaryRefreshRateMax*/,
                                           float* /*outAppRequestRefreshRateMin*/,
@@ -862,7 +873,19 @@
         return NO_ERROR;
     }
 
-    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; }
+    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
+        return NO_ERROR;
+    }
+
+    status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& /*surface*/,
+                                   int64_t /*frameTimelineVsyncId*/) override {
+        return NO_ERROR;
+    }
+
+    status_t addTransactionTraceListener(
+            const sp<gui::ITransactionTraceListener>& /*listener*/) override {
+        return NO_ERROR;
+    }
 
 protected:
     IBinder* onAsBinder() override { return nullptr; }
@@ -1909,8 +1932,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
 
-    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
-    consumer->consumerConnect(dummyConsumer, false);
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
     consumer->setDefaultBufferSize(10, 10);
 
     sp<Surface> surface = new Surface(producer);
@@ -1974,4 +1997,29 @@
     ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
 }
 
+TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<MockConsumer> mockConsumer(new MockConsumer);
+    consumer->consumerConnect(mockConsumer, false);
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+
+    int count = -1;
+    ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+    EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+
+    consumer->setMaxBufferCount(10);
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+    EXPECT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+    EXPECT_EQ(10, count);
+
+    ASSERT_EQ(NO_ERROR, native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU));
+    ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+    EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+}
+
 } // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index d64dfd5..1bfe462 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -45,7 +45,9 @@
         if (res != OK) return res;
     }
 
-    return IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+    res = IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+    if (res != OK) return res;
+    return parcel->writeStrongBinder(surfaceControlHandle);
 }
 
 status_t Surface::readFromParcel(const Parcel* parcel) {
@@ -68,17 +70,14 @@
     }
 
     graphicBufferProducer = IGraphicBufferProducer::createFromParcel(parcel);
+    surfaceControlHandle = parcel->readStrongBinder();
     return OK;
 }
 
 String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
-    size_t len;
-    const char16_t* str = parcel->readString16Inplace(&len);
-    if (str != nullptr) {
-        return String16(str, len);
-    } else {
-        return String16();
-    }
+    std::optional<String16> str;
+    parcel->readString16(&str);
+    return str.value_or(String16());
 }
 
 } // namespace view
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 7037680..fce3000 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,6 +14,17 @@
 
 // libinput is partially built for the host (used by build time keymap validation tool)
 
+filegroup {
+    name: "inputconstants_aidl",
+    srcs: [
+        "android/os/BlockUntrustedTouchesMode.aidl",
+        "android/os/IInputConstants.aidl",
+        "android/os/InputEventInjectionResult.aidl",
+        "android/os/InputEventInjectionSync.aidl",
+        "android/os/TouchOcclusionMode.aidl",
+    ],
+}
+
 cc_library {
     name: "libinput",
     host_supported: true,
@@ -25,10 +36,15 @@
     srcs: [
         "Input.cpp",
         "InputDevice.cpp",
+        "InputEventLabels.cpp",
         "Keyboard.cpp",
         "KeyCharacterMap.cpp",
         "KeyLayoutMap.cpp",
+        "LatencyStatistics.cpp",
+        "PropertyMap.cpp",
         "TouchVideoFrame.cpp",
+        "VelocityControl.cpp",
+        "VelocityTracker.cpp",
         "VirtualKeyMap.cpp",
     ],
 
@@ -43,19 +59,32 @@
         "libcutils",
     ],
 
+    static_libs: [
+        "libui-types",
+    ],
+
+    export_static_lib_headers: [
+        "libui-types",
+    ],
+
     target: {
         android: {
             srcs: [
-                "IInputFlinger.cpp",
-                "InputApplication.cpp",
                 "InputTransport.cpp",
                 "InputWindow.cpp",
-                "ISetInputWindowsListener.cpp",
-                "LatencyStatistics.cpp",
-                "VelocityControl.cpp",
-                "VelocityTracker.cpp",
+                "android/FocusRequest.aidl",
+                "android/InputApplicationInfo.aidl",
+                "android/os/IInputConstants.aidl",
+                "android/os/InputEventInjectionResult.aidl",
+                "android/os/InputEventInjectionSync.aidl",
+                "android/os/TouchOcclusionMode.aidl",
+                "android/os/BlockUntrustedTouchesMode.aidl",
+                "android/os/IInputFlinger.aidl",
+                "android/os/ISetInputWindowsListener.aidl",
             ],
 
+            export_shared_lib_headers: ["libbinder"],
+
             shared_libs: [
                 "libutils",
                 "libbinder",
@@ -70,8 +99,53 @@
             shared: {
                 enabled: false,
             },
+            include_dirs: [
+                "frameworks/native/libs/arect/include",
+            ],
+        },
+        linux_glibc: {
+            srcs: [
+                "InputTransport.cpp",
+                "InputWindow.cpp",
+                "android/FocusRequest.aidl",
+                "android/InputApplicationInfo.aidl",
+                "android/os/IInputConstants.aidl",
+                "android/os/IInputFlinger.aidl",
+                "android/os/ISetInputWindowsListener.aidl",
+                "android/os/TouchOcclusionMode.aidl",
+            ],
+            static_libs: [
+                "libhostgraphics",
+            ],
+            shared_libs: [
+                "libbinder",
+            ],
         },
     },
+
+    aidl: {
+        local_include_dirs: ["."],
+        export_aidl_headers: true,
+    },
+}
+
+cc_defaults {
+    name: "libinput_fuzz_defaults",
+    host_supported: true,
+    shared_libs: [
+        "libutils",
+        "libbase",
+        "liblog",
+    ],
+}
+
+cc_fuzz {
+    name: "libinput_fuzz_propertymap",
+    defaults: ["libinput_fuzz_defaults"],
+    srcs: [
+        "PropertyMap.cpp",
+        "PropertyMap_fuzz.cpp",
+    ],
 }
 
 subdirs = ["tests"]
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
deleted file mode 100644
index 8ec5165..0000000
--- a/libs/input/IInputFlinger.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <input/IInputFlinger.h>
-
-namespace android {
-
-class BpInputFlinger : public BpInterface<IInputFlinger> {
-public:
-    explicit BpInputFlinger(const sp<IBinder>& impl) :
-            BpInterface<IInputFlinger>(impl) { }
-
-    virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
-        data.writeUint32(static_cast<uint32_t>(inputInfo.size()));
-        for (const auto& info : inputInfo) {
-            info.write(data);
-        }
-        data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener));
-
-        remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
-                IBinder::FLAG_ONEWAY);
-    }
-
-    virtual void registerInputChannel(const sp<InputChannel>& channel) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-        channel->write(data);
-        remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
-    }
-
-    virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-        channel->write(data);
-        remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
-
-status_t BnInputFlinger::onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-    case SET_INPUT_WINDOWS_TRANSACTION: {
-        CHECK_INTERFACE(IInputFlinger, data, reply);
-        size_t count = data.readUint32();
-        if (count > data.dataSize()) {
-            return BAD_VALUE;
-        }
-        std::vector<InputWindowInfo> handles;
-        for (size_t i = 0; i < count; i++) {
-            handles.push_back(InputWindowInfo::read(data));
-        }
-        const sp<ISetInputWindowsListener> setInputWindowsListener =
-                ISetInputWindowsListener::asInterface(data.readStrongBinder());
-        setInputWindows(handles, setInputWindowsListener);
-        break;
-    }
-    case REGISTER_INPUT_CHANNEL_TRANSACTION: {
-        CHECK_INTERFACE(IInputFlinger, data, reply);
-        sp<InputChannel> channel = InputChannel::read(data);
-        registerInputChannel(channel);
-        break;
-    }
-    case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
-        CHECK_INTERFACE(IInputFlinger, data, reply);
-        sp<InputChannel> channel = InputChannel::read(data);
-        unregisterInputChannel(channel);
-        break;
-    }
-    default:
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    return NO_ERROR;
-}
-
-};
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp
deleted file mode 100644
index a0330da..0000000
--- a/libs/input/ISetInputWindowsListener.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> {
-public:
-    explicit BpSetInputWindowsListener(const sp<IBinder>& impl)
-        : BpInterface<ISetInputWindowsListener>(impl) {
-    }
-
-    virtual ~BpSetInputWindowsListener() = default;
-
-    virtual void onSetInputWindowsFinished() {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor());
-        remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply,
-                IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener");
-
-status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case ON_SET_INPUT_WINDOWS_FINISHED: {
-            CHECK_INTERFACE(ISetInputWindowsListener, data, reply);
-            onSetInputWindowsFinished();
-            return NO_ERROR;
-        }
-        default: {
-            return BBinder::onTransact(code, data, reply, flags);
-        }
-    }
-}
-
-} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c243767..0ea3889 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,19 +17,26 @@
 #define LOG_TAG "Input"
 //#define LOG_NDEBUG 0
 
+#include <attestation/HmacKeyManager.h>
 #include <cutils/compiler.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <string.h>
 
+#include <android-base/stringprintf.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
-#ifdef __ANDROID__
+#ifdef __linux__
 #include <binder/Parcel.h>
+#endif
+#ifdef __ANDROID__
 #include <sys/random.h>
 #endif
 
+using android::base::StringPrintf;
+
 namespace android {
 
 const char* motionClassificationToString(MotionClassification classification) {
@@ -135,11 +142,11 @@
 // --- KeyEvent ---
 
 const char* KeyEvent::getLabel(int32_t keyCode) {
-    return getLabelByKeyCode(keyCode);
+    return InputEventLookup::getLabelByKeyCode(keyCode);
 }
 
 int32_t KeyEvent::getKeyCodeFromLabel(const char* label) {
-    return getKeyCodeByLabel(label);
+    return InputEventLookup::getKeyCodeByLabel(label);
 }
 
 void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
@@ -169,6 +176,18 @@
     mEventTime = from.mEventTime;
 }
 
+const char* KeyEvent::actionToString(int32_t action) {
+    // Convert KeyEvent action to string
+    switch (action) {
+        case AKEY_EVENT_ACTION_DOWN:
+            return "DOWN";
+        case AKEY_EVENT_ACTION_UP:
+            return "UP";
+        case AKEY_EVENT_ACTION_MULTIPLE:
+            return "MULTIPLE";
+    }
+    return "UNKNOWN";
+}
 
 // --- PointerCoords ---
 
@@ -237,7 +256,7 @@
     setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
 }
 
-#ifdef __ANDROID__
+#ifdef __linux__
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
     bits = parcel->readInt64();
 
@@ -289,6 +308,11 @@
     }
 }
 
+void PointerCoords::transform(const ui::Transform& transform) {
+    vec2 newCoords = transform.transform(getX(), getY());
+    setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x);
+    setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y);
+}
 
 // --- PointerProperties ---
 
@@ -308,10 +332,10 @@
 void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
                              std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
                              int32_t flags, int32_t edgeFlags, int32_t metaState,
-                             int32_t buttonState, MotionClassification classification, float xScale,
-                             float yScale, float xOffset, float yOffset, float xPrecision,
-                             float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
-                             nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                             int32_t buttonState, MotionClassification classification,
+                             const ui::Transform& transform, float xPrecision, float yPrecision,
+                             float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+                             nsecs_t eventTime, size_t pointerCount,
                              const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
     InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -322,10 +346,7 @@
     mMetaState = metaState;
     mButtonState = buttonState;
     mClassification = classification;
-    mXScale = xScale;
-    mYScale = yScale;
-    mXOffset = xOffset;
-    mYOffset = yOffset;
+    mTransform = transform;
     mXPrecision = xPrecision;
     mYPrecision = yPrecision;
     mRawXCursorPosition = rawXCursorPosition;
@@ -348,10 +369,7 @@
     mMetaState = other->mMetaState;
     mButtonState = other->mButtonState;
     mClassification = other->mClassification;
-    mXScale = other->mXScale;
-    mYScale = other->mYScale;
-    mXOffset = other->mXOffset;
-    mYOffset = other->mYOffset;
+    mTransform = other->mTransform;
     mXPrecision = other->mXPrecision;
     mYPrecision = other->mYPrecision;
     mRawXCursorPosition = other->mRawXCursorPosition;
@@ -364,7 +382,7 @@
         mSamplePointerCoords = other->mSamplePointerCoords;
     } else {
         mSampleEventTimes.clear();
-        mSampleEventTimes.push(other->getEventTime());
+        mSampleEventTimes.push_back(other->getEventTime());
         mSamplePointerCoords.clear();
         size_t pointerCount = other->getPointerCount();
         size_t historySize = other->getHistorySize();
@@ -376,23 +394,25 @@
 void MotionEvent::addSample(
         int64_t eventTime,
         const PointerCoords* pointerCoords) {
-    mSampleEventTimes.push(eventTime);
+    mSampleEventTimes.push_back(eventTime);
     mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
 }
 
 float MotionEvent::getXCursorPosition() const {
-    const float rawX = getRawXCursorPosition();
-    return rawX * mXScale + mXOffset;
+    vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+    return vals.x;
 }
 
 float MotionEvent::getYCursorPosition() const {
-    const float rawY = getRawYCursorPosition();
-    return rawY * mYScale + mYOffset;
+    vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+    return vals.y;
 }
 
 void MotionEvent::setCursorPosition(float x, float y) {
-    mRawXCursorPosition = (x - mXOffset) / mXScale;
-    mRawYCursorPosition = (y - mYOffset) / mYScale;
+    ui::Transform inverse = mTransform.inverse();
+    vec2 vals = inverse.transform(x, y);
+    mRawXCursorPosition = vals.x;
+    mRawYCursorPosition = vals.y;
 }
 
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -404,14 +424,7 @@
 }
 
 float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
-    float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
-    switch (axis) {
-    case AMOTION_EVENT_AXIS_X:
-        return value * mXScale + mXOffset;
-    case AMOTION_EVENT_AXIS_Y:
-        return value * mYScale + mYOffset;
-    }
-    return value;
+    return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
 }
 
 const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
@@ -426,14 +439,23 @@
 
 float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
         size_t historicalIndex) const {
-    float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+        return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    }
+
+    float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
+    float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
+    vec2 vals = mTransform.transform(rawX, rawY);
+
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        return value * mXScale + mXOffset;
+        return vals.x;
     case AMOTION_EVENT_AXIS_Y:
-        return value * mYScale + mYOffset;
+        return vals.y;
     }
-    return value;
+
+    // This should never happen
+    return 0;
 }
 
 ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -447,23 +469,24 @@
 }
 
 void MotionEvent::offsetLocation(float xOffset, float yOffset) {
-    mXOffset += xOffset;
-    mYOffset += yOffset;
+    float currXOffset = mTransform.tx();
+    float currYOffset = mTransform.ty();
+    mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
 }
 
 void MotionEvent::scale(float globalScaleFactor) {
-    mXOffset *= globalScaleFactor;
-    mYOffset *= globalScaleFactor;
+    mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
     mXPrecision *= globalScaleFactor;
     mYPrecision *= globalScaleFactor;
 
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
-        mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
+        mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
+                                                 globalScaleFactor);
     }
 }
 
-static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) {
+static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) {
     // Apply perspective transform like Skia.
     float newX = matrix[0] * x + matrix[1] * y + matrix[2];
     float newY = matrix[3] * x + matrix[4] * y + matrix[5];
@@ -471,22 +494,25 @@
     if (newZ) {
         newZ = 1.0f / newZ;
     }
-    *outX = newX * newZ;
-    *outY = newY * newZ;
+    vec2 transformedPoint;
+    transformedPoint.x = newX * newZ;
+    transformedPoint.y = newY * newZ;
+    return transformedPoint;
 }
 
-static float transformAngle(const float matrix[9], float angleRadians,
-        float originX, float originY) {
+static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX,
+                            float originY) {
     // Construct and transform a vector oriented at the specified clockwise angle from vertical.
     // Coordinate system: down is increasing Y, right is increasing X.
     float x = sinf(angleRadians);
     float y = -cosf(angleRadians);
-    transformPoint(matrix, x, y, &x, &y);
-    x -= originX;
-    y -= originY;
+    vec2 transformedPoint = transformPoint(matrix, x, y);
+
+    transformedPoint.x -= originX;
+    transformedPoint.y -= originY;
 
     // Derive the transformed vector's clockwise angle from vertical.
-    float result = atan2f(x, -y);
+    float result = atan2f(transformedPoint.x, -transformedPoint.y);
     if (result < - M_PI_2) {
         result += M_PI;
     } else if (result > M_PI_2) {
@@ -495,51 +521,51 @@
     return result;
 }
 
-void MotionEvent::transform(const float matrix[9]) {
-    // The tricky part of this implementation is to preserve the value of
-    // rawX and rawY.  So we apply the transformation to the first point
-    // then derive an appropriate new X/Y offset that will preserve rawX
-     // and rawY for that point.
-    float oldXOffset = mXOffset;
-    float oldYOffset = mYOffset;
-    float newX, newY;
-    float scaledRawX = getRawX(0) * mXScale;
-    float scaledRawY = getRawY(0) * mYScale;
-    transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
-    mXOffset = newX - scaledRawX;
-    mYOffset = newY - scaledRawY;
+void MotionEvent::transform(const std::array<float, 9>& matrix) {
+    // We want to preserve the rawX and rawY so we just update the transform
+    // using the values of the transform passed in
+    ui::Transform newTransform;
+    newTransform.set(matrix);
+    mTransform = newTransform * mTransform;
 
     // Determine how the origin is transformed by the matrix so that we
     // can transform orientation vectors.
-    float originX, originY;
-    transformPoint(matrix, 0, 0, &originX, &originY);
-
-    // Apply the transformation to cursor position.
-    if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
-        float x = mRawXCursorPosition * mXScale + oldXOffset;
-        float y = mRawYCursorPosition * mYScale + oldYOffset;
-        transformPoint(matrix, x, y, &x, &y);
-        mRawXCursorPosition = (x - mXOffset) / mXScale;
-        mRawYCursorPosition = (y - mYOffset) / mYScale;
-    }
+    vec2 origin = transformPoint(matrix, 0, 0);
 
     // Apply the transformation to all samples.
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
         PointerCoords& c = mSamplePointerCoords.editItemAt(i);
-        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
-        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
-        transformPoint(matrix, x, y, &x, &y);
-        c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
-        c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
-
         float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
         c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
-                transformAngle(matrix, orientation, originX, originY));
+                       transformAngle(matrix, orientation, origin.x, origin.y));
     }
 }
 
-#ifdef __ANDROID__
+#ifdef __linux__
+static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
+    float dsdx, dtdx, tx, dtdy, dsdy, ty;
+    status_t status = parcel.readFloat(&dsdx);
+    status |= parcel.readFloat(&dtdx);
+    status |= parcel.readFloat(&tx);
+    status |= parcel.readFloat(&dtdy);
+    status |= parcel.readFloat(&dsdy);
+    status |= parcel.readFloat(&ty);
+
+    transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+    return status;
+}
+
+static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) {
+    status_t status = parcel.writeFloat(transform.dsdx());
+    status |= parcel.writeFloat(transform.dtdx());
+    status |= parcel.writeFloat(transform.tx());
+    status |= parcel.writeFloat(transform.dtdy());
+    status |= parcel.writeFloat(transform.dsdy());
+    status |= parcel.writeFloat(transform.ty());
+    return status;
+}
+
 status_t MotionEvent::readFromParcel(Parcel* parcel) {
     size_t pointerCount = parcel->readInt32();
     size_t sampleCount = parcel->readInt32();
@@ -565,10 +591,11 @@
     mMetaState = parcel->readInt32();
     mButtonState = parcel->readInt32();
     mClassification = static_cast<MotionClassification>(parcel->readByte());
-    mXScale = parcel->readFloat();
-    mYScale = parcel->readFloat();
-    mXOffset = parcel->readFloat();
-    mYOffset = parcel->readFloat();
+
+    result = android::readFromParcel(mTransform, *parcel);
+    if (result != OK) {
+        return result;
+    }
     mXPrecision = parcel->readFloat();
     mYPrecision = parcel->readFloat();
     mRawXCursorPosition = parcel->readFloat();
@@ -578,7 +605,7 @@
     mPointerProperties.clear();
     mPointerProperties.setCapacity(pointerCount);
     mSampleEventTimes.clear();
-    mSampleEventTimes.setCapacity(sampleCount);
+    mSampleEventTimes.reserve(sampleCount);
     mSamplePointerCoords.clear();
     mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
 
@@ -591,7 +618,7 @@
 
     while (sampleCount > 0) {
         sampleCount--;
-        mSampleEventTimes.push(parcel->readInt64());
+        mSampleEventTimes.push_back(parcel->readInt64());
         for (size_t i = 0; i < pointerCount; i++) {
             mSamplePointerCoords.push();
             status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
@@ -623,10 +650,11 @@
     parcel->writeInt32(mMetaState);
     parcel->writeInt32(mButtonState);
     parcel->writeByte(static_cast<int8_t>(mClassification));
-    parcel->writeFloat(mXScale);
-    parcel->writeFloat(mYScale);
-    parcel->writeFloat(mXOffset);
-    parcel->writeFloat(mYOffset);
+
+    status_t result = android::writeToParcel(mTransform, *parcel);
+    if (result != OK) {
+        return result;
+    }
     parcel->writeFloat(mXPrecision);
     parcel->writeFloat(mYPrecision);
     parcel->writeFloat(mRawXCursorPosition);
@@ -641,7 +669,7 @@
 
     const PointerCoords* pc = mSamplePointerCoords.array();
     for (size_t h = 0; h < sampleCount; h++) {
-        parcel->writeInt64(mSampleEventTimes.itemAt(h));
+        parcel->writeInt64(mSampleEventTimes[h]);
         for (size_t i = 0; i < pointerCount; i++) {
             status_t status = (pc++)->writeToParcel(parcel);
             if (status) {
@@ -671,11 +699,44 @@
 }
 
 const char* MotionEvent::getLabel(int32_t axis) {
-    return getAxisLabel(axis);
+    return InputEventLookup::getAxisLabel(axis);
 }
 
 int32_t MotionEvent::getAxisFromLabel(const char* label) {
-    return getAxisByLabel(label);
+    return InputEventLookup::getAxisByLabel(label);
+}
+
+std::string MotionEvent::actionToString(int32_t action) {
+    // Convert MotionEvent action to string
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+        case AMOTION_EVENT_ACTION_DOWN:
+            return "DOWN";
+        case AMOTION_EVENT_ACTION_UP:
+            return "UP";
+        case AMOTION_EVENT_ACTION_MOVE:
+            return "MOVE";
+        case AMOTION_EVENT_ACTION_CANCEL:
+            return "CANCEL";
+        case AMOTION_EVENT_ACTION_OUTSIDE:
+            return "OUTSIDE";
+        case AMOTION_EVENT_ACTION_POINTER_DOWN:
+            return "POINTER_DOWN";
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+            return "POINTER_UP";
+        case AMOTION_EVENT_ACTION_HOVER_MOVE:
+            return "HOVER_MOVE";
+        case AMOTION_EVENT_ACTION_SCROLL:
+            return "SCROLL";
+        case AMOTION_EVENT_ACTION_HOVER_ENTER:
+            return "HOVER_ENTER";
+        case AMOTION_EVENT_ACTION_HOVER_EXIT:
+            return "HOVER_EXIT";
+        case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+            return "BUTTON_PRESS";
+        case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
+            return "BUTTON_RELEASE";
+    }
+    return android::base::StringPrintf("%" PRId32, action);
 }
 
 // --- FocusEvent ---
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
deleted file mode 100644
index 1d9f8a7..0000000
--- a/libs/input/InputApplication.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputApplication"
-
-#include <input/InputApplication.h>
-
-#include <android/log.h>
-
-namespace android {
-
-// --- InputApplicationHandle ---
-
-InputApplicationHandle::InputApplicationHandle() {
-}
-
-InputApplicationHandle::~InputApplicationHandle() {
-}
-
-InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
-    InputApplicationInfo ret;
-    ret.token = from.readStrongBinder();
-    ret.name = from.readString8().c_str();
-    ret.dispatchingTimeout = from.readInt64();
-
-    return ret;
-}
-
-status_t InputApplicationInfo::write(Parcel& output) const {
-    output.writeStrongBinder(token);
-    output.writeString8(String8(name.c_str()));
-    output.writeInt64(dispatchingTimeout);
-    
-    return OK;
-}
-
-} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4db9e06..34eba5b 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -46,9 +46,9 @@
 
 static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
         const std::string& name, InputDeviceConfigurationFileType type) {
-    path += CONFIGURATION_FILE_DIR[type];
+    path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)];
     path += name;
-    path += CONFIGURATION_FILE_EXTENSION[type];
+    path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)];
 }
 
 std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
@@ -153,14 +153,20 @@
     initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
 }
 
-InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
-        mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber),
-        mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal),
-        mHasMic(other.mHasMic), mSources(other.mSources),
-        mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap),
-        mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad),
-        mMotionRanges(other.mMotionRanges) {
-}
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other)
+      : mId(other.mId),
+        mGeneration(other.mGeneration),
+        mControllerNumber(other.mControllerNumber),
+        mIdentifier(other.mIdentifier),
+        mAlias(other.mAlias),
+        mIsExternal(other.mIsExternal),
+        mHasMic(other.mHasMic),
+        mSources(other.mSources),
+        mKeyboardType(other.mKeyboardType),
+        mKeyCharacterMap(other.mKeyCharacterMap),
+        mHasVibrator(other.mHasVibrator),
+        mHasButtonUnderPad(other.mHasButtonUnderPad),
+        mMotionRanges(other.mMotionRanges) {}
 
 InputDeviceInfo::~InputDeviceInfo() {
 }
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
new file mode 100644
index 0000000..c0aa2e2
--- /dev/null
+++ b/libs/input/InputEventLabels.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputEventLabels.h>
+
+#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
+#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
+#define DEFINE_LED(led) { #led, ALED_##led }
+#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
+
+namespace android {
+
+// NOTE: If you add a new keycode here you must also add it to several other files.
+//       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+#define KEYCODES_SEQUENCE \
+    DEFINE_KEYCODE(UNKNOWN), \
+    DEFINE_KEYCODE(SOFT_LEFT), \
+    DEFINE_KEYCODE(SOFT_RIGHT), \
+    DEFINE_KEYCODE(HOME), \
+    DEFINE_KEYCODE(BACK), \
+    DEFINE_KEYCODE(CALL), \
+    DEFINE_KEYCODE(ENDCALL), \
+    DEFINE_KEYCODE(0), \
+    DEFINE_KEYCODE(1), \
+    DEFINE_KEYCODE(2), \
+    DEFINE_KEYCODE(3), \
+    DEFINE_KEYCODE(4), \
+    DEFINE_KEYCODE(5), \
+    DEFINE_KEYCODE(6), \
+    DEFINE_KEYCODE(7), \
+    DEFINE_KEYCODE(8), \
+    DEFINE_KEYCODE(9), \
+    DEFINE_KEYCODE(STAR), \
+    DEFINE_KEYCODE(POUND), \
+    DEFINE_KEYCODE(DPAD_UP), \
+    DEFINE_KEYCODE(DPAD_DOWN), \
+    DEFINE_KEYCODE(DPAD_LEFT), \
+    DEFINE_KEYCODE(DPAD_RIGHT), \
+    DEFINE_KEYCODE(DPAD_CENTER), \
+    DEFINE_KEYCODE(VOLUME_UP), \
+    DEFINE_KEYCODE(VOLUME_DOWN), \
+    DEFINE_KEYCODE(POWER), \
+    DEFINE_KEYCODE(CAMERA), \
+    DEFINE_KEYCODE(CLEAR), \
+    DEFINE_KEYCODE(A), \
+    DEFINE_KEYCODE(B), \
+    DEFINE_KEYCODE(C), \
+    DEFINE_KEYCODE(D), \
+    DEFINE_KEYCODE(E), \
+    DEFINE_KEYCODE(F), \
+    DEFINE_KEYCODE(G), \
+    DEFINE_KEYCODE(H), \
+    DEFINE_KEYCODE(I), \
+    DEFINE_KEYCODE(J), \
+    DEFINE_KEYCODE(K), \
+    DEFINE_KEYCODE(L), \
+    DEFINE_KEYCODE(M), \
+    DEFINE_KEYCODE(N), \
+    DEFINE_KEYCODE(O), \
+    DEFINE_KEYCODE(P), \
+    DEFINE_KEYCODE(Q), \
+    DEFINE_KEYCODE(R), \
+    DEFINE_KEYCODE(S), \
+    DEFINE_KEYCODE(T), \
+    DEFINE_KEYCODE(U), \
+    DEFINE_KEYCODE(V), \
+    DEFINE_KEYCODE(W), \
+    DEFINE_KEYCODE(X), \
+    DEFINE_KEYCODE(Y), \
+    DEFINE_KEYCODE(Z), \
+    DEFINE_KEYCODE(COMMA), \
+    DEFINE_KEYCODE(PERIOD), \
+    DEFINE_KEYCODE(ALT_LEFT), \
+    DEFINE_KEYCODE(ALT_RIGHT), \
+    DEFINE_KEYCODE(SHIFT_LEFT), \
+    DEFINE_KEYCODE(SHIFT_RIGHT), \
+    DEFINE_KEYCODE(TAB), \
+    DEFINE_KEYCODE(SPACE), \
+    DEFINE_KEYCODE(SYM), \
+    DEFINE_KEYCODE(EXPLORER), \
+    DEFINE_KEYCODE(ENVELOPE), \
+    DEFINE_KEYCODE(ENTER), \
+    DEFINE_KEYCODE(DEL), \
+    DEFINE_KEYCODE(GRAVE), \
+    DEFINE_KEYCODE(MINUS), \
+    DEFINE_KEYCODE(EQUALS), \
+    DEFINE_KEYCODE(LEFT_BRACKET), \
+    DEFINE_KEYCODE(RIGHT_BRACKET), \
+    DEFINE_KEYCODE(BACKSLASH), \
+    DEFINE_KEYCODE(SEMICOLON), \
+    DEFINE_KEYCODE(APOSTROPHE), \
+    DEFINE_KEYCODE(SLASH), \
+    DEFINE_KEYCODE(AT), \
+    DEFINE_KEYCODE(NUM), \
+    DEFINE_KEYCODE(HEADSETHOOK), \
+    DEFINE_KEYCODE(FOCUS), \
+    DEFINE_KEYCODE(PLUS), \
+    DEFINE_KEYCODE(MENU), \
+    DEFINE_KEYCODE(NOTIFICATION), \
+    DEFINE_KEYCODE(SEARCH), \
+    DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \
+    DEFINE_KEYCODE(MEDIA_STOP), \
+    DEFINE_KEYCODE(MEDIA_NEXT), \
+    DEFINE_KEYCODE(MEDIA_PREVIOUS), \
+    DEFINE_KEYCODE(MEDIA_REWIND), \
+    DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \
+    DEFINE_KEYCODE(MUTE), \
+    DEFINE_KEYCODE(PAGE_UP), \
+    DEFINE_KEYCODE(PAGE_DOWN), \
+    DEFINE_KEYCODE(PICTSYMBOLS), \
+    DEFINE_KEYCODE(SWITCH_CHARSET), \
+    DEFINE_KEYCODE(BUTTON_A), \
+    DEFINE_KEYCODE(BUTTON_B), \
+    DEFINE_KEYCODE(BUTTON_C), \
+    DEFINE_KEYCODE(BUTTON_X), \
+    DEFINE_KEYCODE(BUTTON_Y), \
+    DEFINE_KEYCODE(BUTTON_Z), \
+    DEFINE_KEYCODE(BUTTON_L1), \
+    DEFINE_KEYCODE(BUTTON_R1), \
+    DEFINE_KEYCODE(BUTTON_L2), \
+    DEFINE_KEYCODE(BUTTON_R2), \
+    DEFINE_KEYCODE(BUTTON_THUMBL), \
+    DEFINE_KEYCODE(BUTTON_THUMBR), \
+    DEFINE_KEYCODE(BUTTON_START), \
+    DEFINE_KEYCODE(BUTTON_SELECT), \
+    DEFINE_KEYCODE(BUTTON_MODE), \
+    DEFINE_KEYCODE(ESCAPE), \
+    DEFINE_KEYCODE(FORWARD_DEL), \
+    DEFINE_KEYCODE(CTRL_LEFT), \
+    DEFINE_KEYCODE(CTRL_RIGHT), \
+    DEFINE_KEYCODE(CAPS_LOCK), \
+    DEFINE_KEYCODE(SCROLL_LOCK), \
+    DEFINE_KEYCODE(META_LEFT), \
+    DEFINE_KEYCODE(META_RIGHT), \
+    DEFINE_KEYCODE(FUNCTION), \
+    DEFINE_KEYCODE(SYSRQ), \
+    DEFINE_KEYCODE(BREAK), \
+    DEFINE_KEYCODE(MOVE_HOME), \
+    DEFINE_KEYCODE(MOVE_END), \
+    DEFINE_KEYCODE(INSERT), \
+    DEFINE_KEYCODE(FORWARD), \
+    DEFINE_KEYCODE(MEDIA_PLAY), \
+    DEFINE_KEYCODE(MEDIA_PAUSE), \
+    DEFINE_KEYCODE(MEDIA_CLOSE), \
+    DEFINE_KEYCODE(MEDIA_EJECT), \
+    DEFINE_KEYCODE(MEDIA_RECORD), \
+    DEFINE_KEYCODE(F1), \
+    DEFINE_KEYCODE(F2), \
+    DEFINE_KEYCODE(F3), \
+    DEFINE_KEYCODE(F4), \
+    DEFINE_KEYCODE(F5), \
+    DEFINE_KEYCODE(F6), \
+    DEFINE_KEYCODE(F7), \
+    DEFINE_KEYCODE(F8), \
+    DEFINE_KEYCODE(F9), \
+    DEFINE_KEYCODE(F10), \
+    DEFINE_KEYCODE(F11), \
+    DEFINE_KEYCODE(F12), \
+    DEFINE_KEYCODE(NUM_LOCK), \
+    DEFINE_KEYCODE(NUMPAD_0), \
+    DEFINE_KEYCODE(NUMPAD_1), \
+    DEFINE_KEYCODE(NUMPAD_2), \
+    DEFINE_KEYCODE(NUMPAD_3), \
+    DEFINE_KEYCODE(NUMPAD_4), \
+    DEFINE_KEYCODE(NUMPAD_5), \
+    DEFINE_KEYCODE(NUMPAD_6), \
+    DEFINE_KEYCODE(NUMPAD_7), \
+    DEFINE_KEYCODE(NUMPAD_8), \
+    DEFINE_KEYCODE(NUMPAD_9), \
+    DEFINE_KEYCODE(NUMPAD_DIVIDE), \
+    DEFINE_KEYCODE(NUMPAD_MULTIPLY), \
+    DEFINE_KEYCODE(NUMPAD_SUBTRACT), \
+    DEFINE_KEYCODE(NUMPAD_ADD), \
+    DEFINE_KEYCODE(NUMPAD_DOT), \
+    DEFINE_KEYCODE(NUMPAD_COMMA), \
+    DEFINE_KEYCODE(NUMPAD_ENTER), \
+    DEFINE_KEYCODE(NUMPAD_EQUALS), \
+    DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \
+    DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \
+    DEFINE_KEYCODE(VOLUME_MUTE), \
+    DEFINE_KEYCODE(INFO), \
+    DEFINE_KEYCODE(CHANNEL_UP), \
+    DEFINE_KEYCODE(CHANNEL_DOWN), \
+    DEFINE_KEYCODE(ZOOM_IN), \
+    DEFINE_KEYCODE(ZOOM_OUT), \
+    DEFINE_KEYCODE(TV), \
+    DEFINE_KEYCODE(WINDOW), \
+    DEFINE_KEYCODE(GUIDE), \
+    DEFINE_KEYCODE(DVR), \
+    DEFINE_KEYCODE(BOOKMARK), \
+    DEFINE_KEYCODE(CAPTIONS), \
+    DEFINE_KEYCODE(SETTINGS), \
+    DEFINE_KEYCODE(TV_POWER), \
+    DEFINE_KEYCODE(TV_INPUT), \
+    DEFINE_KEYCODE(STB_POWER), \
+    DEFINE_KEYCODE(STB_INPUT), \
+    DEFINE_KEYCODE(AVR_POWER), \
+    DEFINE_KEYCODE(AVR_INPUT), \
+    DEFINE_KEYCODE(PROG_RED), \
+    DEFINE_KEYCODE(PROG_GREEN), \
+    DEFINE_KEYCODE(PROG_YELLOW), \
+    DEFINE_KEYCODE(PROG_BLUE), \
+    DEFINE_KEYCODE(APP_SWITCH), \
+    DEFINE_KEYCODE(BUTTON_1), \
+    DEFINE_KEYCODE(BUTTON_2), \
+    DEFINE_KEYCODE(BUTTON_3), \
+    DEFINE_KEYCODE(BUTTON_4), \
+    DEFINE_KEYCODE(BUTTON_5), \
+    DEFINE_KEYCODE(BUTTON_6), \
+    DEFINE_KEYCODE(BUTTON_7), \
+    DEFINE_KEYCODE(BUTTON_8), \
+    DEFINE_KEYCODE(BUTTON_9), \
+    DEFINE_KEYCODE(BUTTON_10), \
+    DEFINE_KEYCODE(BUTTON_11), \
+    DEFINE_KEYCODE(BUTTON_12), \
+    DEFINE_KEYCODE(BUTTON_13), \
+    DEFINE_KEYCODE(BUTTON_14), \
+    DEFINE_KEYCODE(BUTTON_15), \
+    DEFINE_KEYCODE(BUTTON_16), \
+    DEFINE_KEYCODE(LANGUAGE_SWITCH), \
+    DEFINE_KEYCODE(MANNER_MODE), \
+    DEFINE_KEYCODE(3D_MODE), \
+    DEFINE_KEYCODE(CONTACTS), \
+    DEFINE_KEYCODE(CALENDAR), \
+    DEFINE_KEYCODE(MUSIC), \
+    DEFINE_KEYCODE(CALCULATOR), \
+    DEFINE_KEYCODE(ZENKAKU_HANKAKU), \
+    DEFINE_KEYCODE(EISU), \
+    DEFINE_KEYCODE(MUHENKAN), \
+    DEFINE_KEYCODE(HENKAN), \
+    DEFINE_KEYCODE(KATAKANA_HIRAGANA), \
+    DEFINE_KEYCODE(YEN), \
+    DEFINE_KEYCODE(RO), \
+    DEFINE_KEYCODE(KANA), \
+    DEFINE_KEYCODE(ASSIST), \
+    DEFINE_KEYCODE(BRIGHTNESS_DOWN), \
+    DEFINE_KEYCODE(BRIGHTNESS_UP), \
+    DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \
+    DEFINE_KEYCODE(SLEEP), \
+    DEFINE_KEYCODE(WAKEUP), \
+    DEFINE_KEYCODE(PAIRING), \
+    DEFINE_KEYCODE(MEDIA_TOP_MENU), \
+    DEFINE_KEYCODE(11), \
+    DEFINE_KEYCODE(12), \
+    DEFINE_KEYCODE(LAST_CHANNEL), \
+    DEFINE_KEYCODE(TV_DATA_SERVICE), \
+    DEFINE_KEYCODE(VOICE_ASSIST), \
+    DEFINE_KEYCODE(TV_RADIO_SERVICE), \
+    DEFINE_KEYCODE(TV_TELETEXT), \
+    DEFINE_KEYCODE(TV_NUMBER_ENTRY), \
+    DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \
+    DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \
+    DEFINE_KEYCODE(TV_SATELLITE), \
+    DEFINE_KEYCODE(TV_SATELLITE_BS), \
+    DEFINE_KEYCODE(TV_SATELLITE_CS), \
+    DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \
+    DEFINE_KEYCODE(TV_NETWORK), \
+    DEFINE_KEYCODE(TV_ANTENNA_CABLE), \
+    DEFINE_KEYCODE(TV_INPUT_HDMI_1), \
+    DEFINE_KEYCODE(TV_INPUT_HDMI_2), \
+    DEFINE_KEYCODE(TV_INPUT_HDMI_3), \
+    DEFINE_KEYCODE(TV_INPUT_HDMI_4), \
+    DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \
+    DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \
+    DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \
+    DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \
+    DEFINE_KEYCODE(TV_INPUT_VGA_1), \
+    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \
+    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \
+    DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \
+    DEFINE_KEYCODE(TV_ZOOM_MODE), \
+    DEFINE_KEYCODE(TV_CONTENTS_MENU), \
+    DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \
+    DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \
+    DEFINE_KEYCODE(HELP), \
+    DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \
+    DEFINE_KEYCODE(NAVIGATE_NEXT), \
+    DEFINE_KEYCODE(NAVIGATE_IN), \
+    DEFINE_KEYCODE(NAVIGATE_OUT), \
+    DEFINE_KEYCODE(STEM_PRIMARY), \
+    DEFINE_KEYCODE(STEM_1), \
+    DEFINE_KEYCODE(STEM_2), \
+    DEFINE_KEYCODE(STEM_3), \
+    DEFINE_KEYCODE(DPAD_UP_LEFT), \
+    DEFINE_KEYCODE(DPAD_DOWN_LEFT), \
+    DEFINE_KEYCODE(DPAD_UP_RIGHT), \
+    DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \
+    DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \
+    DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \
+    DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \
+    DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \
+    DEFINE_KEYCODE(SOFT_SLEEP), \
+    DEFINE_KEYCODE(CUT), \
+    DEFINE_KEYCODE(COPY), \
+    DEFINE_KEYCODE(PASTE), \
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \
+    DEFINE_KEYCODE(ALL_APPS), \
+    DEFINE_KEYCODE(REFRESH), \
+    DEFINE_KEYCODE(THUMBS_UP), \
+    DEFINE_KEYCODE(THUMBS_DOWN), \
+    DEFINE_KEYCODE(PROFILE_SWITCH)
+
+// NOTE: If you add a new axis here you must also add it to several other files.
+//       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+#define AXES_SEQUENCE \
+    DEFINE_AXIS(X), \
+    DEFINE_AXIS(Y), \
+    DEFINE_AXIS(PRESSURE), \
+    DEFINE_AXIS(SIZE), \
+    DEFINE_AXIS(TOUCH_MAJOR), \
+    DEFINE_AXIS(TOUCH_MINOR), \
+    DEFINE_AXIS(TOOL_MAJOR), \
+    DEFINE_AXIS(TOOL_MINOR), \
+    DEFINE_AXIS(ORIENTATION), \
+    DEFINE_AXIS(VSCROLL), \
+    DEFINE_AXIS(HSCROLL), \
+    DEFINE_AXIS(Z), \
+    DEFINE_AXIS(RX), \
+    DEFINE_AXIS(RY), \
+    DEFINE_AXIS(RZ), \
+    DEFINE_AXIS(HAT_X), \
+    DEFINE_AXIS(HAT_Y), \
+    DEFINE_AXIS(LTRIGGER), \
+    DEFINE_AXIS(RTRIGGER), \
+    DEFINE_AXIS(THROTTLE), \
+    DEFINE_AXIS(RUDDER), \
+    DEFINE_AXIS(WHEEL), \
+    DEFINE_AXIS(GAS), \
+    DEFINE_AXIS(BRAKE), \
+    DEFINE_AXIS(DISTANCE), \
+    DEFINE_AXIS(TILT), \
+    DEFINE_AXIS(SCROLL), \
+    DEFINE_AXIS(RELATIVE_X), \
+    DEFINE_AXIS(RELATIVE_Y), \
+    {"RESERVED_29", 29}, \
+    {"RESERVED_30", 30}, \
+    {"RESERVED_31", 31}, \
+    DEFINE_AXIS(GENERIC_1), \
+    DEFINE_AXIS(GENERIC_2), \
+    DEFINE_AXIS(GENERIC_3), \
+    DEFINE_AXIS(GENERIC_4), \
+    DEFINE_AXIS(GENERIC_5), \
+    DEFINE_AXIS(GENERIC_6), \
+    DEFINE_AXIS(GENERIC_7), \
+    DEFINE_AXIS(GENERIC_8), \
+    DEFINE_AXIS(GENERIC_9), \
+    DEFINE_AXIS(GENERIC_10), \
+    DEFINE_AXIS(GENERIC_11), \
+    DEFINE_AXIS(GENERIC_12), \
+    DEFINE_AXIS(GENERIC_13), \
+    DEFINE_AXIS(GENERIC_14), \
+    DEFINE_AXIS(GENERIC_15), \
+    DEFINE_AXIS(GENERIC_16)
+
+
+// NOTE: If you add new LEDs here, you must also add them to Input.h
+#define LEDS_SEQUENCE \
+    DEFINE_LED(NUM_LOCK), \
+    DEFINE_LED(CAPS_LOCK), \
+    DEFINE_LED(SCROLL_LOCK), \
+    DEFINE_LED(COMPOSE), \
+    DEFINE_LED(KANA), \
+    DEFINE_LED(SLEEP), \
+    DEFINE_LED(SUSPEND), \
+    DEFINE_LED(MUTE), \
+    DEFINE_LED(MISC), \
+    DEFINE_LED(MAIL), \
+    DEFINE_LED(CHARGING), \
+    DEFINE_LED(CONTROLLER_1), \
+    DEFINE_LED(CONTROLLER_2), \
+    DEFINE_LED(CONTROLLER_3), \
+    DEFINE_LED(CONTROLLER_4)
+
+#define FLAGS_SEQUENCE \
+    DEFINE_FLAG(VIRTUAL), \
+    DEFINE_FLAG(FUNCTION), \
+    DEFINE_FLAG(GESTURE), \
+    DEFINE_FLAG(WAKE)
+
+// --- InputEventLookup ---
+const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
+
+int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+                                         const char* literal) {
+    std::string str(literal);
+    auto it = map.find(str);
+    return it != map.end() ? it->second : 0;
+}
+
+const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec,
+                                                 int value) {
+    if (static_cast<size_t>(value) < vec.size()) {
+        return vec[value].literal;
+    }
+    return nullptr;
+}
+
+int32_t InputEventLookup::getKeyCodeByLabel(const char* label) {
+    return int32_t(lookupValueByLabel(KEYCODES, label));
+}
+
+const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
+    if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
+        return lookupLabelByValue(KEY_NAMES, keyCode);
+    }
+    return nullptr;
+}
+
+uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) {
+    return uint32_t(lookupValueByLabel(FLAGS, label));
+}
+
+int32_t InputEventLookup::getAxisByLabel(const char* label) {
+    return int32_t(lookupValueByLabel(AXES, label));
+}
+
+const char* InputEventLookup::getAxisLabel(int32_t axisId) {
+    return lookupLabelByValue(AXES_NAMES, axisId);
+}
+
+int32_t InputEventLookup::getLedByLabel(const char* label) {
+    return int32_t(lookupValueByLabel(LEDS, label));
+}
+
+} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 11af23e..85df405 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -133,12 +133,11 @@
 
     // Write the header
     msg->header.type = header.type;
+    msg->header.seq = header.seq;
 
     // Write the body
     switch(header.type) {
         case InputMessage::Type::KEY: {
-            // uint32_t seq
-            msg->body.key.seq = body.key.seq;
             // int32_t eventId
             msg->body.key.eventId = body.key.eventId;
             // nsecs_t eventTime
@@ -168,8 +167,6 @@
             break;
         }
         case InputMessage::Type::MOTION: {
-            // uint32_t seq
-            msg->body.motion.seq = body.motion.seq;
             // int32_t eventId
             msg->body.motion.eventId = body.motion.eventId;
             // nsecs_t eventTime
@@ -198,14 +195,14 @@
             msg->body.motion.edgeFlags = body.motion.edgeFlags;
             // nsecs_t downTime
             msg->body.motion.downTime = body.motion.downTime;
-            // float xScale
-            msg->body.motion.xScale = body.motion.xScale;
-            // float yScale
-            msg->body.motion.yScale = body.motion.yScale;
-            // float xOffset
-            msg->body.motion.xOffset = body.motion.xOffset;
-            // float yOffset
-            msg->body.motion.yOffset = body.motion.yOffset;
+
+            msg->body.motion.dsdx = body.motion.dsdx;
+            msg->body.motion.dtdx = body.motion.dtdx;
+            msg->body.motion.dtdy = body.motion.dtdy;
+            msg->body.motion.dsdy = body.motion.dsdy;
+            msg->body.motion.tx = body.motion.tx;
+            msg->body.motion.ty = body.motion.ty;
+
             // float xPrecision
             msg->body.motion.xPrecision = body.motion.xPrecision;
             // float yPrecision
@@ -232,12 +229,10 @@
             break;
         }
         case InputMessage::Type::FINISHED: {
-            msg->body.finished.seq = body.finished.seq;
             msg->body.finished.handled = body.finished.handled;
             break;
         }
         case InputMessage::Type::FOCUS: {
-            msg->body.focus.seq = body.focus.seq;
             msg->body.focus.eventId = body.focus.eventId;
             msg->body.focus.hasFocus = body.focus.hasFocus;
             msg->body.focus.inTouchMode = body.focus.inTouchMode;
@@ -248,39 +243,40 @@
 
 // --- InputChannel ---
 
-sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
-                                      sp<IBinder> token) {
+std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,
+                                                   android::base::unique_fd fd, sp<IBinder> token) {
     const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
     if (result != 0) {
         LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
                          strerror(errno));
         return nullptr;
     }
-    return new InputChannel(name, std::move(fd), token);
+    // using 'new' to access a non-public constructor
+    return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
 }
 
-InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
-      : mName(name), mFd(std::move(fd)), mToken(token) {
+InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
+      : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
     if (DEBUG_CHANNEL_LIFECYCLE) {
-        ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+        ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
     }
 }
 
 InputChannel::~InputChannel() {
     if (DEBUG_CHANNEL_LIFECYCLE) {
-        ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
+        ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
     }
 }
 
 status_t InputChannel::openInputChannelPair(const std::string& name,
-        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+                                            std::unique_ptr<InputChannel>& outServerChannel,
+                                            std::unique_ptr<InputChannel>& outClientChannel) {
     int sockets[2];
     if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
         status_t result = -errno;
-        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
-                name.c_str(), errno);
-        outServerChannel.clear();
-        outClientChannel.clear();
+        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d", name.c_str(), errno);
+        outServerChannel.reset();
+        outClientChannel.reset();
         return result;
     }
 
@@ -308,7 +304,7 @@
     msg->getSanitizedCopy(&cleanMsg);
     ssize_t nWrite;
     do {
-        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+        nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
     } while (nWrite == -1 && errno == EINTR);
 
     if (nWrite < 0) {
@@ -343,7 +339,7 @@
 status_t InputChannel::receiveMessage(InputMessage* msg) {
     ssize_t nRead;
     do {
-        nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+        nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
     } while (nRead == -1 && errno == EINTR);
 
     if (nRead < 0) {
@@ -380,10 +376,43 @@
     return OK;
 }
 
-sp<InputChannel> InputChannel::dup() const {
+std::unique_ptr<InputChannel> InputChannel::dup() const {
+    base::unique_fd newFd(dupFd());
+    return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
+}
+
+void InputChannel::copyTo(InputChannel& outChannel) const {
+    outChannel.mName = getName();
+    outChannel.mFd = dupFd();
+    outChannel.mToken = getConnectionToken();
+}
+
+status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+    return parcel->writeStrongBinder(mToken)
+            ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
+}
+
+status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+    mToken = parcel->readStrongBinder();
+    return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
+}
+
+sp<IBinder> InputChannel::getConnectionToken() const {
+    return mToken;
+}
+
+base::unique_fd InputChannel::dupFd() const {
     android::base::unique_fd newFd(::dup(getFd()));
     if (!newFd.ok()) {
-        ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+        ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
               strerror(errno));
         const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
         // If this process is out of file descriptors, then throwing that might end up exploding
@@ -392,47 +421,14 @@
         // Other failures could be client errors, so we still propagate those back to the caller.
         LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
                             getName().c_str());
-        return nullptr;
+        return {};
     }
-    return InputChannel::create(mName, std::move(newFd), mToken);
-}
-
-status_t InputChannel::write(Parcel& out) const {
-    status_t s = out.writeCString(getName().c_str());
-    if (s != OK) {
-        return s;
-    }
-
-    s = out.writeStrongBinder(mToken);
-    if (s != OK) {
-        return s;
-    }
-
-    s = out.writeUniqueFileDescriptor(mFd);
-    return s;
-}
-
-sp<InputChannel> InputChannel::read(const Parcel& from) {
-    std::string name = from.readCString();
-    sp<IBinder> token = from.readStrongBinder();
-    android::base::unique_fd rawFd;
-    status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
-    if (fdResult != OK) {
-        return nullptr;
-    }
-
-    return InputChannel::create(name, std::move(rawFd), token);
-}
-
-sp<IBinder> InputChannel::getConnectionToken() const {
-    return mToken;
+    return newFd;
 }
 
 // --- InputPublisher ---
 
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
-        mChannel(channel) {
-}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
 
 InputPublisher::~InputPublisher() {
 }
@@ -463,7 +459,7 @@
 
     InputMessage msg;
     msg.header.type = InputMessage::Type::KEY;
-    msg.body.key.seq = seq;
+    msg.header.seq = seq;
     msg.body.key.eventId = eventId;
     msg.body.key.deviceId = deviceId;
     msg.body.key.source = source;
@@ -484,10 +480,10 @@
         uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
         std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
         int32_t edgeFlags, int32_t metaState, int32_t buttonState,
-        MotionClassification classification, float xScale, float yScale, float xOffset,
-        float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
-        float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
-        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
+        MotionClassification classification, const ui::Transform& transform, float xPrecision,
+        float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf(
                 "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -495,17 +491,18 @@
         ATRACE_NAME(message.c_str());
     }
     if (DEBUG_TRANSPORT_ACTIONS) {
+        std::string transformString;
+        transform.dump(transformString, "transform", "        ");
         ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
               "displayId=%" PRId32 ", "
               "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
-              "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
-              "xOffset=%.1f, yOffset=%.1f, "
+              "metaState=0x%x, buttonState=0x%x, classification=%s,"
               "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
-              "pointerCount=%" PRIu32,
+              "pointerCount=%" PRIu32 " \n%s",
               mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
               flags, edgeFlags, metaState, buttonState,
-              motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
-              xPrecision, yPrecision, downTime, eventTime, pointerCount);
+              motionClassificationToString(classification), xPrecision, yPrecision, downTime,
+              eventTime, pointerCount, transformString.c_str());
     }
 
     if (!seq) {
@@ -521,7 +518,7 @@
 
     InputMessage msg;
     msg.header.type = InputMessage::Type::MOTION;
-    msg.body.motion.seq = seq;
+    msg.header.seq = seq;
     msg.body.motion.eventId = eventId;
     msg.body.motion.deviceId = deviceId;
     msg.body.motion.source = source;
@@ -534,10 +531,12 @@
     msg.body.motion.metaState = metaState;
     msg.body.motion.buttonState = buttonState;
     msg.body.motion.classification = classification;
-    msg.body.motion.xScale = xScale;
-    msg.body.motion.yScale = yScale;
-    msg.body.motion.xOffset = xOffset;
-    msg.body.motion.yOffset = yOffset;
+    msg.body.motion.dsdx = transform.dsdx();
+    msg.body.motion.dtdx = transform.dtdx();
+    msg.body.motion.dtdy = transform.dtdy();
+    msg.body.motion.dsdy = transform.dsdy();
+    msg.body.motion.tx = transform.tx();
+    msg.body.motion.ty = transform.ty();
     msg.body.motion.xPrecision = xPrecision;
     msg.body.motion.yPrecision = yPrecision;
     msg.body.motion.xCursorPosition = xCursorPosition;
@@ -565,7 +564,7 @@
 
     InputMessage msg;
     msg.header.type = InputMessage::Type::FOCUS;
-    msg.body.focus.seq = seq;
+    msg.header.seq = seq;
     msg.body.focus.eventId = eventId;
     msg.body.focus.hasFocus = hasFocus ? 1 : 0;
     msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
@@ -589,17 +588,15 @@
                 mChannel->getName().c_str(), msg.header.type);
         return UNKNOWN_ERROR;
     }
-    *outSeq = msg.body.finished.seq;
+    *outSeq = msg.header.seq;
     *outHandled = msg.body.finished.handled == 1;
     return OK;
 }
 
 // --- InputConsumer ---
 
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
-        mResampleTouch(isTouchResamplingEnabled()),
-        mChannel(channel), mMsgDeferred(false) {
-}
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
+      : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
 
 InputConsumer::~InputConsumer() {
 }
@@ -650,7 +647,7 @@
                 if (!keyEvent) return NO_MEMORY;
 
                 initializeKeyEvent(keyEvent, &mMsg);
-                *outSeq = mMsg.body.key.seq;
+                *outSeq = mMsg.header.seq;
                 *outEvent = keyEvent;
                 if (DEBUG_TRANSPORT_ACTIONS) {
                     ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
@@ -662,9 +659,9 @@
             case InputMessage::Type::MOTION: {
                 ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
                 if (batchIndex >= 0) {
-                    Batch& batch = mBatches.editItemAt(batchIndex);
+                    Batch& batch = mBatches[batchIndex];
                     if (canAddSample(batch, &mMsg)) {
-                        batch.samples.push(mMsg);
+                        batch.samples.push_back(mMsg);
                         if (DEBUG_TRANSPORT_ACTIONS) {
                             ALOGD("channel '%s' consumer ~ appended to batch event",
                                   mChannel->getName().c_str());
@@ -675,18 +672,18 @@
                         // No need to process events that we are going to cancel anyways
                         const size_t count = batch.samples.size();
                         for (size_t i = 0; i < count; i++) {
-                            const InputMessage& msg = batch.samples.itemAt(i);
-                            sendFinishedSignal(msg.body.motion.seq, false);
+                            const InputMessage& msg = batch.samples[i];
+                            sendFinishedSignal(msg.header.seq, false);
                         }
-                        batch.samples.removeItemsAt(0, count);
-                        mBatches.removeAt(batchIndex);
+                        batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+                        mBatches.erase(mBatches.begin() + batchIndex);
                     } else {
                         // We cannot append to the batch in progress, so we need to consume
                         // the previous batch right now and defer the new message until later.
                         mMsgDeferred = true;
                         status_t result = consumeSamples(factory, batch, batch.samples.size(),
                                                          outSeq, outEvent);
-                        mBatches.removeAt(batchIndex);
+                        mBatches.erase(mBatches.begin() + batchIndex);
                         if (result) {
                             return result;
                         }
@@ -702,9 +699,9 @@
                 // Start a new batch if needed.
                 if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
                     mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                    mBatches.push();
-                    Batch& batch = mBatches.editTop();
-                    batch.samples.push(mMsg);
+                    Batch batch;
+                    batch.samples.push_back(mMsg);
+                    mBatches.push_back(batch);
                     if (DEBUG_TRANSPORT_ACTIONS) {
                         ALOGD("channel '%s' consumer ~ started batch event",
                               mChannel->getName().c_str());
@@ -717,7 +714,7 @@
 
                 updateTouchState(mMsg);
                 initializeMotionEvent(motionEvent, &mMsg);
-                *outSeq = mMsg.body.motion.seq;
+                *outSeq = mMsg.header.seq;
                 *outEvent = motionEvent;
 
                 if (DEBUG_TRANSPORT_ACTIONS) {
@@ -738,7 +735,7 @@
                 if (!focusEvent) return NO_MEMORY;
 
                 initializeFocusEvent(focusEvent, &mMsg);
-                *outSeq = mMsg.body.focus.seq;
+                *outSeq = mMsg.header.seq;
                 *outEvent = focusEvent;
                 break;
             }
@@ -752,10 +749,10 @@
     status_t result;
     for (size_t i = mBatches.size(); i > 0; ) {
         i--;
-        Batch& batch = mBatches.editItemAt(i);
+        Batch& batch = mBatches[i];
         if (frameTime < 0) {
             result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
-            mBatches.removeAt(i);
+            mBatches.erase(mBatches.begin() + i);
             return result;
         }
 
@@ -770,11 +767,11 @@
 
         result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
         const InputMessage* next;
-        if (batch.samples.isEmpty()) {
-            mBatches.removeAt(i);
+        if (batch.samples.empty()) {
+            mBatches.erase(mBatches.begin() + i);
             next = nullptr;
         } else {
-            next = &batch.samples.itemAt(0);
+            next = &batch.samples[0];
         }
         if (!result && mResampleTouch) {
             resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
@@ -792,20 +789,20 @@
 
     uint32_t chain = 0;
     for (size_t i = 0; i < count; i++) {
-        InputMessage& msg = batch.samples.editItemAt(i);
+        InputMessage& msg = batch.samples[i];
         updateTouchState(msg);
         if (i) {
             SeqChain seqChain;
-            seqChain.seq = msg.body.motion.seq;
+            seqChain.seq = msg.header.seq;
             seqChain.chain = chain;
-            mSeqChains.push(seqChain);
+            mSeqChains.push_back(seqChain);
             addSample(motionEvent, &msg);
         } else {
             initializeMotionEvent(motionEvent, &msg);
         }
-        chain = msg.body.motion.seq;
+        chain = msg.header.seq;
     }
-    batch.samples.removeItemsAt(0, count);
+    batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
 
     *outSeq = chain;
     *outEvent = motionEvent;
@@ -827,10 +824,10 @@
     case AMOTION_EVENT_ACTION_DOWN: {
         ssize_t index = findTouchState(deviceId, source);
         if (index < 0) {
-            mTouchStates.push();
+            mTouchStates.push_back({});
             index = mTouchStates.size() - 1;
         }
-        TouchState& touchState = mTouchStates.editItemAt(index);
+        TouchState& touchState = mTouchStates[index];
         touchState.initialize(deviceId, source);
         touchState.addHistory(msg);
         break;
@@ -839,7 +836,7 @@
     case AMOTION_EVENT_ACTION_MOVE: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            TouchState& touchState = mTouchStates.editItemAt(index);
+            TouchState& touchState = mTouchStates[index];
             touchState.addHistory(msg);
             rewriteMessage(touchState, msg);
         }
@@ -849,7 +846,7 @@
     case AMOTION_EVENT_ACTION_POINTER_DOWN: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            TouchState& touchState = mTouchStates.editItemAt(index);
+            TouchState& touchState = mTouchStates[index];
             touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
             rewriteMessage(touchState, msg);
         }
@@ -859,7 +856,7 @@
     case AMOTION_EVENT_ACTION_POINTER_UP: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            TouchState& touchState = mTouchStates.editItemAt(index);
+            TouchState& touchState = mTouchStates[index];
             rewriteMessage(touchState, msg);
             touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
         }
@@ -869,7 +866,7 @@
     case AMOTION_EVENT_ACTION_SCROLL: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            TouchState& touchState = mTouchStates.editItemAt(index);
+            TouchState& touchState = mTouchStates[index];
             rewriteMessage(touchState, msg);
         }
         break;
@@ -879,9 +876,9 @@
     case AMOTION_EVENT_ACTION_CANCEL: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            TouchState& touchState = mTouchStates.editItemAt(index);
+            TouchState& touchState = mTouchStates[index];
             rewriteMessage(touchState, msg);
-            mTouchStates.removeAt(index);
+            mTouchStates.erase(mTouchStates.begin() + index);
         }
         break;
     }
@@ -938,7 +935,7 @@
         return;
     }
 
-    TouchState& touchState = mTouchStates.editItemAt(index);
+    TouchState& touchState = mTouchStates[index];
     if (touchState.historySize < 1) {
 #if DEBUG_RESAMPLING
         ALOGD("Not resampled, no history for device.");
@@ -1084,11 +1081,11 @@
         size_t chainIndex = 0;
         for (size_t i = seqChainCount; i > 0; ) {
              i--;
-             const SeqChain& seqChain = mSeqChains.itemAt(i);
+             const SeqChain& seqChain = mSeqChains[i];
              if (seqChain.seq == currentSeq) {
                  currentSeq = seqChain.chain;
                  chainSeqs[chainIndex++] = currentSeq;
-                 mSeqChains.removeAt(i);
+                 mSeqChains.erase(mSeqChains.begin() + i);
              }
         }
         status_t status = OK;
@@ -1102,7 +1099,7 @@
                 SeqChain seqChain;
                 seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
                 seqChain.chain = chainSeqs[chainIndex];
-                mSeqChains.push(seqChain);
+                mSeqChains.push_back(seqChain);
                 if (!chainIndex) break;
                 chainIndex--;
             }
@@ -1117,7 +1114,7 @@
 status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
     InputMessage msg;
     msg.header.type = InputMessage::Type::FINISHED;
-    msg.body.finished.seq = seq;
+    msg.header.seq = seq;
     msg.body.finished.handled = handled ? 1 : 0;
     return mChannel->sendMessage(&msg);
 }
@@ -1127,23 +1124,23 @@
 }
 
 bool InputConsumer::hasPendingBatch() const {
-    return !mBatches.isEmpty();
+    return !mBatches.empty();
 }
 
 int32_t InputConsumer::getPendingBatchSource() const {
-    if (mBatches.isEmpty()) {
+    if (mBatches.empty()) {
         return AINPUT_SOURCE_CLASS_NONE;
     }
 
-    const Batch& batch = mBatches.itemAt(0);
-    const InputMessage& head = batch.samples.itemAt(0);
+    const Batch& batch = mBatches[0];
+    const InputMessage& head = batch.samples[0];
     return head.body.motion.source;
 }
 
 ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
     for (size_t i = 0; i < mBatches.size(); i++) {
-        const Batch& batch = mBatches.itemAt(i);
-        const InputMessage& head = batch.samples.itemAt(0);
+        const Batch& batch = mBatches[i];
+        const InputMessage& head = batch.samples[0];
         if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
             return i;
         }
@@ -1153,7 +1150,7 @@
 
 ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
     for (size_t i = 0; i < mTouchStates.size(); i++) {
-        const TouchState& touchState = mTouchStates.itemAt(i);
+        const TouchState& touchState = mTouchStates[i];
         if (touchState.deviceId == deviceId && touchState.source == source) {
             return i;
         }
@@ -1183,16 +1180,18 @@
         pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
     }
 
+    ui::Transform transform;
+    transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
+                   msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
     event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
                       msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
                       msg->body.motion.actionButton, msg->body.motion.flags,
                       msg->body.motion.edgeFlags, msg->body.motion.metaState,
-                      msg->body.motion.buttonState, msg->body.motion.classification,
-                      msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
-                      msg->body.motion.yOffset, msg->body.motion.xPrecision,
-                      msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
-                      msg->body.motion.yCursorPosition, msg->body.motion.downTime,
-                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+                      msg->body.motion.buttonState, msg->body.motion.classification, transform,
+                      msg->body.motion.xPrecision, msg->body.motion.yPrecision,
+                      msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+                      msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
+                      pointerProperties, pointerCoords);
 }
 
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1207,7 +1206,7 @@
 }
 
 bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
-    const InputMessage& head = batch.samples.itemAt(0);
+    const InputMessage& head = batch.samples[0];
     uint32_t pointerCount = msg->body.motion.pointerCount;
     if (head.body.motion.pointerCount != pointerCount
             || head.body.motion.action != msg->body.motion.action) {
@@ -1225,11 +1224,72 @@
 ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
     size_t numSamples = batch.samples.size();
     size_t index = 0;
-    while (index < numSamples
-            && batch.samples.itemAt(index).body.motion.eventTime <= time) {
+    while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
         index += 1;
     }
     return ssize_t(index) - 1;
 }
 
+std::string InputConsumer::dump() const {
+    std::string out;
+    out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
+    out = out + "mChannel = " + mChannel->getName() + "\n";
+    out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
+    if (mMsgDeferred) {
+        out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n";
+    }
+    out += "Batches:\n";
+    for (const Batch& batch : mBatches) {
+        out += "    Batch:\n";
+        for (const InputMessage& msg : batch.samples) {
+            out += android::base::StringPrintf("        Message %" PRIu32 ": %s ", msg.header.seq,
+                                               InputMessage::typeToString(msg.header.type));
+            switch (msg.header.type) {
+                case InputMessage::Type::KEY: {
+                    out += android::base::StringPrintf("action=%s keycode=%" PRId32,
+                                                       KeyEvent::actionToString(
+                                                               msg.body.key.action),
+                                                       msg.body.key.keyCode);
+                    break;
+                }
+                case InputMessage::Type::MOTION: {
+                    out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
+                    for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+                        const float x = msg.body.motion.pointers[i].coords.getX();
+                        const float y = msg.body.motion.pointers[i].coords.getY();
+                        out += android::base::StringPrintf("\n            Pointer %" PRIu32
+                                                           " : x=%.1f y=%.1f",
+                                                           i, x, y);
+                    }
+                    break;
+                }
+                case InputMessage::Type::FINISHED: {
+                    out += android::base::StringPrintf("handled=%s",
+                                                       toString(msg.body.finished.handled));
+                    break;
+                }
+                case InputMessage::Type::FOCUS: {
+                    out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
+                                                       toString(msg.body.focus.hasFocus),
+                                                       toString(msg.body.focus.inTouchMode));
+                    break;
+                }
+            }
+            out += "\n";
+        }
+    }
+    if (mBatches.empty()) {
+        out += "    <empty>\n";
+    }
+    out += "mSeqChains:\n";
+    for (const SeqChain& chain : mSeqChains) {
+        out += android::base::StringPrintf("    chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
+                                           chain.chain);
+    }
+    if (mSeqChains.empty()) {
+        out += "    <empty>\n";
+    }
+    return out;
+}
+
 } // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 0455022..8546bbb 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <type_traits>
 #define LOG_TAG "InputWindow"
 #define LOG_NDEBUG 0
 
@@ -24,122 +25,8 @@
 
 #include <log/log.h>
 
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
 namespace android {
 
-const char* inputWindowFlagToString(uint32_t flag) {
-    switch (flag) {
-        case InputWindowInfo::FLAG_ALLOW_LOCK_WHILE_SCREEN_ON: {
-            return "ALLOW_LOCK_WHILE_SCREEN_ON";
-        }
-        case InputWindowInfo::FLAG_DIM_BEHIND: {
-            return "DIM_BEHIND";
-        }
-        case InputWindowInfo::FLAG_BLUR_BEHIND: {
-            return "BLUR_BEHIND";
-        }
-        case InputWindowInfo::FLAG_NOT_FOCUSABLE: {
-            return "NOT_FOCUSABLE";
-        }
-        case InputWindowInfo::FLAG_NOT_TOUCHABLE: {
-            return "NOT_TOUCHABLE";
-        }
-        case InputWindowInfo::FLAG_NOT_TOUCH_MODAL: {
-            return "NOT_TOUCH_MODAL";
-        }
-        case InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING: {
-            return "TOUCHABLE_WHEN_WAKING";
-        }
-        case InputWindowInfo::FLAG_KEEP_SCREEN_ON: {
-            return "KEEP_SCREEN_ON";
-        }
-        case InputWindowInfo::FLAG_LAYOUT_IN_SCREEN: {
-            return "LAYOUT_IN_SCREEN";
-        }
-        case InputWindowInfo::FLAG_LAYOUT_NO_LIMITS: {
-            return "LAYOUT_NO_LIMITS";
-        }
-        case InputWindowInfo::FLAG_FULLSCREEN: {
-            return "FULLSCREEN";
-        }
-        case InputWindowInfo::FLAG_FORCE_NOT_FULLSCREEN: {
-            return "FORCE_NOT_FULLSCREEN";
-        }
-        case InputWindowInfo::FLAG_DITHER: {
-            return "DITHER";
-        }
-        case InputWindowInfo::FLAG_SECURE: {
-            return "SECURE";
-        }
-        case InputWindowInfo::FLAG_SCALED: {
-            return "SCALED";
-        }
-        case InputWindowInfo::FLAG_IGNORE_CHEEK_PRESSES: {
-            return "IGNORE_CHEEK_PRESSES";
-        }
-        case InputWindowInfo::FLAG_LAYOUT_INSET_DECOR: {
-            return "LAYOUT_INSET_DECOR";
-        }
-        case InputWindowInfo::FLAG_ALT_FOCUSABLE_IM: {
-            return "ALT_FOCUSABLE_IM";
-        }
-        case InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH: {
-            return "WATCH_OUTSIDE_TOUCH";
-        }
-        case InputWindowInfo::FLAG_SHOW_WHEN_LOCKED: {
-            return "SHOW_WHEN_LOCKED";
-        }
-        case InputWindowInfo::FLAG_SHOW_WALLPAPER: {
-            return "SHOW_WALLPAPER";
-        }
-        case InputWindowInfo::FLAG_TURN_SCREEN_ON: {
-            return "TURN_SCREEN_ON";
-        }
-        case InputWindowInfo::FLAG_DISMISS_KEYGUARD: {
-            return "DISMISS_KEYGUARD";
-        }
-        case InputWindowInfo::FLAG_SPLIT_TOUCH: {
-            return "SPLIT_TOUCH";
-        }
-        case InputWindowInfo::FLAG_HARDWARE_ACCELERATED: {
-            return "HARDWARE_ACCELERATED";
-        }
-        case InputWindowInfo::FLAG_LAYOUT_IN_OVERSCAN: {
-            return "LAYOUT_IN_OVERSCAN";
-        }
-        case InputWindowInfo::FLAG_TRANSLUCENT_STATUS: {
-            return "TRANSLUCENT_STATUS";
-        }
-        case InputWindowInfo::FLAG_TRANSLUCENT_NAVIGATION: {
-            return "TRANSLUCENT_NAVIGATION";
-        }
-        case InputWindowInfo::FLAG_LOCAL_FOCUS_MODE: {
-            return "LOCAL_FOCUS_MODE";
-        }
-        case InputWindowInfo::FLAG_SLIPPERY: {
-            return "SLIPPERY";
-        }
-        case InputWindowInfo::FLAG_LAYOUT_ATTACHED_IN_DECOR: {
-            return "LAYOUT_ATTACHED_IN_DECOR";
-        }
-        case InputWindowInfo::FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS: {
-            return "DRAWS_SYSTEM_BAR_BACKGROUNDS";
-        }
-    }
-    return "UNKNOWN";
-}
-
-std::string inputWindowFlagsToString(uint32_t flags) {
-    std::string result;
-    for (BitSet32 bits(flags); !bits.isEmpty();) {
-        uint32_t bit = bits.clearLastMarkedBit(); // counts from left
-        const uint32_t flag = 1 << (32 - bit - 1);
-        result += android::base::StringPrintf("%s | ", inputWindowFlagToString(flag));
-    }
-    return result;
-}
 
 // --- InputWindowInfo ---
 void InputWindowInfo::addTouchableRegion(const Rect& region) {
@@ -156,7 +43,7 @@
 }
 
 bool InputWindowInfo::supportsSplitTouch() const {
-    return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+    return flags.test(Flag::SPLIT_TOUCH);
 }
 
 bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
@@ -164,96 +51,158 @@
             && frameTop < other->frameBottom && frameBottom > other->frameTop;
 }
 
-status_t InputWindowInfo::write(Parcel& output) const {
+bool InputWindowInfo::operator==(const InputWindowInfo& info) const {
+    return info.token == token && info.id == id && info.name == name && info.flags == flags &&
+            info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
+            info.frameLeft == frameLeft && info.frameTop == frameTop &&
+            info.frameRight == frameRight && info.frameBottom == frameBottom &&
+            info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
+            info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+            info.visible == visible && info.trustedOverlay == trustedOverlay &&
+            info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+            info.hasWallpaper == hasWallpaper && info.paused == paused &&
+            info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+            info.packageName == packageName && info.inputFeatures == inputFeatures &&
+            info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
+            info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
+            info.applicationInfo == applicationInfo;
+}
+
+status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
     if (name.empty()) {
-        output.writeInt32(0);
+        parcel->writeInt32(0);
         return OK;
     }
-    output.writeInt32(1);
-    status_t s = output.writeStrongBinder(token);
-    if (s != OK) return s;
+    parcel->writeInt32(1);
 
-    output.writeInt32(id);
-    output.writeString8(String8(name.c_str()));
-    output.writeInt32(layoutParamsFlags);
-    output.writeInt32(layoutParamsType);
-    output.writeInt64(dispatchingTimeout);
-    output.writeInt32(frameLeft);
-    output.writeInt32(frameTop);
-    output.writeInt32(frameRight);
-    output.writeInt32(frameBottom);
-    output.writeInt32(surfaceInset);
-    output.writeFloat(globalScaleFactor);
-    output.writeFloat(windowXScale);
-    output.writeFloat(windowYScale);
-    output.writeBool(visible);
-    output.writeBool(canReceiveKeys);
-    output.writeBool(hasFocus);
-    output.writeBool(hasWallpaper);
-    output.writeBool(paused);
-    output.writeBool(trustedOverlay);
-    output.writeInt32(ownerPid);
-    output.writeInt32(ownerUid);
-    output.writeInt32(inputFeatures);
-    output.writeInt32(displayId);
-    output.writeInt32(portalToDisplayId);
-    applicationInfo.write(output);
-    output.write(touchableRegion);
-    output.writeBool(replaceTouchableRegionWithCrop);
-    output.writeStrongBinder(touchableRegionCropHandle.promote());
-    return OK;
+    // clang-format off
+    status_t status = parcel->writeStrongBinder(token) ?:
+        parcel->writeInt64(dispatchingTimeout.count()) ?:
+        parcel->writeInt32(id) ?:
+        parcel->writeUtf8AsUtf16(name) ?:
+        parcel->writeInt32(flags.get()) ?:
+        parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?:
+        parcel->writeInt32(frameLeft) ?:
+        parcel->writeInt32(frameTop) ?:
+        parcel->writeInt32(frameRight) ?:
+        parcel->writeInt32(frameBottom) ?:
+        parcel->writeInt32(surfaceInset) ?:
+        parcel->writeFloat(globalScaleFactor) ?:
+        parcel->writeFloat(alpha) ?:
+        parcel->writeFloat(transform.dsdx()) ?:
+        parcel->writeFloat(transform.dtdx()) ?:
+        parcel->writeFloat(transform.tx()) ?:
+        parcel->writeFloat(transform.dtdy()) ?:
+        parcel->writeFloat(transform.dsdy()) ?:
+        parcel->writeFloat(transform.ty()) ?:
+        parcel->writeBool(visible) ?:
+        parcel->writeBool(focusable) ?:
+        parcel->writeBool(hasWallpaper) ?:
+        parcel->writeBool(paused) ?:
+        parcel->writeBool(trustedOverlay) ?:
+        parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
+        parcel->writeInt32(ownerPid) ?:
+        parcel->writeInt32(ownerUid) ?:
+        parcel->writeUtf8AsUtf16(packageName) ?:
+        parcel->writeInt32(inputFeatures.get()) ?:
+        parcel->writeInt32(displayId) ?:
+        parcel->writeInt32(portalToDisplayId) ?:
+        applicationInfo.writeToParcel(parcel) ?:
+        parcel->write(touchableRegion) ?:
+        parcel->writeBool(replaceTouchableRegionWithCrop) ?:
+        parcel->writeStrongBinder(touchableRegionCropHandle.promote());
+    // clang-format on
+    return status;
 }
 
-InputWindowInfo InputWindowInfo::read(const Parcel& from) {
-    InputWindowInfo ret;
-
-    if (from.readInt32() == 0) {
-        return ret;
+status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+    if (parcel->readInt32() == 0) {
+        return OK;
     }
 
-    ret.token = from.readStrongBinder();
-    ret.id = from.readInt32();
-    ret.name = from.readString8().c_str();
-    ret.layoutParamsFlags = from.readInt32();
-    ret.layoutParamsType = from.readInt32();
-    ret.dispatchingTimeout = from.readInt64();
-    ret.frameLeft = from.readInt32();
-    ret.frameTop = from.readInt32();
-    ret.frameRight = from.readInt32();
-    ret.frameBottom = from.readInt32();
-    ret.surfaceInset = from.readInt32();
-    ret.globalScaleFactor = from.readFloat();
-    ret.windowXScale = from.readFloat();
-    ret.windowYScale = from.readFloat();
-    ret.visible = from.readBool();
-    ret.canReceiveKeys = from.readBool();
-    ret.hasFocus = from.readBool();
-    ret.hasWallpaper = from.readBool();
-    ret.paused = from.readBool();
-    ret.trustedOverlay = from.readBool();
-    ret.ownerPid = from.readInt32();
-    ret.ownerUid = from.readInt32();
-    ret.inputFeatures = from.readInt32();
-    ret.displayId = from.readInt32();
-    ret.portalToDisplayId = from.readInt32();
-    ret.applicationInfo = InputApplicationInfo::read(from);
-    from.read(ret.touchableRegion);
-    ret.replaceTouchableRegionWithCrop = from.readBool();
-    ret.touchableRegionCropHandle = from.readStrongBinder();
+    token = parcel->readStrongBinder();
+    dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64());
+    status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name);
+    if (status != OK) {
+        return status;
+    }
 
-    return ret;
-}
+    flags = Flags<Flag>(parcel->readInt32());
+    type = static_cast<Type>(parcel->readInt32());
+    float dsdx, dtdx, tx, dtdy, dsdy, ty;
+    int32_t touchOcclusionModeInt;
+    // clang-format off
+    status = parcel->readInt32(&frameLeft) ?:
+        parcel->readInt32(&frameTop) ?:
+        parcel->readInt32(&frameRight) ?:
+        parcel->readInt32(&frameBottom) ?:
+        parcel->readInt32(&surfaceInset) ?:
+        parcel->readFloat(&globalScaleFactor) ?:
+        parcel->readFloat(&alpha) ?:
+        parcel->readFloat(&dsdx) ?:
+        parcel->readFloat(&dtdx) ?:
+        parcel->readFloat(&tx) ?:
+        parcel->readFloat(&dtdy) ?:
+        parcel->readFloat(&dsdy) ?:
+        parcel->readFloat(&ty) ?:
+        parcel->readBool(&visible) ?:
+        parcel->readBool(&focusable) ?:
+        parcel->readBool(&hasWallpaper) ?:
+        parcel->readBool(&paused) ?:
+        parcel->readBool(&trustedOverlay) ?:
+        parcel->readInt32(&touchOcclusionModeInt) ?:
+        parcel->readInt32(&ownerPid) ?:
+        parcel->readInt32(&ownerUid) ?:
+        parcel->readUtf8FromUtf16(&packageName);
+    // clang-format on
 
-InputWindowInfo::InputWindowInfo(const Parcel& from) {
-    *this = read(from);
+    if (status != OK) {
+        return status;
+    }
+
+    touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+
+    inputFeatures = Flags<Feature>(parcel->readInt32());
+    status = parcel->readInt32(&displayId) ?:
+        parcel->readInt32(&portalToDisplayId) ?:
+        applicationInfo.readFromParcel(parcel) ?:
+        parcel->read(touchableRegion) ?:
+        parcel->readBool(&replaceTouchableRegionWithCrop);
+
+    if (status != OK) {
+        return status;
+    }
+
+    touchableRegionCropHandle = parcel->readStrongBinder();
+    transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+    return OK;
 }
 
 // --- InputWindowHandle ---
 
-InputWindowHandle::InputWindowHandle() {
+InputWindowHandle::InputWindowHandle() {}
+
+InputWindowHandle::~InputWindowHandle() {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {}
+
+status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const {
+    return mInfo.writeToParcel(parcel);
 }
 
-InputWindowHandle::~InputWindowHandle() {
+status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) {
+    return mInfo.readFromParcel(parcel);
 }
 
 void InputWindowHandle::releaseChannel() {
@@ -267,5 +216,4 @@
 void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
     mInfo = handle->mInfo;
 }
-
 } // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index cb68165..f5432ad 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -19,14 +19,14 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef __ANDROID__
+#ifdef __linux__
 #include <binder/Parcel.h>
 #endif
-
 #include <android/keycodes.h>
+#include <attestation/HmacKeyManager.h>
 #include <input/InputEventLabels.h>
-#include <input/Keyboard.h>
 #include <input/KeyCharacterMap.h>
+#include <input/Keyboard.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -85,15 +85,12 @@
 
 // --- KeyCharacterMap ---
 
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
+KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
 
-KeyCharacterMap::KeyCharacterMap() :
-    mType(KEYBOARD_TYPE_UNKNOWN) {
-}
-
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
-    RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
-    mKeysByUsageCode(other.mKeysByUsageCode) {
+KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
+      : mType(other.mType),
+        mKeysByScanCode(other.mKeysByScanCode),
+        mKeysByUsageCode(other.mKeysByUsageCode) {
     for (size_t i = 0; i < other.mKeys.size(); i++) {
         mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
     }
@@ -106,104 +103,95 @@
     }
 }
 
-status_t KeyCharacterMap::load(const std::string& filename,
-        Format format, sp<KeyCharacterMap>* outMap) {
-    outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+                                                                     Format format) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
-        ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
-    } else {
-        status = load(tokenizer, format, outMap);
-        delete tokenizer;
+        return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get(), format);
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
 }
 
-status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
-        Format format, sp<KeyCharacterMap>* outMap) {
-    outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
+        const std::string& filename, const char* contents, Format format) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
     if (status) {
         ALOGE("Error %d opening key character map.", status);
-    } else {
-        status = load(tokenizer, format, outMap);
-        delete tokenizer;
+        return Errorf("Error {} opening key character map.", status);
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get(), format);
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
 }
 
-status_t KeyCharacterMap::load(Tokenizer* tokenizer,
-        Format format, sp<KeyCharacterMap>* outMap) {
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
+                                                                     Format format) {
     status_t status = OK;
-    sp<KeyCharacterMap> map = new KeyCharacterMap();
+    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
     if (!map.get()) {
         ALOGE("Error allocating key character map.");
-        status = NO_MEMORY;
-    } else {
-#if DEBUG_PARSER_PERFORMANCE
-        nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
-        Parser parser(map.get(), 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) {
-            *outMap = map;
-        }
+        return Errorf("Error allocating key character map.");
     }
-    return status;
+#if DEBUG_PARSER_PERFORMANCE
+    nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+    Parser parser(map.get(), 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;
+    }
+
+    return Errorf("Load KeyCharacterMap failed {}.", status);
 }
 
-sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
-        const sp<KeyCharacterMap>& overlay) {
-    if (overlay == nullptr) {
-        return base;
-    }
-    if (base == nullptr) {
-        return overlay;
-    }
-
-    sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
-    for (size_t i = 0; i < overlay->mKeys.size(); i++) {
-        int32_t keyCode = overlay->mKeys.keyAt(i);
-        Key* key = overlay->mKeys.valueAt(i);
-        ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
+void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+    for (size_t i = 0; i < overlay.mKeys.size(); i++) {
+        int32_t keyCode = overlay.mKeys.keyAt(i);
+        Key* key = overlay.mKeys.valueAt(i);
+        ssize_t oldIndex = mKeys.indexOfKey(keyCode);
         if (oldIndex >= 0) {
-            delete map->mKeys.valueAt(oldIndex);
-            map->mKeys.editValueAt(oldIndex) = new Key(*key);
+            delete mKeys.valueAt(oldIndex);
+            mKeys.editValueAt(oldIndex) = new Key(*key);
         } else {
-            map->mKeys.add(keyCode, new Key(*key));
+            mKeys.add(keyCode, new Key(*key));
         }
     }
 
-    for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
-        map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
-                overlay->mKeysByScanCode.valueAt(i));
+    for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
+        mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
+                                        overlay.mKeysByScanCode.valueAt(i));
     }
 
-    for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
-        map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
-                overlay->mKeysByUsageCode.valueAt(i));
+    for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
+        mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
+                                         overlay.mKeysByUsageCode.valueAt(i));
     }
-    return map;
+    mLoadFileName = overlay.mLoadFileName;
 }
 
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
-    return sEmpty;
-}
-
-int32_t KeyCharacterMap::getKeyboardType() const {
+KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
     return mType;
 }
 
+const std::string KeyCharacterMap::getLoadFileName() const {
+    return mLoadFileName;
+}
+
 char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
     char16_t result = 0;
     const Key* key;
@@ -599,10 +587,14 @@
     }
 }
 
-#ifdef __ANDROID__
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
-    sp<KeyCharacterMap> map = new KeyCharacterMap();
-    map->mType = parcel->readInt32();
+#ifdef __linux__
+std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return nullptr;
+    }
+    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+    map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
         return nullptr;
@@ -656,7 +648,11 @@
 }
 
 void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
-    parcel->writeInt32(mType);
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return;
+    }
+    parcel->writeInt32(static_cast<int32_t>(mType));
 
     size_t numKeys = mKeys.size();
     parcel->writeInt32(numKeys);
@@ -677,8 +673,7 @@
         parcel->writeInt32(0);
     }
 }
-#endif
-
+#endif // __linux__
 
 // --- KeyCharacterMap::Key ---
 
@@ -782,20 +777,20 @@
         return BAD_VALUE;
     }
 
-    if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+    if (mMap->mType == KeyboardType::UNKNOWN) {
         ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
                 mTokenizer->getLocation().string());
         return BAD_VALUE;
     }
 
-    if (mFormat == FORMAT_BASE) {
-        if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
+    if (mFormat == Format::BASE) {
+        if (mMap->mType == KeyboardType::OVERLAY) {
             ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
                     mTokenizer->getLocation().string());
             return BAD_VALUE;
         }
-    } else if (mFormat == FORMAT_OVERLAY) {
-        if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
+    } else if (mFormat == Format::OVERLAY) {
+        if (mMap->mType != KeyboardType::OVERLAY) {
             ALOGE("%s: Overlay keyboard layout missing required keyboard "
                     "'type OVERLAY' declaration.",
                     mTokenizer->getLocation().string());
@@ -807,7 +802,7 @@
 }
 
 status_t KeyCharacterMap::Parser::parseType() {
-    if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
+    if (mMap->mType != KeyboardType::UNKNOWN) {
         ALOGE("%s: Duplicate keyboard 'type' declaration.",
                 mTokenizer->getLocation().string());
         return BAD_VALUE;
@@ -816,20 +811,20 @@
     KeyboardType type;
     String8 typeToken = mTokenizer->nextToken(WHITESPACE);
     if (typeToken == "NUMERIC") {
-        type = KEYBOARD_TYPE_NUMERIC;
+        type = KeyboardType::NUMERIC;
     } else if (typeToken == "PREDICTIVE") {
-        type = KEYBOARD_TYPE_PREDICTIVE;
+        type = KeyboardType::PREDICTIVE;
     } else if (typeToken == "ALPHA") {
-        type = KEYBOARD_TYPE_ALPHA;
+        type = KeyboardType::ALPHA;
     } else if (typeToken == "FULL") {
-        type = KEYBOARD_TYPE_FULL;
+        type = KeyboardType::FULL;
     } else if (typeToken == "SPECIAL_FUNCTION") {
         ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set "
                 "the property 'keyboard.specialFunction' to '1' there instead.");
         // TODO: return BAD_VALUE here in Q
-        type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+        type = KeyboardType::SPECIAL_FUNCTION;
     } else if (typeToken == "OVERLAY") {
-        type = KEYBOARD_TYPE_OVERLAY;
+        type = KeyboardType::OVERLAY;
     } else {
         ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
                 typeToken.string());
@@ -880,7 +875,7 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
@@ -897,7 +892,7 @@
 
 status_t KeyCharacterMap::Parser::parseKey() {
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
@@ -1017,7 +1012,7 @@
             } else if (token == "fallback") {
                 mTokenizer->skipDelimiters(WHITESPACE);
                 token = mTokenizer->nextToken(WHITESPACE);
-                int32_t keyCode = getKeyCodeByLabel(token.string());
+                int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
                 if (!keyCode) {
                     ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
                             mTokenizer->getLocation().string(),
@@ -1034,7 +1029,7 @@
             } else if (token == "replace") {
                 mTokenizer->skipDelimiters(WHITESPACE);
                 token = mTokenizer->nextToken(WHITESPACE);
-                int32_t keyCode = getKeyCodeByLabel(token.string());
+                int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
                 if (!keyCode) {
                     ALOGE("%s: Invalid key code label for replace, got '%s'.",
                             mTokenizer->getLocation().string(),
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index efca68d..16ce48a 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -49,37 +49,60 @@
 KeyLayoutMap::~KeyLayoutMap() {
 }
 
-status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
-    outMap->clear();
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
+                                                                       const char* contents) {
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+    if (status) {
+        ALOGE("Error %d opening key layout map.", status);
+        return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
+    }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get());
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
+}
 
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
         ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
-    } else {
-        sp<KeyLayoutMap> map = new KeyLayoutMap();
-        if (!map.get()) {
-            ALOGE("Error allocating key layout map.");
-            status = NO_MEMORY;
-        } else {
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
-            Parser parser(map.get(), tokenizer);
-            status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
-            ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
-                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),
-                    elapsedTime / 1000000.0);
-#endif
-            if (!status) {
-                *outMap = map;
-            }
-        }
-        delete tokenizer;
+        return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get());
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
+}
+
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) {
+    std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap());
+    status_t status = OK;
+    if (!map.get()) {
+        ALOGE("Error allocating key layout map.");
+        return Errorf("Error allocating key layout map.");
+    } else {
+#if DEBUG_PARSER_PERFORMANCE
+        nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+        Parser parser(map.get(), tokenizer);
+        status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+        nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+        ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
+              tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+              elapsedTime / 1000000.0);
+#endif
+        if (!status) {
+            return std::move(map);
+        }
+    }
+    return Errorf("Load KeyLayoutMap failed {}.", status);
 }
 
 status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
@@ -264,7 +287,7 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
@@ -277,7 +300,7 @@
         if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
 
         String8 flagToken = mTokenizer->nextToken(WHITESPACE);
-        uint32_t flag = getKeyFlagByLabel(flagToken.string());
+        uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
         if (!flag) {
             ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
                     flagToken.string());
@@ -326,7 +349,7 @@
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 axisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.axis = getAxisByLabel(axisToken.string());
+        axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string());
         if (axisInfo.axis < 0) {
             ALOGE("%s: Expected inverted axis label, got '%s'.",
                     mTokenizer->getLocation().string(), axisToken.string());
@@ -346,7 +369,7 @@
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.axis = getAxisByLabel(lowAxisToken.string());
+        axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
         if (axisInfo.axis < 0) {
             ALOGE("%s: Expected low axis label, got '%s'.",
                     mTokenizer->getLocation().string(), lowAxisToken.string());
@@ -355,14 +378,14 @@
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
+        axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
         if (axisInfo.highAxis < 0) {
             ALOGE("%s: Expected high axis label, got '%s'.",
                     mTokenizer->getLocation().string(), highAxisToken.string());
             return BAD_VALUE;
         }
     } else {
-        axisInfo.axis = getAxisByLabel(token.string());
+        axisInfo.axis = InputEventLookup::getAxisByLabel(token.string());
         if (axisInfo.axis < 0) {
             ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
                     mTokenizer->getLocation().string(), token.string());
@@ -428,7 +451,7 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t ledCode = getLedByLabel(ledCodeToken.string());
+    int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
     if (ledCode < 0) {
         ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
                 ledCodeToken.string());
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 56900c1..14dc9e5 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -105,35 +105,34 @@
 
 status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
         const std::string& name) {
-    std::string path(getPath(deviceIdentifier, name,
-            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+    std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT));
     if (path.empty()) {
         return NAME_NOT_FOUND;
     }
 
-    status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
-    if (status) {
-        return status;
+    base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+    if (!ret) {
+        return ret.error().code();
     }
-
+    keyLayoutMap = *ret;
     keyLayoutFile = path;
     return OK;
 }
 
 status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
         const std::string& name) {
-    std::string path = getPath(deviceIdentifier, name,
-            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+    std::string path =
+            getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP);
     if (path.empty()) {
         return NAME_NOT_FOUND;
     }
 
-    status_t status = KeyCharacterMap::load(path,
-            KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
-    if (status) {
-        return status;
+    base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+            KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+    if (!ret) {
+        return ret.error().code();
     }
-
+    keyCharacterMap = *ret;
     keyCharacterMapFile = path;
     return OK;
 }
@@ -160,9 +159,9 @@
 bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
         const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
     // TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q
-    if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration)
-            || keyMap->keyCharacterMap->getKeyboardType()
-                    == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+    if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) ||
+        keyMap->keyCharacterMap->getKeyboardType() ==
+                KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) {
         return false;
     }
 
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
new file mode 100644
index 0000000..a842166
--- /dev/null
+++ b/libs/input/PropertyMap.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PropertyMap"
+
+#include <input/PropertyMap.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {}
+
+PropertyMap::~PropertyMap() {}
+
+void PropertyMap::clear() {
+    mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+    mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+    return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+    ssize_t index = mProperties.indexOfKey(key);
+    if (index < 0) {
+        return false;
+    }
+
+    outValue = mProperties.valueAt(index);
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+    int32_t intValue;
+    if (!tryGetProperty(key, intValue)) {
+        return false;
+    }
+
+    outValue = intValue;
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+    String8 stringValue;
+    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+        return false;
+    }
+
+    char* end;
+    int value = strtol(stringValue.string(), &end, 10);
+    if (*end != '\0') {
+        ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.string(),
+              stringValue.string());
+        return false;
+    }
+    outValue = value;
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+    String8 stringValue;
+    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+        return false;
+    }
+
+    char* end;
+    float value = strtof(stringValue.string(), &end);
+    if (*end != '\0') {
+        ALOGW("Property key '%s' has invalid value '%s'.  Expected a float.", key.string(),
+              stringValue.string());
+        return false;
+    }
+    outValue = value;
+    return true;
+}
+
+void PropertyMap::addAll(const PropertyMap* map) {
+    for (size_t i = 0; i < map->mProperties.size(); i++) {
+        mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+    }
+}
+
+android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
+    std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
+    if (outMap == nullptr) {
+        return android::base::Error(NO_MEMORY) << "Error allocating property map.";
+    }
+
+    Tokenizer* rawTokenizer;
+    status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
+    std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
+    if (status) {
+        ALOGE("Error %d opening property file %s.", status, filename);
+    } else {
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+            Parser parser(outMap.get(), tokenizer.get());
+            status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+            ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
+                  tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+                  elapsedTime / 1000000.0);
+#endif
+            if (status) {
+                return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
+            }
+    }
+    return std::move(outMap);
+}
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer)
+      : mMap(map), mTokenizer(tokenizer) {}
+
+PropertyMap::Parser::~Parser() {}
+
+status_t PropertyMap::Parser::parse() {
+    while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+        ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+              mTokenizer->peekRemainderOfLine().string());
+#endif
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+
+        if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+            String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+            if (keyToken.isEmpty()) {
+                ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+
+            if (mTokenizer->nextChar() != '=') {
+                ALOGE("%s: Expected '=' between property key and value.",
+                      mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+
+            String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+            if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+                ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
+                      mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+            if (!mTokenizer->isEol()) {
+                ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
+                      mTokenizer->peekRemainderOfLine().string());
+                return BAD_VALUE;
+            }
+
+            if (mMap->hasProperty(keyToken)) {
+                ALOGE("%s: Duplicate property value for key '%s'.",
+                      mTokenizer->getLocation().string(), keyToken.string());
+                return BAD_VALUE;
+            }
+
+            mMap->addProperty(keyToken, valueToken);
+        }
+
+        mTokenizer->nextLine();
+    }
+    return OK;
+}
+
+} // namespace android
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
new file mode 100755
index 0000000..afb97a1
--- /dev/null
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "input/PropertyMap.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_FILE_SIZE = 256;
+static constexpr int MAX_STR_LEN = 2048;
+static constexpr int MAX_OPERATIONS = 1000;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+        operations = {
+                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+                    propertyMap.getProperties();
+                },
+                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+                    propertyMap.clear();
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    propertyMap.hasProperty(key);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    android::String8 out;
+                    propertyMap.tryGetProperty(key, out);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
+                    TemporaryFile tf;
+                    // Generate file contents
+                    std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
+                    // If we have string contents, dump them into the file.
+                    // Otherwise, just leave it as an empty file.
+                    if (contents.length() > 0) {
+                        const char* bytes = contents.c_str();
+                        android::base::WriteStringToFd(bytes, tf.fd);
+                    }
+                    android::PropertyMap::load(tf.path);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    android::String8 val = android::String8(valStr.c_str());
+                    propertyMap.addProperty(key, val);
+                },
+};
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    android::PropertyMap propertyMap = android::PropertyMap();
+
+    int opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        operations[op](&dataProvider, propertyMap);
+    }
+    return 0;
+}
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..9626d8d
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/native/services/inputflinger"
+    }
+  ]
+}
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index bcf55b0..2c04d42 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -66,7 +66,7 @@
         if (deltaY) {
             mRawPosition.y += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
 
         float vx, vy;
         float scale = mParameters.scale;
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 7c28ac5..a44f0b7 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -193,7 +193,11 @@
     mStrategy->clearPointers(idBits);
 }
 
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
+void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                  const std::vector<VelocityTracker::Position>& positions) {
+    LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
+                        "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+                        idBits.count(), positions.size());
     while (idBits.count() > MAX_POINTERS) {
         idBits.clearLastMarkedBit();
     }
@@ -285,12 +289,12 @@
         pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
     }
 
-    nsecs_t eventTime;
-    Position positions[pointerCount];
+    std::vector<Position> positions;
+    positions.resize(pointerCount);
 
     size_t historySize = event->getHistorySize();
-    for (size_t h = 0; h < historySize; h++) {
-        eventTime = event->getHistoricalEventTime(h);
+    for (size_t h = 0; h <= historySize; h++) {
+        nsecs_t eventTime = event->getHistoricalEventTime(h);
         for (size_t i = 0; i < pointerCount; i++) {
             uint32_t index = pointerIndex[i];
             positions[index].x = event->getHistoricalX(i, h);
@@ -298,14 +302,6 @@
         }
         addMovement(eventTime, idBits, positions);
     }
-
-    eventTime = event->getEventTime();
-    for (size_t i = 0; i < pointerCount; i++) {
-        uint32_t index = pointerIndex[i];
-        positions[index].x = event->getX(i);
-        positions[index].y = event->getY(i);
-    }
-    addMovement(eventTime, idBits, positions);
 }
 
 bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
@@ -346,8 +342,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -419,13 +416,15 @@
  * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
  * http://en.wikipedia.org/wiki/Gram-Schmidt
  */
-static bool solveLeastSquares(const float* x, const float* y,
-        const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
+static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
+                              const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
+    const size_t m = x.size();
 #if DEBUG_STRATEGY
     ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
             vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
             vectorToString(w, m).c_str());
 #endif
+    LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
 
     // Expand the X vector to a matrix A, pre-multiplied by the weights.
     float a[n][m]; // column-major order
@@ -542,7 +541,9 @@
  * the default implementation
  */
 static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
-        const float* x, const float* y, size_t count) {
+        const std::vector<float>& x, const std::vector<float>& y) {
+    const size_t count = x.size();
+    LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
     // Solving y = a*x^2 + b*x + c
     float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
 
@@ -594,11 +595,11 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    float x[HISTORY_SIZE];
-    float y[HISTORY_SIZE];
-    float w[HISTORY_SIZE];
-    float time[HISTORY_SIZE];
-    uint32_t m = 0;
+    std::vector<float> x;
+    std::vector<float> y;
+    std::vector<float> w;
+    std::vector<float> time;
+
     uint32_t index = mIndex;
     const Movement& newestMovement = mMovements[mIndex];
     do {
@@ -613,13 +614,14 @@
         }
 
         const VelocityTracker::Position& position = movement.getPosition(id);
-        x[m] = position.x;
-        y[m] = position.y;
-        w[m] = chooseWeight(index);
-        time[m] = -age * 0.000000001f;
+        x.push_back(position.x);
+        y.push_back(position.y);
+        w.push_back(chooseWeight(index));
+        time.push_back(-age * 0.000000001f);
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (++m < HISTORY_SIZE);
+    } while (x.size() < HISTORY_SIZE);
 
+    const size_t m = x.size();
     if (m == 0) {
         return false; // no data
     }
@@ -632,8 +634,8 @@
 
     if (degree == 2 && mWeighting == WEIGHTING_NONE) {
         // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m);
-        std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m);
+        std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
+        std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
         if (xCoeff && yCoeff) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = 2;
@@ -648,8 +650,8 @@
         // General case for an Nth degree polynomial fit
         float xdet, ydet;
         uint32_t n = degree + 1;
-        if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
-                && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
+        if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
+            solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = degree;
             outEstimator->confidence = xdet * ydet;
@@ -758,8 +760,9 @@
     mPointerIdBits.value &= ~idBits.value;
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     uint32_t index = 0;
     for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
         uint32_t id = iterIdBits.clearFirstMarkedBit();
@@ -876,8 +879,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void LegacyVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
@@ -990,8 +994,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
new file mode 100644
index 0000000..303dd1c
--- /dev/null
+++ b/libs/input/android/FocusRequest.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android;
+
+/** @hide */
+parcelable FocusRequest {
+    /**
+     * Input channel token used to identify the window that should gain focus.
+     */
+    IBinder token;
+    /**
+     * The token that the caller expects currently to be focused. If the
+     * specified token does not match the currently focused window, this request will be dropped.
+     * If the specified focused token matches the currently focused window, the call will succeed.
+     * Set this to "null" if this call should succeed no matter what the currently focused token
+     * is.
+     */
+    @nullable IBinder focusedToken;
+    /**
+     * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus
+     * change. This determines which request gets precedence if there is a focus change request
+     * from another source such as pointer down.
+     */
+    long timestamp;
+    /**
+     * Display id associated with this request.
+     */
+     int displayId;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/input/android/InputApplicationInfo.aidl
similarity index 66%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/input/android/InputApplicationInfo.aidl
index 10cf17c..9336039 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/input/android/InputApplicationInfo.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
+/**
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,10 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+package android;
+
+parcelable InputApplicationInfo {
+    @nullable IBinder token;
+    @utf8InCpp String name;
+    long dispatchingTimeoutMillis;
+}
diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl
new file mode 100644
index 0000000..c2d1112
--- /dev/null
+++ b/libs/input/android/InputChannel.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android;
+
+parcelable InputChannel cpp_header "input/InputTransport.h";
diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl
new file mode 100644
index 0000000..eeaf400
--- /dev/null
+++ b/libs/input/android/InputWindowInfo.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android;
+
+parcelable InputWindowInfo cpp_header "input/InputWindow.h";
diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
new file mode 100644
index 0000000..9504e99
--- /dev/null
+++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/**
+  * Block untrusted touches feature mode.
+  *
+  * @hide
+  */
+@Backing(type="int")
+enum BlockUntrustedTouchesMode {
+    /** Feature is off. */
+    DISABLED,
+
+    /** Untrusted touches are flagged but not blocked. */
+    PERMISSIVE,
+
+    /** Untrusted touches are blocked. */
+    BLOCK
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/input/android/os/IInputConstants.aidl
similarity index 67%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/input/android/os/IInputConstants.aidl
index 10cf17c..82c220f 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
+/**
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,11 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+package android.os;
+
+
+/** @hide */
+interface IInputConstants
+{
+    const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
+}
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
new file mode 100644
index 0000000..1771d19
--- /dev/null
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlinger
+{
+    // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
+    // ordering when needed.
+    // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
+    // shouldn't be a concern.
+    oneway void setInputWindows(in InputWindowInfo[] inputHandles,
+            in @nullable ISetInputWindowsListener setInputWindowsListener);
+    InputChannel createInputChannel(in @utf8InCpp String name);
+    void removeInputChannel(in IBinder connectionToken);
+    /**
+     * Sets focus to the window identified by the token. This must be called
+     * after updating any input window handles.
+     */
+    oneway void setFocusedWindow(in FocusRequest request);
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/input/android/os/ISetInputWindowsListener.aidl
similarity index 68%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/input/android/os/ISetInputWindowsListener.aidl
index 10cf17c..bb58fb6 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/input/android/os/ISetInputWindowsListener.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
+/**
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,10 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+package android.os;
+
+/** @hide */
+oneway interface ISetInputWindowsListener
+{
+    void onSetInputWindowsFinished();
+}
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
new file mode 100644
index 0000000..34f10ec
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Constants used to report the outcome of input event injection.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionResult {
+    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+    PENDING = -1,
+
+    /* Injection succeeded. */
+    SUCCEEDED = 0,
+
+    /* Injection failed because the injector did not have permission to inject
+     * into the application with input focus. */
+    PERMISSION_DENIED = 1,
+
+    /* Injection failed because there were no available input targets. */
+    FAILED = 2,
+
+    /* Injection failed due to a timeout. */
+    TIMED_OUT = 3,
+}
diff --git a/libs/input/android/os/InputEventInjectionSync.aidl b/libs/input/android/os/InputEventInjectionSync.aidl
new file mode 100644
index 0000000..95d24cb
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionSync.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Constants used to specify the input event injection synchronization mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionSync {
+    /* Injection is asynchronous and is assumed always to be successful. */
+    NONE = 0,
+
+    /* Waits for previous events to be dispatched so that the input dispatcher can determine
+     * whether input event injection willbe permitted based on the current input focus.
+     * Does not wait for the input event to finish processing. */
+    WAIT_FOR_RESULT = 1,
+
+    /* Waits for the input event to be completely processed. */
+    WAIT_FOR_FINISHED = 2,
+}
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl
new file mode 100644
index 0000000..106f159
--- /dev/null
+++ b/libs/input/android/os/TouchOcclusionMode.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/**
+  * Touch occlusion modes: These modes represent how windows are taken into
+  * consideration in order to decide whether to block obscured touches or
+  * not.
+  *
+  * @hide
+  */
+@Backing(type="int")
+enum TouchOcclusionMode {
+    /**
+      * Touches that pass through this window will be blocked if they are
+      * consumed by a different UID and this window is not trusted.
+      */
+    BLOCK_UNTRUSTED,
+
+    /**
+      * The window's opacity will be taken into consideration for touch
+      * occlusion rules if the touch passes through it and the window is not
+      * trusted.
+      */
+    USE_OPACITY,
+
+    /**
+      * The window won't count for touch occlusion rules if the touch passes
+      * through it.
+      */
+    ALLOW
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 3b57146..b23aade 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,8 @@
 cc_test {
     name: "libinput_tests",
     srcs: [
+        "NamedEnum_test.cpp",
+        "Flags_test.cpp",
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
         "InputDevice_test.cpp",
@@ -18,14 +20,18 @@
         "-Wextra",
         "-Werror",
     ],
-    shared_libs: [
+    static_libs: [
         "libinput",
-        "libcutils",
-        "libutils",
-        "libbinder",
-        "libui",
+    ],
+    shared_libs: [
         "libbase",
-    ]
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+    test_suites: ["device-tests"],
 }
 
 // NOTE: This is a compile time test, and does not need to be
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
new file mode 100644
index 0000000..0dbb4cf
--- /dev/null
+++ b/libs/input/tests/Flags_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <input/Flags.h>
+
+#include <type_traits>
+
+namespace android::test {
+
+using namespace android::flag_operators;
+
+enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+
+TEST(Flags, Test) {
+    Flags<TestFlags> flags = TestFlags::ONE;
+    ASSERT_TRUE(flags.test(TestFlags::ONE));
+    ASSERT_FALSE(flags.test(TestFlags::TWO));
+    ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
+TEST(Flags, Any) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_TRUE(flags.any(TestFlags::ONE));
+    ASSERT_TRUE(flags.any(TestFlags::TWO));
+    ASSERT_FALSE(flags.any(TestFlags::THREE));
+    ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO));
+    ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
+    ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
+    ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, All) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_TRUE(flags.all(TestFlags::ONE));
+    ASSERT_TRUE(flags.all(TestFlags::TWO));
+    ASSERT_FALSE(flags.all(TestFlags::THREE));
+    ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO));
+    ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE));
+    ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE));
+    ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
+    Flags<TestFlags> flags;
+    ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) {
+    Flags<TestFlags> flags;
+    flags = ~flags;
+    ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) {
+    Flags<TestFlags> flags = TestFlags::TWO;
+    flags = ~flags;
+    ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+    ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withNewFlag) {
+    Flags<TestFlags> flags = TestFlags::ONE;
+    Flags<TestFlags> flags2 = flags | TestFlags::TWO;
+    ASSERT_FALSE(flags2.test(TestFlags::THREE));
+    ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withExistingFlag) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+    Flags<TestFlags> flags2 = flags | TestFlags::THREE;
+    ASSERT_FALSE(flags2.test(TestFlags::TWO));
+    ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE));
+}
+
+TEST(Flags, OrEqualsOperator_withNewFlag) {
+    Flags<TestFlags> flags;
+    flags |= TestFlags::THREE;
+    ASSERT_TRUE(flags.test(TestFlags::THREE));
+    ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrEqualsOperator_withExistingFlag) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+    flags |= TestFlags::THREE;
+    ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+    ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withOneSetFlag) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+    Flags<TestFlags> andFlags = flags & TestFlags::THREE;
+    ASSERT_TRUE(andFlags.test(TestFlags::THREE));
+    ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withMultipleSetFlags) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+    Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE);
+    ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE));
+    ASSERT_FALSE(andFlags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withNoSetFlags) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+    Flags<TestFlags> andFlags = flags & TestFlags::TWO;
+    ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, Equality) {
+    Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+    Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_EQ(flags1, flags2);
+}
+
+TEST(Flags, Inequality) {
+    Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+    Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE;
+    ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, EqualsOperator) {
+    Flags<TestFlags> flags;
+    flags = TestFlags::ONE;
+    ASSERT_TRUE(flags.test(TestFlags::ONE));
+    ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, EqualsOperator_DontShareState) {
+    Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+    Flags<TestFlags> flags2 = flags1;
+    ASSERT_EQ(flags1, flags2);
+
+    flags1 &= TestFlags::TWO;
+    ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, String_NoFlags) {
+    Flags<TestFlags> flags;
+    ASSERT_EQ(flags.string(), "0x0");
+}
+
+TEST(Flags, String_KnownValues) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_EQ(flags.string(), "ONE | TWO");
+}
+
+TEST(Flags, String_UnknownValues) {
+    auto flags = Flags<TestFlags>(0b1011);
+    ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+}
+
+TEST(FlagsIterator, IteratesOverAllFlags) {
+    Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+    Flags<TestFlags> flags2;
+    for (TestFlags f : flags1) {
+        flags2 |= f;
+    }
+    ASSERT_EQ(flags2, flags1);
+}
+
+TEST(FlagsIterator, IteratesInExpectedOrder) {
+    const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO};
+    Flags<TestFlags> flags;
+    for (TestFlags f : flagOrder) {
+        flags |= f;
+    }
+
+    size_t idx = 0;
+    auto iter = flags.begin();
+    while (iter != flags.end() && idx < flagOrder.size()) {
+        // Make sure the order is what we expect
+        ASSERT_EQ(*iter, flagOrder[idx]);
+        iter++;
+        idx++;
+    }
+    ASSERT_EQ(iter, flags.end());
+}
+TEST(FlagsIterator, PostFixIncrement) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    auto iter = flags.begin();
+    ASSERT_EQ(*(iter++), TestFlags::ONE);
+    ASSERT_EQ(*iter, TestFlags::TWO);
+    ASSERT_EQ(*(iter++), TestFlags::TWO);
+    ASSERT_EQ(iter, flags.end());
+}
+
+TEST(FlagsIterator, PreFixIncrement) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    auto iter = flags.begin();
+    ASSERT_EQ(*++iter, TestFlags::TWO);
+    ASSERT_EQ(++iter, flags.end());
+}
+
+TEST(FlagNames, RuntimeFlagName) {
+    TestFlags f = TestFlags::ONE;
+    ASSERT_EQ(flag_name(f), "ONE");
+}
+
+TEST(FlagNames, RuntimeUnknownFlagName) {
+    TestFlags f = static_cast<TestFlags>(0x8);
+    ASSERT_EQ(flag_name(f), std::nullopt);
+}
+
+TEST(FlagNames, CompileTimeFlagName) {
+    static_assert(flag_name<TestFlags::TWO>() == "TWO");
+}
+
+} // namespace android::test
\ No newline at end of file
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index ada275d..0661261 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -23,6 +23,7 @@
 #include <errno.h>
 
 #include <binder/Binder.h>
+#include <binder/Parcel.h>
 #include <gtest/gtest.h>
 #include <input/InputTransport.h>
 #include <utils/StopWatch.h>
@@ -32,9 +33,6 @@
 namespace android {
 
 class InputChannelTest : public testing::Test {
-protected:
-    virtual void SetUp() { }
-    virtual void TearDown() { }
 };
 
 
@@ -46,7 +44,7 @@
 
     android::base::unique_fd sendFd(pipe.sendFd);
 
-    sp<InputChannel> inputChannel =
+    std::unique_ptr<InputChannel> inputChannel =
             InputChannel::create("channel name", std::move(sendFd), new BBinder());
 
     EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
@@ -61,14 +59,14 @@
 TEST_F(InputChannelTest, SetAndGetToken) {
     Pipe pipe;
     sp<IBinder> token = new BBinder();
-    sp<InputChannel> channel =
+    std::unique_ptr<InputChannel> channel =
             InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
 
     EXPECT_EQ(token, channel->getConnectionToken());
 }
 
 TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
-    sp<InputChannel> serverChannel, clientChannel;
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
 
     status_t result = InputChannel::openInputChannelPair("channel name",
             serverChannel, clientChannel);
@@ -102,7 +100,7 @@
     InputMessage clientReply;
     memset(&clientReply, 0, sizeof(InputMessage));
     clientReply.header.type = InputMessage::Type::FINISHED;
-    clientReply.body.finished.seq = 0x11223344;
+    clientReply.header.seq = 0x11223344;
     clientReply.body.finished.handled = true;
     EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
             << "client channel should be able to send message to server channel";
@@ -112,14 +110,14 @@
             << "server channel should be able to receive message from client channel";
     EXPECT_EQ(clientReply.header.type, serverReply.header.type)
             << "server channel should receive the correct message from client channel";
-    EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
+    EXPECT_EQ(clientReply.header.seq, serverReply.header.seq)
             << "server channel should receive the correct message from client channel";
     EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
             << "server channel should receive the correct message from client channel";
 }
 
 TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
 
     status_t result = InputChannel::openInputChannelPair("channel name",
             serverChannel, clientChannel);
@@ -133,7 +131,7 @@
 }
 
 TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
 
     status_t result = InputChannel::openInputChannelPair("channel name",
             serverChannel, clientChannel);
@@ -141,7 +139,7 @@
     ASSERT_EQ(OK, result)
             << "should have successfully opened a channel pair";
 
-    serverChannel.clear(); // close server channel
+    serverChannel.reset(); // close server channel
 
     InputMessage msg;
     EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
@@ -149,7 +147,7 @@
 }
 
 TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
 
     status_t result = InputChannel::openInputChannelPair("channel name",
             serverChannel, clientChannel);
@@ -157,7 +155,7 @@
     ASSERT_EQ(OK, result)
             << "should have successfully opened a channel pair";
 
-    serverChannel.clear(); // close server channel
+    serverChannel.reset(); // close server channel
 
     InputMessage msg;
     msg.header.type = InputMessage::Type::KEY;
@@ -166,7 +164,7 @@
 }
 
 TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
-    sp<InputChannel> serverChannel, clientChannel;
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
     status_t result = InputChannel::openInputChannelPair("channel name",
             serverChannel, clientChannel);
     ASSERT_EQ(OK, result)
@@ -180,7 +178,7 @@
 
     InputMessage serverMsg = {}, clientMsg;
     serverMsg.header.type = InputMessage::Type::MOTION;
-    serverMsg.body.motion.seq = 1;
+    serverMsg.header.seq = 1;
     serverMsg.body.motion.pointerCount = 1;
 
     for (MotionClassification classification : classifications) {
@@ -197,5 +195,36 @@
     }
 }
 
+TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) {
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+    status_t result =
+            InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+    InputChannel chan;
+    Parcel parcel;
+    ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel));
+    parcel.setDataPosition(0);
+    chan.readFromParcel(&parcel);
+
+    EXPECT_EQ(chan == *serverChannel, true)
+            << "inputchannel should be equal after parceling and unparceling.\n"
+            << "name " << chan.getName() << " name " << serverChannel->getName();
+}
+
+TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+    status_t result =
+            InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+    std::unique_ptr<InputChannel> dupChan = serverChannel->dup();
+
+    EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication";
+}
 
 } // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 553dc4c..601d8da 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -17,6 +17,7 @@
 #include <array>
 #include <math.h>
 
+#include <attestation/HmacKeyManager.h>
 #include <binder/Parcel.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
@@ -225,6 +226,7 @@
     static constexpr float Y_OFFSET = 1.1;
 
     int32_t mId;
+    ui::Transform mTransform;
 
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
@@ -233,6 +235,7 @@
 
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     mId = InputEvent::nextId();
+    mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
 
     PointerProperties pointerProperties[2];
     pointerProperties[0].clear();
@@ -266,7 +269,7 @@
     event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
                       AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
                       AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
-                      MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+                      MotionClassification::NONE, mTransform, 2.0f, 2.1f,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                       ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
                       pointerCoords);
@@ -326,8 +329,7 @@
     ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
     ASSERT_EQ(MotionClassification::NONE, event->getClassification());
-    EXPECT_EQ(X_SCALE, event->getXScale());
-    EXPECT_EQ(Y_SCALE, event->getYScale());
+    EXPECT_EQ(mTransform, event->getTransform());
     ASSERT_EQ(X_OFFSET, event->getXOffset());
     ASSERT_EQ(Y_OFFSET, event->getYOffset());
     ASSERT_EQ(2.0f, event->getXPrecision());
@@ -545,7 +547,7 @@
     ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
 }
 
-static void setRotationMatrix(float matrix[9], float angle) {
+static void setRotationMatrix(std::array<float, 9>& matrix, float angle) {
     float sin = sinf(angle);
     float cos = cosf(angle);
     matrix[0] = cos;
@@ -584,13 +586,14 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
     }
     MotionEvent event;
+    ui::Transform identityTransform;
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                     MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
-                     0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
-                     3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
-                     0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+                     MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+                     0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+                     pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -606,7 +609,7 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 
     // Apply a rotation about the origin by ROTATION degrees clockwise.
-    float matrix[9];
+    std::array<float, 9> matrix;
     setRotationMatrix(matrix, ROTATION * PI_180);
     event.transform(matrix);
 
@@ -648,11 +651,12 @@
         pointerCoords[i].clear();
     }
 
+    ui::Transform identityTransform;
     for (MotionClassification classification : classifications) {
         event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
-                         AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/,
-                         1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
+                         identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
                          pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
@@ -670,10 +674,11 @@
         pointerCoords[i].clear();
     }
 
+    ui::Transform identityTransform;
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
-                     AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0,
-                     0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+                     AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
                      0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 8e2eec8..4f53dc9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -20,43 +20,32 @@
 #include <sys/mman.h>
 #include <time.h>
 
+#include <attestation/HmacKeyManager.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 #include <input/InputTransport.h>
-#include <utils/Timers.h>
 #include <utils/StopWatch.h>
+#include <utils/Timers.h>
 
 namespace android {
 
 class InputPublisherAndConsumerTest : public testing::Test {
 protected:
-    sp<InputChannel> serverChannel, clientChannel;
-    InputPublisher* mPublisher;
-    InputConsumer* mConsumer;
+    std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+    std::unique_ptr<InputPublisher> mPublisher;
+    std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
 
-    virtual void SetUp() {
+    void SetUp() override {
+        std::unique_ptr<InputChannel> serverChannel, clientChannel;
         status_t result = InputChannel::openInputChannelPair("channel name",
                 serverChannel, clientChannel);
         ASSERT_EQ(OK, result);
+        mServerChannel = std::move(serverChannel);
+        mClientChannel = std::move(clientChannel);
 
-        mPublisher = new InputPublisher(serverChannel);
-        mConsumer = new InputConsumer(clientChannel);
-    }
-
-    virtual void TearDown() {
-        if (mPublisher) {
-            delete mPublisher;
-            mPublisher = nullptr;
-        }
-
-        if (mConsumer) {
-            delete mConsumer;
-            mConsumer = nullptr;
-        }
-
-        serverChannel.clear();
-        clientChannel.clear();
+        mPublisher = std::make_unique<InputPublisher>(mServerChannel);
+        mConsumer = std::make_unique<InputConsumer>(mClientChannel);
     }
 
     void PublishAndConsumeKeyEvent();
@@ -65,8 +54,12 @@
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
-    EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
-    EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+    ASSERT_NE(nullptr, mPublisher->getChannel());
+    ASSERT_NE(nullptr, mConsumer->getChannel());
+    EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
+    EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
+    ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(),
+              mConsumer->getChannel()->getConnectionToken());
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
@@ -185,12 +178,13 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
     }
 
+    ui::Transform transform;
+    transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
     status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                             actionButton, flags, edgeFlags, metaState, buttonState,
-                                            classification, xScale, yScale, xOffset, yOffset,
-                                            xPrecision, yPrecision, xCursorPosition,
-                                            yCursorPosition, downTime, eventTime, pointerCount,
-                                            pointerProperties, pointerCoords);
+                                            classification, transform, xPrecision, yPrecision,
+                                            xCursorPosition, yCursorPosition, downTime, eventTime,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -218,8 +212,7 @@
     EXPECT_EQ(metaState, motionEvent->getMetaState());
     EXPECT_EQ(buttonState, motionEvent->getButtonState());
     EXPECT_EQ(classification, motionEvent->getClassification());
-    EXPECT_EQ(xScale, motionEvent->getXScale());
-    EXPECT_EQ(yScale, motionEvent->getYScale());
+    EXPECT_EQ(transform, motionEvent->getTransform());
     EXPECT_EQ(xOffset, motionEvent->getXOffset());
     EXPECT_EQ(yOffset, motionEvent->getYOffset());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
@@ -338,10 +331,10 @@
         pointerCoords[i].clear();
     }
 
+    ui::Transform identityTransform;
     status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
-                                            1 /* yScale */, 0, 0, 0, 0,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            0, 0, 0, MotionClassification::NONE, identityTransform,
+                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                             AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
@@ -354,10 +347,10 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
+    ui::Transform identityTransform;
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
-                                            1 /* yScale */, 0, 0, 0, 0,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            0, 0, 0, MotionClassification::NONE, identityTransform,
+                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                             AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
@@ -375,10 +368,10 @@
         pointerCoords[i].clear();
     }
 
+    ui::Transform identityTransform;
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
-                                            1 /* yScale */, 0, 0, 0, 0,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            0, 0, 0, MotionClassification::NONE, identityTransform,
+                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                             AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index d1cb527..c18a17f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -22,17 +22,19 @@
 #include <input/InputWindow.h>
 #include <input/InputTransport.h>
 
+using std::chrono_literals::operator""s;
+
 namespace android {
 namespace test {
 
 TEST(InputWindowInfo, ParcellingWithoutToken) {
-    InputWindowInfo i;
+    InputWindowInfo i, i2;
     i.token = nullptr;
 
     Parcel p;
-    ASSERT_EQ(OK, i.write(p));
+    ASSERT_EQ(OK, i.writeToParcel(&p));
     p.setDataPosition(0);
-    InputWindowInfo i2 = InputWindowInfo::read(p);
+    i2.readFromParcel(&p);
     ASSERT_TRUE(i2.token == nullptr);
 }
 
@@ -42,40 +44,44 @@
     i.token = new BBinder();
     i.id = 1;
     i.name = "Foobar";
-    i.layoutParamsFlags = 7;
-    i.layoutParamsType = 39;
-    i.dispatchingTimeout = 12;
+    i.flags = InputWindowInfo::Flag::SLIPPERY;
+    i.type = InputWindowInfo::Type::INPUT_METHOD;
+    i.dispatchingTimeout = 12s;
     i.frameLeft = 93;
     i.frameTop = 34;
     i.frameRight = 16;
     i.frameBottom = 19;
     i.surfaceInset = 17;
     i.globalScaleFactor = 0.3;
-    i.windowXScale = 0.4;
-    i.windowYScale = 0.5;
+    i.alpha = 0.7;
+    i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
     i.visible = false;
-    i.canReceiveKeys = false;
-    i.hasFocus = false;
+    i.focusable = false;
     i.hasWallpaper = false;
     i.paused = false;
+    i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
     i.ownerPid = 19;
     i.ownerUid = 24;
-    i.inputFeatures = 29;
+    i.packageName = "com.example.package";
+    i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
     i.displayId = 34;
     i.portalToDisplayId = 2;
     i.replaceTouchableRegionWithCrop = true;
     i.touchableRegionCropHandle = touchableRegionCropHandle;
+    i.applicationInfo.name = "ApplicationFooBar";
+    i.applicationInfo.token = new BBinder();
+    i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
 
     Parcel p;
-    i.write(p);
-
+    i.writeToParcel(&p);
     p.setDataPosition(0);
-    InputWindowInfo i2 = InputWindowInfo::read(p);
+    InputWindowInfo i2;
+    i2.readFromParcel(&p);
     ASSERT_EQ(i.token, i2.token);
     ASSERT_EQ(i.id, i2.id);
     ASSERT_EQ(i.name, i2.name);
-    ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
-    ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+    ASSERT_EQ(i.flags, i2.flags);
+    ASSERT_EQ(i.type, i2.type);
     ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
     ASSERT_EQ(i.frameLeft, i2.frameLeft);
     ASSERT_EQ(i.frameTop, i2.frameTop);
@@ -83,20 +89,36 @@
     ASSERT_EQ(i.frameBottom, i2.frameBottom);
     ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
     ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
-    ASSERT_EQ(i.windowXScale, i2.windowXScale);
-    ASSERT_EQ(i.windowYScale, i2.windowYScale);
+    ASSERT_EQ(i.alpha, i2.alpha);
+    ASSERT_EQ(i.transform, i2.transform);
     ASSERT_EQ(i.visible, i2.visible);
-    ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
-    ASSERT_EQ(i.hasFocus, i2.hasFocus);
+    ASSERT_EQ(i.focusable, i2.focusable);
     ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
     ASSERT_EQ(i.paused, i2.paused);
+    ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
     ASSERT_EQ(i.ownerPid, i2.ownerPid);
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
+    ASSERT_EQ(i.packageName, i2.packageName);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
     ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
     ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
+    ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
+}
+
+TEST(InputApplicationInfo, Parcelling) {
+    InputApplicationInfo i;
+    i.token = new BBinder();
+    i.name = "ApplicationFooBar";
+    i.dispatchingTimeoutMillis = 0x12345678ABCD;
+
+    Parcel p;
+    ASSERT_EQ(i.writeToParcel(&p), OK);
+    p.setDataPosition(0);
+    InputApplicationInfo i2;
+    ASSERT_EQ(i2.readFromParcel(&p), OK);
+    ASSERT_EQ(i, i2);
 }
 
 } // namespace test
diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp
new file mode 100644
index 0000000..4e93f71
--- /dev/null
+++ b/libs/input/tests/NamedEnum_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <input/NamedEnum.h>
+
+namespace android {
+
+// Test enum class maximum enum value smaller than default maximum of 8.
+enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
+// Big enum contains enum values greater than default maximum of 8.
+enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
+
+// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
+template <>
+constexpr size_t NamedEnum::max<TestBigEnums> = 16;
+
+namespace test {
+using android::TestBigEnums;
+using android::TestEnums;
+
+TEST(NamedEnum, RuntimeNamedEnum) {
+    TestEnums e = TestEnums::ZERO;
+    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+    e = TestEnums::ONE;
+    ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
+
+    e = TestEnums::THREE;
+    ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
+
+    e = TestEnums::SEVEN;
+    ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
+}
+
+// Test big enum
+TEST(NamedEnum, RuntimeBigNamedEnum) {
+    TestBigEnums e = TestBigEnums::ZERO;
+    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+    e = TestBigEnums::FIFTEEN;
+    ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeNamedEnumAsString) {
+    TestEnums e = TestEnums::ZERO;
+    ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+    e = TestEnums::ONE;
+    ASSERT_EQ(NamedEnum::string(e), "ONE");
+
+    e = TestEnums::THREE;
+    ASSERT_EQ(NamedEnum::string(e), "THREE");
+
+    e = TestEnums::SEVEN;
+    ASSERT_EQ(NamedEnum::string(e), "SEVEN");
+}
+
+TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
+    TestBigEnums e = TestBigEnums::ZERO;
+    ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+    e = TestBigEnums::FIFTEEN;
+    ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnum) {
+    TestEnums e = static_cast<TestEnums>(0x5);
+    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+    e = static_cast<TestEnums>(0x9);
+    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
+    TestEnums e = static_cast<TestEnums>(0x5);
+    ASSERT_EQ(NamedEnum::string(e), "0x00000005");
+    e = static_cast<TestEnums>(0x9);
+    ASSERT_EQ(NamedEnum::string(e), "0x00000009");
+}
+
+TEST(NamedEnum, CompileTimeFlagName) {
+    static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
+    static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
+}
+
+} // namespace test
+
+} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 1fe7bb9..3c5fb22 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -34,8 +34,7 @@
 void TestInputMessageAlignment() {
   CHECK_OFFSET(InputMessage, body, 8);
 
-  CHECK_OFFSET(InputMessage::Body::Key, seq, 0);
-  CHECK_OFFSET(InputMessage::Body::Key, eventId, 4);
+  CHECK_OFFSET(InputMessage::Body::Key, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Key, source, 20);
@@ -49,8 +48,7 @@
   CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
   CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
 
-  CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
-  CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4);
+  CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -64,27 +62,29 @@
   CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
   CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
   CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
-  CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
-  CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
-  CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
-  CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
-  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
-  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
-  CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
-  CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96);
+  CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100);
+  CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104);
+  CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108);
+  CHECK_OFFSET(InputMessage::Body::Motion, tx, 112);
+  CHECK_OFFSET(InputMessage::Body::Motion, ty, 116);
+  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120);
+  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
+  CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
+  CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
 
-  CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
-  CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4);
-  CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12);
-  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14);
+  CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
 
-  CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
 }
 
 void TestHeaderSize() {
+    CHECK_OFFSET(InputMessage::Header, type, 0);
+    CHECK_OFFSET(InputMessage::Header, seq, 4);
     static_assert(sizeof(InputMessage::Header) == 8);
 }
 
@@ -98,7 +98,7 @@
                   offsetof(InputMessage::Body::Motion, pointers) +
                           sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
     static_assert(sizeof(InputMessage::Body::Finished) == 8);
-    static_assert(sizeof(InputMessage::Body::Focus) == 16);
+    static_assert(sizeof(InputMessage::Body::Focus) == 8);
 }
 
 // --- VerifiedInputEvent ---
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 249d9d4..d049d05 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -21,6 +21,7 @@
 #include <math.h>
 
 #include <android-base/stringprintf.h>
+#include <attestation/HmacKeyManager.h>
 #include <gtest/gtest.h>
 #include <input/VelocityTracker.h>
 
@@ -176,12 +177,12 @@
         EXPECT_EQ(pointerIndex, pointerCount);
 
         MotionEvent event;
+        ui::Transform identityTransform;
         event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
                          DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                         MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
-                         0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+                         0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
@@ -239,10 +240,10 @@
     // It is difficult to determine the correct answer here, but at least the direction
     // of the reported velocity should be positive.
     std::vector<MotionEventEntry> motions = {
-        {0ms, {{ 273, NAN}}},
-        {12585us, {{293, NAN}}},
-        {14730us, {{293, NAN}}},
-        {14730us, {{293, NAN}}}, // ACTION_UP
+            {0ms, {{273, 0}}},
+            {12585us, {{293, 0}}},
+            {14730us, {{293, 0}}},
+            {14730us, {{293, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
                             1600);
@@ -251,10 +252,10 @@
 TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
     // Same coordinate is reported 3 times in a row
     std::vector<MotionEventEntry> motions = {
-        { 0ms, {{293, NAN}} },
-        { 6132us, {{293, NAN}} },
-        { 11283us, {{293, NAN}} },
-        { 11283us, {{293, NAN}} }, // ACTION_UP
+            {0ms, {{293, 0}}},
+            {6132us, {{293, 0}}},
+            {11283us, {{293, 0}}},
+            {11283us, {{293, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
     computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
@@ -263,10 +264,7 @@
 TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
     // Fixed velocity at 5 points per 10 milliseconds
     std::vector<MotionEventEntry> motions = {
-        { 0ms, {{0, NAN}} },
-        { 10ms, {{5, NAN}} },
-        { 20ms, {{10, NAN}} },
-        { 20ms, {{10, NAN}} }, // ACTION_UP
+            {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
     computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 4e8e840..36f87b8 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <attestation/HmacKeyManager.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
 
@@ -39,13 +40,14 @@
         pointerCoords[i].clear();
     }
 
+    ui::Transform transform;
+    transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                     MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
-                     5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
-                     540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
-                     pointerProperties, pointerCoords);
+                     MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
+                     200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     return event;
 }
 
diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp
index 9e7e4a2..8399e8c 100644
--- a/libs/nativebase/Android.bp
+++ b/libs/nativebase/Android.bp
@@ -16,6 +16,8 @@
     name: "libnativebase_headers",
     vendor_available: true,
     host_supported: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index e458b2e..9e515cd 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -128,14 +128,19 @@
 
     static Choreographer* getForThread();
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+    int64_t getVsyncId() const;
+    int64_t getFrameDeadline() const;
+
 
 private:
     Choreographer(const Choreographer&) = delete;
 
-    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+                       VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
                                nsecs_t vsyncPeriod) override;
+    void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
 
     void scheduleCallbacks();
 
@@ -145,6 +150,7 @@
     std::vector<RefreshRateCallback> mRefreshRateCallbacks;
 
     nsecs_t mLatestVsyncPeriod = -1;
+    VsyncEventData mLastVsyncEventData;
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
@@ -170,7 +176,7 @@
 
 Choreographer::Choreographer(const sp<Looper>& looper)
       : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
-                               ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
+                               ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
         mLooper(looper),
         mThreadId(std::this_thread::get_id()) {
     std::lock_guard<std::mutex> _l(gChoreographers.lock);
@@ -294,8 +300,14 @@
     } else {
         // If the looper thread is detached from Choreographer, then refresh rate
         // changes will be handled in AChoreographer_handlePendingEvents, so we
-        // need to redispatch a config from SF
-        requestLatestConfig();
+        // need to wake up the looper thread by writing to the write-end of the
+        // socket the looper is listening on.
+        // Fortunately, these events are small so sending packets across the
+        // socket should be atomic across processes.
+        DisplayEventReceiver::Event event;
+        event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+                                                           PhysicalDisplayId(0), systemTime()};
+        injectEvent(event);
     }
 }
 
@@ -343,7 +355,8 @@
 // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
 // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
 // the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+                                  VsyncEventData vsyncEventData) {
     std::vector<FrameCallback> callbacks{};
     {
         std::lock_guard<std::mutex> _l{mLock};
@@ -353,6 +366,7 @@
             mFrameCallbacks.pop();
         }
     }
+    mLastVsyncEventData = vsyncEventData;
     for (const auto& cb : callbacks) {
         if (cb.callback64 != nullptr) {
             cb.callback64(timestamp, cb.data);
@@ -363,9 +377,8 @@
 }
 
 void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
-    ALOGV("choreographer %p ~ received hotplug event (displayId=%"
-            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
-            this, displayId, toString(connected));
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
+            this, to_string(displayId).c_str(), toString(connected));
 }
 
 // TODO(b/74619554): The PhysicalDisplayId is ignored because currently
@@ -374,28 +387,14 @@
 // displays. When multi-display choreographer is properly supported, then
 // PhysicalDisplayId should no longer be ignored.
 void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
-                                          nsecs_t vsyncPeriod) {
-    ALOGV("choreographer %p ~ received config change event "
-          "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
-          this, displayId, configId);
+                                          nsecs_t) {
+    ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).",
+          this, to_string(displayId).c_str(), configId);
+}
 
-    const nsecs_t lastPeriod = mLatestVsyncPeriod;
-    std::vector<RefreshRateCallback> callbacks{};
-    {
-        std::lock_guard<std::mutex> _l{mLock};
-        for (auto& cb : mRefreshRateCallbacks) {
-            callbacks.push_back(cb);
-            cb.firstCallbackFired = true;
-        }
-    }
-
-    for (auto& cb : callbacks) {
-        if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
-            cb.callback(vsyncPeriod, cb.data);
-        }
-    }
-
-    mLatestVsyncPeriod = vsyncPeriod;
+void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
+    ALOGV("choreographer %p ~ received null event.", this);
+    handleRefreshRateUpdates();
 }
 
 void Choreographer::handleMessage(const Message& message) {
@@ -412,6 +411,14 @@
     }
 }
 
+int64_t Choreographer::getVsyncId() const {
+    return mLastVsyncEventData.id;
+}
+
+int64_t Choreographer::getFrameDeadline() const {
+    return mLastVsyncEventData.deadlineTimestamp;
+}
+
 } // namespace android
 using namespace android;
 
@@ -419,6 +426,11 @@
     return reinterpret_cast<Choreographer*>(choreographer);
 }
 
+static inline const Choreographer* AChoreographer_to_Choreographer(
+        const AChoreographer* choreographer) {
+    return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
 // Glue for private C api
 namespace android {
 void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -449,12 +461,18 @@
 }
 void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
                                            AChoreographer_frameCallback callback, void* data) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     return AChoreographer_postFrameCallback(choreographer, callback, data);
+#pragma clang diagnostic pop
 }
 void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
                                                   AChoreographer_frameCallback callback, void* data,
                                                   long delayMillis) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+#pragma clang diagnostic pop
 }
 void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
                                              AChoreographer_frameCallback64 callback, void* data) {
@@ -476,15 +494,18 @@
     return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
 }
 
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
+    return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+}
+
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
+    return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+}
+
 } // namespace android
 
 /* Glue for the NDK interface */
 
-static inline const Choreographer* AChoreographer_to_Choreographer(
-        const AChoreographer* choreographer) {
-    return reinterpret_cast<const Choreographer*>(choreographer);
-}
-
 static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
     return reinterpret_cast<AChoreographer*>(choreographer);
 }
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 2164930..d3a4a66 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,6 +29,19 @@
 // for consumption by callbacks.
 void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
 
+// Returns the vsync id of the last frame callback. Client are expected to call
+// this function from their frame callback function to get the vsyncId and pass
+// it together with a buffer or transaction to the Surface Composer. Calling
+// this function from anywhere else will return an undefined value.
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
+
+// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
+// Client are expected to call this function from their frame callback function
+// to get the deadline and use it to know whether a frame is likely to miss
+// presentation. Calling this function from anywhere else will return an undefined
+// value.
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
+
 // Trampoline functions allowing libandroid.so to define the NDK symbols without including
 // the entirety of libnativedisplay as a whole static lib. As libnativedisplay
 // maintains global state, libnativedisplay can never be directly statically
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index e2d036b..f371667 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -79,6 +79,8 @@
 
 /**
  * ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ * The caller gets ownership of the buffer and need to release it with
+ * AHardwareBuffer_release.
  */
 AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
                                                android_dataspace* outDataspace,
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index fc59431..fda6a20 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -29,6 +29,8 @@
       android::AChoreographer_routeRegisterRefreshRateCallback*;
       android::AChoreographer_routeUnregisterRefreshRateCallback*;
       android::AChoreographer_signalRefreshRateCallbacks*;
+      android::AChoreographer_getVsyncId*;
+      android::AChoreographer_getFrameDeadline*;
       android::ADisplay_acquirePhysicalDisplays*;
       android::ADisplay_release*;
       android::ADisplay_getMaxSupportedFps*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 16afc68..365e788 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -51,7 +51,15 @@
     }
 
     int slot = item.mSlot;
+    *outQueueEmpty = false;
     if (item.mFence->isValid()) {
+        // If fence is not signaled, that means frame is not ready and
+        // outQueueEmpty is set to true. By the time the fence is signaled,
+        // there may be a new buffer queued. This is a proper detection for an
+        // empty queue and it is needed to avoid infinite loop in
+        // ASurfaceTexture_dequeueBuffer (see b/159921224).
+        *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled;
+
         // Wait on the producer fence for the buffer to be ready.
         err = fenceWait(item.mFence->get(), fencePassThroughHandle);
         if (err != OK) {
@@ -112,7 +120,6 @@
     st.mCurrentFrameNumber = item.mFrameNumber;
     st.computeCurrentTransformMatrixLocked();
 
-    *outQueueEmpty = false;
     *outDataspace = item.mDataSpace;
     *outSlotid = slot;
     return st.mSlots[slot].mGraphicBuffer;
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index d1bcd8d..ebe4484 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -208,7 +208,15 @@
             *outNewContent = true;
         }
     } while (buffer.get() && (!queueEmpty));
-    return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+    AHardwareBuffer* result = nullptr;
+    if (buffer.get()) {
+      result = buffer->toAHardwareBuffer();
+      // add a reference to keep the hardware buffer alive, even if
+      // BufferQueueProducer is disconnected. This is needed, because
+      // sp reference is destroyed at the end of this function.
+      AHardwareBuffer_acquire(result);
+    }
+    return result;
 }
 
 } // namespace android
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index ee006aa..07e5d86 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -25,6 +25,8 @@
     name: "libnativewindow_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     min_sdk_version: "29",
 }
 
@@ -38,6 +40,7 @@
 
 cc_library {
     name: "libnativewindow",
+    llndk_stubs: "libnativewindow.llndk",
     export_include_dirs: [
         "include",
         "include-private",
@@ -93,7 +96,7 @@
 }
 
 llndk_library {
-    name: "libnativewindow",
+    name: "libnativewindow.llndk",
     symbol_file: "libnativewindow.map.txt",
     unversioned: true,
     export_include_dirs: ["include"],
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b78fc5d..138e08f 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -255,6 +255,7 @@
     NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
+    NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC        = 48,    /* private */
     // clang-format on
 };
 
@@ -1022,6 +1023,12 @@
                            (int)compatibility);
 }
 
+static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window,
+                                                         int64_t frameTimelineVsyncId) {
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC,
+                           frameTimelineVsyncId);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Candidates for APEX visibility
 // These functions are planned to be made stable for APEX modules, but have not
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3dcb498..cd7f37b 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -31,6 +31,7 @@
         "libui",
         "libutils",
     ],
+    whole_static_libs: ["libskia"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
@@ -71,13 +72,19 @@
     ],
 }
 
+filegroup {
+    name: "librenderengine_skia_sources",
+    srcs: [
+        "skia/SkiaRenderEngine.cpp",
+        "skia/SkiaGLRenderEngine.cpp",
+        "skia/filters/BlurFilter.cpp",
+        "skia/filters/LinearEffect.cpp",
+    ],
+}
+
 cc_library_static {
     name: "librenderengine",
     defaults: ["librenderengine_defaults"],
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
     double_loadable: true,
     clang: true,
     cflags: [
@@ -88,6 +95,7 @@
         ":librenderengine_sources",
         ":librenderengine_gl_sources",
         ":librenderengine_threaded_sources",
+        ":librenderengine_skia_sources",
     ],
     lto: {
         thin: true,
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
index b9cea10..245c9e1 100644
--- a/libs/renderengine/Description.cpp
+++ b/libs/renderengine/Description.cpp
@@ -52,5 +52,10 @@
     return colorMatrix != identity;
 }
 
+bool Description::hasDisplayColorMatrix() const {
+    const mat4 identity;
+    return displayColorMatrix != identity;
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index c00fbba..b44456b 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,2 +1,4 @@
+alecmouri@google.com
+jreck@google.com
 lpy@google.com
 stoza@google.com
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index c3fbb60..3e65d9a 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -18,10 +18,11 @@
 
 #include <cutils/properties.h>
 #include <log/log.h>
-#include <private/gui/SyncFeatures.h>
 #include "gl/GLESRenderEngine.h"
 #include "threaded/RenderEngineThreaded.h"
 
+#include "skia/SkiaGLRenderEngine.h"
+
 namespace android {
 namespace renderengine {
 
@@ -37,12 +38,17 @@
     if (strcmp(prop, "threaded") == 0) {
         renderEngineType = RenderEngineType::THREADED;
     }
+    if (strcmp(prop, "skiagl") == 0) {
+        renderEngineType = RenderEngineType::SKIA_GL;
+    }
 
     switch (renderEngineType) {
         case RenderEngineType::THREADED:
             ALOGD("Threaded RenderEngine with GLES Backend");
             return renderengine::threaded::RenderEngineThreaded::create(
                     [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+        case RenderEngineType::SKIA_GL:
+            return renderengine::skia::SkiaGLRenderEngine::create(args);
         case RenderEngineType::GLES:
         default:
             ALOGD("RenderEngine with GLES Backend");
@@ -52,20 +58,5 @@
 
 RenderEngine::~RenderEngine() = default;
 
-namespace impl {
-
-RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
-
-RenderEngine::~RenderEngine() = default;
-
-bool RenderEngine::useNativeFenceSync() const {
-    return SyncFeatures::getInstance().useNativeFenceSync();
-}
-
-bool RenderEngine::useWaitSync() const {
-    return SyncFeatures::getInstance().useWaitSync();
-}
-
-} // namespace impl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d102696..be83ebc 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -51,8 +51,6 @@
 #include "ProgramCache.h"
 #include "filters/BlurFilter.h"
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
 bool checkGlError(const char* op, int lineNumber) {
     bool errorFound = false;
     GLint error = glGetError();
@@ -116,6 +114,28 @@
 namespace renderengine {
 namespace gl {
 
+class BindNativeBufferAsFramebuffer {
+public:
+    BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer,
+                                  const bool useFramebufferCache)
+          : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+        mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+                                                      useFramebufferCache)
+                ? mEngine.bindFrameBuffer(mFramebuffer)
+                : NO_MEMORY;
+    }
+    ~BindNativeBufferAsFramebuffer() {
+        mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
+        mEngine.unbindFrameBuffer(mFramebuffer);
+    }
+    status_t getStatus() const { return mStatus; }
+
+private:
+    GLESRenderEngine& mEngine;
+    Framebuffer* mFramebuffer;
+    status_t mStatus;
+};
+
 using base::StringAppendF;
 using ui::Dataspace;
 
@@ -208,16 +228,16 @@
         LOG_ALWAYS_FATAL("failed to initialize EGL");
     }
 
-    const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+    const auto eglVersion = eglQueryString(display, EGL_VERSION);
     if (!eglVersion) {
         checkGlError(__FUNCTION__, __LINE__);
-        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+        LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
     }
 
-    const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+    const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
     if (!eglExtensions) {
         checkGlError(__FUNCTION__, __LINE__);
-        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+        LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
     }
 
     GLExtensions& extensions = GLExtensions::getInstance();
@@ -245,22 +265,22 @@
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
 
-    EGLSurface dummy = EGL_NO_SURFACE;
+    EGLSurface stub = EGL_NO_SURFACE;
     if (!extensions.hasSurfacelessContext()) {
-        dummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
-                                             Protection::UNPROTECTED);
-        LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+        stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+                                           Protection::UNPROTECTED);
+        LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
     }
-    EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
-    LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+    EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
+    LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
     extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
                                  glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
 
-    EGLSurface protectedDummy = EGL_NO_SURFACE;
+    EGLSurface protectedStub = EGL_NO_SURFACE;
     if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
-        protectedDummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
-                                                      Protection::PROTECTED);
-        ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+        protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+                                                    Protection::PROTECTED);
+        ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
     }
 
     // now figure out what version of GL did we actually get
@@ -278,8 +298,8 @@
             break;
         case GLES_VERSION_2_0:
         case GLES_VERSION_3_0:
-            engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, dummy,
-                                                        protectedContext, protectedDummy);
+            engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
+                                                        protectedContext, protectedStub);
             break;
     }
 
@@ -334,19 +354,19 @@
 }
 
 GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
-                                   EGLConfig config, EGLContext ctxt, EGLSurface dummy,
-                                   EGLContext protectedContext, EGLSurface protectedDummy)
-      : renderengine::impl::RenderEngine(args),
-        mEGLDisplay(display),
+                                   EGLConfig config, EGLContext ctxt, EGLSurface stub,
+                                   EGLContext protectedContext, EGLSurface protectedStub)
+      : mEGLDisplay(display),
         mEGLConfig(config),
         mEGLContext(ctxt),
-        mDummySurface(dummy),
+        mStubSurface(stub),
         mProtectedEGLContext(protectedContext),
-        mProtectedDummySurface(protectedDummy),
+        mProtectedStubSurface(protectedStub),
         mVpWidth(0),
         mVpHeight(0),
         mFramebufferImageCacheSize(args.imageCacheSize),
-        mUseColorManagement(args.useColorManagement) {
+        mUseColorManagement(args.useColorManagement),
+        mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) {
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
@@ -355,12 +375,12 @@
 
     // Initialize protected EGL Context.
     if (mProtectedEGLContext != EGL_NO_CONTEXT) {
-        EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+        EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
                                             mProtectedEGLContext);
         ALOGE_IF(!success, "can't make protected context current");
         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
         glPixelStorei(GL_PACK_ALIGNMENT, 4);
-        success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+        success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
         LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
     }
 
@@ -409,20 +429,33 @@
     mImageManager = std::make_unique<ImageManager>(this);
     mImageManager->initThread();
     mDrawingBuffer = createFramebuffer();
+    sp<GraphicBuffer> buf =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
+                              GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");
+
+    const status_t err = buf->initCheck();
+    if (err != OK) {
+        ALOGE("Error allocating placeholder buffer: %d", err);
+        return;
+    }
+    mPlaceholderBuffer = buf.get();
+    EGLint attributes[] = {
+            EGL_NONE,
+    };
+    mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                                          mPlaceholderBuffer, attributes);
+    ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
+             eglGetError());
 }
 
 GLESRenderEngine::~GLESRenderEngine() {
     // Destroy the image manager first.
     mImageManager = nullptr;
+    cleanFramebufferCache();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     unbindFrameBuffer(mDrawingBuffer.get());
     mDrawingBuffer = nullptr;
-    while (!mFramebufferImageCache.empty()) {
-        EGLImageKHR expired = mFramebufferImageCache.front().second;
-        mFramebufferImageCache.pop_front();
-        eglDestroyImageKHR(mEGLDisplay, expired);
-        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
-    }
+    eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
     mImageCache.clear();
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mEGLDisplay);
@@ -442,8 +475,7 @@
 
 void GLESRenderEngine::primeCache() const {
     ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
-                                           mArgs.useColorManagement,
-                                           mArgs.precacheToneMapperShaderOnly);
+                                           mUseColorManagement, mPrecacheToneMapperShaderOnly);
 }
 
 base::unique_fd GLESRenderEngine::flush() {
@@ -589,6 +621,9 @@
 }
 
 void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+    for (int i = 0; i < count; ++i) {
+        mTextureView.erase(names[i]);
+    }
     glDeleteTextures(count, names);
 }
 
@@ -603,13 +638,8 @@
     }
 }
 
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
-                                                     const sp<GraphicBuffer>& buffer,
-                                                     const sp<Fence>& bufferFence) {
-    if (buffer == nullptr) {
-        return BAD_VALUE;
-    }
-
+void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+                                                 const sp<Fence>& bufferFence) {
     ATRACE_CALL();
 
     bool found = false;
@@ -625,7 +655,8 @@
     if (!found) {
         status_t cacheResult = mImageManager->cache(buffer);
         if (cacheResult != NO_ERROR) {
-            return cacheResult;
+            ALOGE("Error with caching buffer: %d", cacheResult);
+            return;
         }
     }
 
@@ -642,10 +673,11 @@
             // We failed creating the image if we got here, so bail out.
             ALOGE("Failed to create an EGLImage when rendering");
             bindExternalTextureImage(texName, *createImage());
-            return NO_INIT;
+            return;
         }
 
         bindExternalTextureImage(texName, *cachedImage->second);
+        mTextureView.insert_or_assign(texName, buffer->getId());
     }
 
     // Wait for the new buffer to be ready.
@@ -654,22 +686,22 @@
             base::unique_fd fenceFd(bufferFence->dup());
             if (fenceFd == -1) {
                 ALOGE("error dup'ing fence fd: %d", errno);
-                return -errno;
+                return;
             }
             if (!waitFence(std::move(fenceFd))) {
                 ALOGE("failed to wait on fence fd");
-                return UNKNOWN_ERROR;
+                return;
             }
         } else {
             status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
             if (err != NO_ERROR) {
                 ALOGE("error waiting for fence: %d", err);
-                return err;
+                return;
             }
         }
     }
 
-    return NO_ERROR;
+    return;
 }
 
 void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
@@ -703,9 +735,9 @@
     bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
                                                    buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
     if (!created) {
-        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
-              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
-              buffer->getPixelFormat());
+        ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+              buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
+              buffer->getUsage(), buffer->getPixelFormat());
         return NO_INIT;
     }
 
@@ -887,7 +919,7 @@
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
     ATRACE_CALL();
 
     if (mPriorResourcesCleaned ||
@@ -896,9 +928,34 @@
         return false;
     }
 
-    // Bind the texture to dummy data so that backing image data can be freed.
+    // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
+    // not need to keep memory around if we don't need to do so.
+    if (mode == CleanupMode::CLEAN_ALL) {
+        // TODO: SurfaceFlinger memory utilization may benefit from resetting
+        // texture bindings as well. Assess if it does and there's no performance regression
+        // when rebinding the same image data to the same texture, and if so then its mode
+        // behavior can be tweaked.
+        if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+            for (auto [textureName, bufferId] : mTextureView) {
+                if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+                    glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+                    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+                                                 static_cast<GLeglImageOES>(mPlaceholderImage));
+                    mTextureView[textureName] = std::nullopt;
+                    checkErrors();
+                }
+            }
+        }
+        {
+            std::lock_guard<std::mutex> lock(mRenderingMutex);
+            mImageCache.clear();
+        }
+    }
+
+    // Bind the texture to placeholder so that backing image data can be freed.
     GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
     glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
     // Release the cached fence here, so that we don't churn reallocations when
     // we could no-op repeated calls of this method instead.
     mLastDrawFence = nullptr;
@@ -906,6 +963,20 @@
     return true;
 }
 
+void GLESRenderEngine::cleanFramebufferCache() {
+    std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+    // Bind the texture to placeholder so that backing image data can be freed.
+    GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+    glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
+    while (!mFramebufferImageCache.empty()) {
+        EGLImageKHR expired = mFramebufferImageCache.front().second;
+        mFramebufferImageCache.pop_front();
+        eglDestroyImageKHR(mEGLDisplay, expired);
+        DEBUG_EGL_IMAGE_TRACKER_DESTROY();
+    }
+}
+
 void GLESRenderEngine::checkErrors() const {
     checkErrors(nullptr);
 }
@@ -934,7 +1005,7 @@
     if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
         return false;
     }
-    const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+    const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
     const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
     const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
     if (success) {
@@ -1050,6 +1121,7 @@
 
     setOutputDataSpace(display.outputDataspace);
     setDisplayMaxLuminance(display.maxLuminance);
+    setDisplayColorTransform(display.colorTransform);
 
     const mat4 projectionMatrix =
             ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
@@ -1118,7 +1190,7 @@
         position[3] = vec2(bounds.right, bounds.top);
 
         setupLayerCropping(*layer, mesh);
-        setColorTransform(display.colorTransform * layer->colorTransform);
+        setColorTransform(layer->colorTransform);
 
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
@@ -1147,6 +1219,11 @@
             texCoords[2] = vec2(1.0, 1.0);
             texCoords[3] = vec2(1.0, 0.0);
             setupLayerTexturing(texture);
+
+            // Do not cache protected EGLImage, protected memory is limited.
+            if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
+                unbindExternalTextureBuffer(gBuf->getId());
+            }
         }
 
         const half3 solidColor = layer->source.solidColor;
@@ -1232,7 +1309,8 @@
 
     if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
         glEnable(GL_BLEND);
-        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+                            GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
     } else {
         glDisable(GL_BLEND);
     }
@@ -1274,6 +1352,10 @@
     mState.colorMatrix = colorTransform;
 }
 
+void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
+    mState.displayColorMatrix = colorTransform;
+}
+
 void GLESRenderEngine::disableTexturing() {
     mState.textureEnabled = false;
 }
@@ -1566,11 +1648,11 @@
     return context;
 }
 
-EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
-                                                          int hwcFormat, Protection protection) {
-    EGLConfig dummyConfig = config;
-    if (dummyConfig == EGL_NO_CONFIG) {
-        dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                         int hwcFormat, Protection protection) {
+    EGLConfig stubConfig = config;
+    if (stubConfig == EGL_NO_CONFIG) {
+        stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
     }
     std::vector<EGLint> attributes;
     attributes.reserve(7);
@@ -1584,7 +1666,7 @@
     }
     attributes.push_back(EGL_NONE);
 
-    return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+    return eglCreatePbufferSurface(display, stubConfig, attributes.data());
 }
 
 bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
@@ -1620,6 +1702,16 @@
     return cachedImage != mImageCache.end();
 }
 
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+    const auto& entry = mTextureView.find(texName);
+    return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+    const auto& entry = mTextureView.find(texName);
+    return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
 bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
     std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
     return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 9ab5ee6..c0449a1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -48,25 +48,20 @@
 class GLImage;
 class BlurFilter;
 
-class GLESRenderEngine : public impl::RenderEngine {
+class GLESRenderEngine : public RenderEngine {
 public:
     static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
 
     GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
-                     EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
-                     EGLSurface protectedDummy);
+                     EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
+                     EGLSurface protectedStub);
     ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
     void primeCache() const override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
-    void bindExternalTextureImage(uint32_t texName, const Image& image) override;
-    status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
-                                       const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
     void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
-    status_t bindFrameBuffer(Framebuffer* framebuffer) override;
-    void unbindFrameBuffer(Framebuffer* framebuffer) override;
 
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
@@ -75,7 +70,7 @@
                         const std::vector<const LayerSettings*>& layers,
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
-    bool cleanupPostRender() override;
+    bool cleanupPostRender(CleanupMode mode) override;
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -86,6 +81,12 @@
     // Test-only methods
     // Returns true iff mImageCache contains an image keyed by bufferId
     bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+    // Returns true iff texName was previously generated by RenderEngine and was
+    // not destroyed.
+    bool isTextureNameKnownForTesting(uint32_t texName);
+    // Returns the buffer ID of the content bound to texName, or nullopt if no
+    // such mapping exists.
+    std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
     // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
     bool isFramebufferImageCachedForTesting(uint64_t bufferId)
             EXCLUDES(mFramebufferImageCacheMutex);
@@ -96,13 +97,15 @@
     std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
 
 protected:
-    Framebuffer* getFramebufferForDrawing() override;
+    Framebuffer* getFramebufferForDrawing();
     void dump(std::string& result) override EXCLUDES(mRenderingMutex)
             EXCLUDES(mFramebufferImageCacheMutex);
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
 
 private:
+    friend class BindNativeBufferAsFramebuffer;
+
     enum GlesVersion {
         GLES_VERSION_1_0 = 0x10000,
         GLES_VERSION_1_1 = 0x10001,
@@ -115,8 +118,8 @@
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
                                        EGLContext shareContext, bool useContextPriority,
                                        Protection protection);
-    static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
-                                                   int hwcFormat, Protection protection);
+    static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                  int hwcFormat, Protection protection);
     std::unique_ptr<Framebuffer> createFramebuffer();
     std::unique_ptr<Image> createImage();
     void checkErrors() const;
@@ -127,6 +130,12 @@
     status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
             EXCLUDES(mRenderingMutex);
     void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+    status_t bindFrameBuffer(Framebuffer* framebuffer);
+    void unbindFrameBuffer(Framebuffer* framebuffer);
+    void bindExternalTextureImage(uint32_t texName, const Image& image);
+    void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+                                   const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+    void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
 
     // A data space is considered HDR data space if it has BT2020 color space
     // with PQ or HLG transfer function.
@@ -159,6 +168,7 @@
     void setupLayerTexturing(const Texture& texture);
     void setupFillWithColor(float r, float g, float b, float a);
     void setColorTransform(const mat4& colorTransform);
+    void setDisplayColorTransform(const mat4& colorTransform);
     void disableTexturing();
     void disableBlending();
     void setupCornerRadiusCropSize(float width, float height);
@@ -175,9 +185,9 @@
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
     EGLContext mEGLContext;
-    EGLSurface mDummySurface;
+    EGLSurface mStubSurface;
     EGLContext mProtectedEGLContext;
-    EGLSurface mProtectedDummySurface;
+    EGLSurface mProtectedStubSurface;
     GLint mMaxViewportDims[2];
     GLint mMaxTextureSize;
     GLuint mVpWidth;
@@ -222,8 +232,14 @@
     // supports sRGB, DisplayP3 color spaces.
     const bool mUseColorManagement = false;
 
+    // Whether only shaders performing tone mapping from HDR to SDR will be generated on
+    // primeCache().
+    const bool mPrecacheToneMapperShaderOnly = false;
+
     // Cache of GL images that we'll store per GraphicBuffer ID
     std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+    std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
+
     // Mutex guarding rendering operations, so that:
     // 1. GL operations aren't interleaved, and
     // 2. Internal state related to rendering that is potentially modified by
@@ -237,6 +253,11 @@
     // ensure that we align on a word. Allocating 16 bytes will provide a
     // guarantee that we don't clobber memory.
     uint32_t mPlaceholderDrawBuffer[4];
+    // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
+    // instead these are intended for cleaning up texture memory with the
+    // GL_TEXTURE_EXTERNAL_OES target.
+    ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
+    EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
     sp<Fence> mLastDrawFence;
     // Store a separate boolean checking if prior resources were cleaned up, as
     // devices that don't support native sync fences can't rely on a last draw
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index f4fbf35..a172c56 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -66,6 +66,7 @@
         mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
         mSamplerLoc = glGetUniformLocation(programId, "sampler");
         mColorLoc = glGetUniformLocation(programId, "color");
+        mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
         mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
         mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
         mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
@@ -129,6 +130,9 @@
         const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
         glUniform4fv(mColorLoc, 1, color);
     }
+    if (mDisplayColorMatrixLoc >= 0) {
+        glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
+    }
     if (mInputTransformMatrixLoc >= 0) {
         mat4 inputTransformMatrix = desc.inputTransformMatrix;
         glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index fc3755e..4292645 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -104,6 +104,7 @@
     /* location of transform matrix */
     GLint mInputTransformMatrixLoc;
     GLint mOutputTransformMatrixLoc;
+    GLint mDisplayColorMatrixLoc;
 
     /* location of corner radius uniform */
     GLint mCornerRadiusLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 3ae35ec..7fc0499 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -180,6 +180,9 @@
                  description.hasOutputTransformMatrix() || description.hasColorMatrix()
                          ? Key::OUTPUT_TRANSFORM_MATRIX_ON
                          : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+            .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
+                 description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+                                                     : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
             .set(Key::ROUNDED_CORNERS_MASK,
                  description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
             .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -661,7 +664,8 @@
             )__SHADER__";
     }
 
-    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+        needs.hasDisplayColorMatrix()) {
         if (needs.needsToneMapping()) {
             fs << "uniform float displayMaxLuminance;";
             fs << "uniform float maxMasteringLuminance;";
@@ -700,6 +704,21 @@
             )__SHADER__";
         }
 
+        if (needs.hasDisplayColorMatrix()) {
+            fs << "uniform mat4 displayColorMatrix;";
+            fs << R"__SHADER__(
+                highp vec3 DisplayColorMatrix(const highp vec3 color) {
+                    return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
+                }
+            )__SHADER__";
+        } else {
+            fs << R"__SHADER__(
+                highp vec3 DisplayColorMatrix(const highp vec3 color) {
+                    return color;
+                }
+            )__SHADER__";
+        }
+
         generateEOTF(fs, needs);
         generateOOTF(fs, needs);
         generateOETF(fs, needs);
@@ -732,14 +751,17 @@
         }
     }
 
-    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+        needs.hasDisplayColorMatrix()) {
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // un-premultiply if needed before linearization
             // avoid divide by 0 by adding 0.5/256 to the alpha channel
             fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
         }
         fs << "gl_FragColor.rgb = "
-              "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+              "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
+              ")));";
+
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // and re-premultiply if needed after gamma correction
             fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 901e631..37bb651 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -117,6 +117,11 @@
             SHADOW_MASK = 1 << SHADOW_SHIFT,
             SHADOW_OFF = 0 << SHADOW_SHIFT,
             SHADOW_ON = 1 << SHADOW_SHIFT,
+
+            DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
+            DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+            DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+            DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
         };
 
         inline Key() : mKey(0) {}
@@ -143,6 +148,10 @@
         inline bool hasOutputTransformMatrix() const {
             return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
         }
+        inline bool hasDisplayColorMatrix() const {
+            return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
+                    DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+        }
         inline bool hasTransformMatrix() const {
             return hasInputTransformMatrix() || hasOutputTransformMatrix();
         }
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index ca16d2c..a637796 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -47,8 +47,8 @@
     // DataSpace::UNKNOWN otherwise.
     ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
 
-    // Additional color transform to apply in linear space after transforming
-    // to the output dataspace.
+    // Additional color transform to apply after transforming to the output
+    // dataspace, in non-linear space.
     mat4 colorTransform = mat4();
 
     // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 95e9367..d8d989e 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -21,6 +21,7 @@
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/Texture.h>
+#include <ui/BlurRegion.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
@@ -151,6 +152,8 @@
     ShadowSettings shadow;
 
     int backgroundBlurRadius = 0;
+
+    std::vector<BlurRegion> blurRegions;
 };
 
 // Keep in sync with custom comparison function in
@@ -182,7 +185,29 @@
             lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
 }
 
+static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
+            lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
+            lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
+            lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
+            lhs.bottom == rhs.bottom;
+}
+
+static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return !(lhs == rhs);
+}
+
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+    if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
+        return false;
+    }
+    const auto size = lhs.blurRegions.size();
+    for (size_t i = 0; i < size; i++) {
+        if (lhs.blurRegions[i] != rhs.blurRegions[i]) {
+            return false;
+        }
+    }
+
     return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index b137023..11b8e44 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -44,7 +44,6 @@
 
 namespace renderengine {
 
-class BindNativeBufferAsFramebuffer;
 class Image;
 class Mesh;
 class Texture;
@@ -74,6 +73,7 @@
     enum class RenderEngineType {
         GLES = 1,
         THREADED = 2,
+        SKIA_GL = 3,
     };
 
     static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
@@ -89,16 +89,8 @@
     // dump the extension strings. always call the base class.
     virtual void dump(std::string& result) = 0;
 
-    virtual bool useNativeFenceSync() const = 0;
-    virtual bool useWaitSync() const = 0;
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
-    virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
-    // Legacy public method used by devices that don't support native fence
-    // synchronization in their GPU driver, as this method provides implicit
-    // synchronization for latching buffers.
-    virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
-                                               const sp<Fence>& fence) = 0;
     // Caches Image resources for this buffer, but does not bind the buffer to
     // a particular texture.
     // Note that work is deferred to an additional thread, i.e. this call
@@ -116,18 +108,25 @@
     // a buffer should never occur before binding the buffer if the caller
     // called {bind, cache}ExternalTextureBuffer before calling unbind.
     virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
-    // When binding a native buffer, it must be done before setViewportAndProjection
-    // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
-    virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
-    virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+    enum class CleanupMode {
+        CLEAN_OUTPUT_RESOURCES,
+        CLEAN_ALL,
+    };
     // Clean-up method that should be called on the main thread after the
     // drawFence returned by drawLayers fires. This method will free up
     // resources used by the most recently drawn frame. If the frame is still
     // being drawn, then this call is silently ignored.
     //
+    // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
+    // output framebuffer are cleaned up, including the sibling texture.
+    //
+    // If mode is CLEAN_ALL, then we also cleanup resources related to any input
+    // buffers.
+    //
     // Returns true if resources were cleaned up, and false if we didn't need to
     // do any work.
-    virtual bool cleanupPostRender() = 0;
+    virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
 
     // queries
     virtual size_t getMaxTextureSize() const = 0;
@@ -174,15 +173,9 @@
                                 const std::vector<const LayerSettings*>& layers,
                                 const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                                 base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+    virtual void cleanFramebufferCache() = 0;
 
 protected:
-    // Gets a framebuffer to render to. This framebuffer may or may not be
-    // cached depending on the implementation.
-    //
-    // Note that this method does not transfer ownership, so the caller most not
-    // live longer than RenderEngine.
-    virtual Framebuffer* getFramebufferForDrawing() = 0;
-    friend class BindNativeBufferAsFramebuffer;
     friend class threaded::RenderEngineThreaded;
 };
 
@@ -269,44 +262,6 @@
     RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
 };
 
-class BindNativeBufferAsFramebuffer {
-public:
-    BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
-                                  const bool useFramebufferCache)
-          : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
-        mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
-                                                      useFramebufferCache)
-                ? mEngine.bindFrameBuffer(mFramebuffer)
-                : NO_MEMORY;
-    }
-    ~BindNativeBufferAsFramebuffer() {
-        mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
-        mEngine.unbindFrameBuffer(mFramebuffer);
-    }
-    status_t getStatus() const { return mStatus; }
-
-private:
-    RenderEngine& mEngine;
-    Framebuffer* mFramebuffer;
-    status_t mStatus;
-};
-
-namespace impl {
-
-// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
-class RenderEngine : public renderengine::RenderEngine {
-public:
-    virtual ~RenderEngine() = 0;
-
-    bool useNativeFenceSync() const override;
-    bool useWaitSync() const override;
-
-protected:
-    RenderEngine(const RenderEngineCreationArgs& args);
-    const RenderEngineCreationArgs mArgs;
-};
-
-} // namespace impl
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index d0343ba..95ee925 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,32 +35,24 @@
     RenderEngine();
     ~RenderEngine() override;
 
-    MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
     MOCK_CONST_METHOD0(primeCache, void());
     MOCK_METHOD1(dump, void(std::string&));
-    MOCK_CONST_METHOD0(useNativeFenceSync, bool());
-    MOCK_CONST_METHOD0(useWaitSync, bool());
-    MOCK_CONST_METHOD0(isCurrent, bool());
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
-    MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
     MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
-    MOCK_METHOD3(bindExternalTextureBuffer,
-                 status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
     MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
-    MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
-    MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
     MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
     MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
     MOCK_CONST_METHOD0(isProtected, bool());
     MOCK_CONST_METHOD0(supportsProtectedContent, bool());
     MOCK_METHOD1(useProtectedContext, bool(bool));
-    MOCK_METHOD0(cleanupPostRender, bool());
+    MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
     MOCK_METHOD6(drawLayers,
                  status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
                           const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                           base::unique_fd*));
+    MOCK_METHOD0(cleanFramebufferCache, void());
 };
 
 } // namespace mock
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index a62161a..fa6ec10 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -44,6 +44,7 @@
     bool hasInputTransformMatrix() const;
     bool hasOutputTransformMatrix() const;
     bool hasColorMatrix() const;
+    bool hasDisplayColorMatrix() const;
 
     // whether textures are premultiplied
     bool isPremultipliedAlpha = false;
@@ -79,6 +80,8 @@
 
     // The color matrix will be applied in linear space right before OETF.
     mat4 colorMatrix;
+    // The display color matrix will be applied in gamma space after OETF
+    mat4 displayColorMatrix;
     mat4 inputTransformMatrix;
     mat4 outputTransformMatrix;
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
new file mode 100644
index 0000000..1f25fbf
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -0,0 +1,886 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#include <cstdint>
+
+#include "SkImageInfo.h"
+#include "system/graphics-base-v1.0.h"
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+
+#include <GrContextOptions.h>
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
+#include <SkColorSpace.h>
+#include <SkImage.h>
+#include <SkImageFilters.h>
+#include <SkShadowUtils.h>
+#include <SkSurface.h>
+#include <gl/GrGLInterface.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+bool checkGlError(const char* op, int lineNumber);
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+                                         EGLint wanted, EGLConfig* outConfig) {
+    EGLint numConfigs = -1, n = 0;
+    eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+    std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+    eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+    configs.resize(n);
+
+    if (!configs.empty()) {
+        if (attribute != EGL_NONE) {
+            for (EGLConfig config : configs) {
+                EGLint value = 0;
+                eglGetConfigAttrib(dpy, config, attribute, &value);
+                if (wanted == value) {
+                    *outConfig = config;
+                    return NO_ERROR;
+                }
+            }
+        } else {
+            // just pick the first one
+            *outConfig = configs[0];
+            return NO_ERROR;
+        }
+    }
+
+    return NAME_NOT_FOUND;
+}
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+                                EGLConfig* config) {
+    // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+    // it is to be used with WIFI displays
+    status_t err;
+    EGLint wantedAttribute;
+    EGLint wantedAttributeValue;
+
+    std::vector<EGLint> attribs;
+    if (renderableType) {
+        const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
+        const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
+
+        // Default to 8 bits per channel.
+        const EGLint tmpAttribs[] = {
+                EGL_RENDERABLE_TYPE,
+                renderableType,
+                EGL_RECORDABLE_ANDROID,
+                EGL_TRUE,
+                EGL_SURFACE_TYPE,
+                EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+                EGL_FRAMEBUFFER_TARGET_ANDROID,
+                EGL_TRUE,
+                EGL_RED_SIZE,
+                is1010102 ? 10 : 8,
+                EGL_GREEN_SIZE,
+                is1010102 ? 10 : 8,
+                EGL_BLUE_SIZE,
+                is1010102 ? 10 : 8,
+                EGL_ALPHA_SIZE,
+                is1010102 ? 2 : 8,
+                EGL_NONE,
+        };
+        std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
+                  std::back_inserter(attribs));
+        wantedAttribute = EGL_NONE;
+        wantedAttributeValue = EGL_NONE;
+    } else {
+        // if no renderable type specified, fallback to a simplified query
+        wantedAttribute = EGL_NATIVE_VISUAL_ID;
+        wantedAttributeValue = format;
+    }
+
+    err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
+                                   config);
+    if (err == NO_ERROR) {
+        EGLint caveat;
+        if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+            ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+    }
+
+    return err;
+}
+
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+static sk_sp<SkColorSpace> toColorSpace(ui::Dataspace dataspace) {
+    skcms_Matrix3x3 gamut;
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            gamut = SkNamedGamut::kSRGB;
+            break;
+        case HAL_DATASPACE_STANDARD_BT2020:
+            gamut = SkNamedGamut::kRec2020;
+            break;
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            gamut = SkNamedGamut::kDisplayP3;
+            break;
+        default:
+            ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
+            gamut = SkNamedGamut::kSRGB;
+            break;
+    }
+
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+        case HAL_DATASPACE_TRANSFER_SRGB:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+        case HAL_DATASPACE_TRANSFER_HLG:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+        default:
+            ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+    }
+}
+
+std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
+        const RenderEngineCreationArgs& args) {
+    // initialize EGL for the default display
+    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (!eglInitialize(display, nullptr, nullptr)) {
+        LOG_ALWAYS_FATAL("failed to initialize EGL");
+    }
+
+    const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+    if (!eglVersion) {
+        checkGlError(__FUNCTION__, __LINE__);
+        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+    }
+
+    const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+    if (!eglExtensions) {
+        checkGlError(__FUNCTION__, __LINE__);
+        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+    }
+
+    auto& extensions = gl::GLExtensions::getInstance();
+    extensions.initWithEGLStrings(eglVersion, eglExtensions);
+
+    // The code assumes that ES2 or later is available if this extension is
+    // supported.
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    if (!extensions.hasNoConfigContext()) {
+        config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
+    }
+
+    bool useContextPriority =
+            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+    EGLContext protectedContext = EGL_NO_CONTEXT;
+    if (args.enableProtectedContext && extensions.hasProtectedContent()) {
+        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+                                            Protection::PROTECTED);
+        ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+    }
+
+    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+                                       Protection::UNPROTECTED);
+
+    // if can't create a GL context, we can only abort.
+    LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+    EGLSurface placeholder = EGL_NO_SURFACE;
+    if (!extensions.hasSurfacelessContext()) {
+        placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+                                                         Protection::UNPROTECTED);
+        LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
+    }
+    EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt);
+    LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current");
+    extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+                                 glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+    EGLSurface protectedPlaceholder = EGL_NO_SURFACE;
+    if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+        protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+                                                                  Protection::PROTECTED);
+        ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE,
+                 "can't create protected placeholder pbuffer");
+    }
+
+    // initialize the renderer while GL is current
+    std::unique_ptr<SkiaGLRenderEngine> engine =
+            std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
+                                                 protectedContext, protectedPlaceholder);
+
+    ALOGI("OpenGL ES informations:");
+    ALOGI("vendor    : %s", extensions.getVendor());
+    ALOGI("renderer  : %s", extensions.getRenderer());
+    ALOGI("version   : %s", extensions.getVersion());
+    ALOGI("extensions: %s", extensions.getExtensions());
+    ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+    ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+    return engine;
+}
+
+EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+    status_t err;
+    EGLConfig config;
+
+    // First try to get an ES3 config
+    err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+    if (err != NO_ERROR) {
+        // If ES3 fails, try to get an ES2 config
+        err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+        if (err != NO_ERROR) {
+            // If ES2 still doesn't work, probably because we're on the emulator.
+            // try a simplified query
+            ALOGW("no suitable EGLConfig found, trying a simpler query");
+            err = selectEGLConfig(display, format, 0, &config);
+            if (err != NO_ERROR) {
+                // this EGL is too lame for android
+                LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+            }
+        }
+    }
+
+    if (logConfig) {
+        // print some debugging info
+        EGLint r, g, b, a;
+        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+        eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+        ALOGI("EGL information:");
+        ALOGI("vendor    : %s", eglQueryString(display, EGL_VENDOR));
+        ALOGI("version   : %s", eglQueryString(display, EGL_VERSION));
+        ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+        ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+        ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+    }
+
+    return config;
+}
+
+SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+                                       EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+                                       EGLContext protectedContext, EGLSurface protectedPlaceholder)
+      : mEGLDisplay(display),
+        mEGLConfig(config),
+        mEGLContext(ctxt),
+        mPlaceholderSurface(placeholder),
+        mProtectedEGLContext(protectedContext),
+        mProtectedPlaceholderSurface(protectedPlaceholder),
+        mUseColorManagement(args.useColorManagement) {
+    // Suppress unused field warnings for things we definitely will need/use
+    // These EGL fields will all be needed for toggling between protected & unprotected contexts
+    // Or we need different RE instances for that
+    (void)mEGLDisplay;
+    (void)mEGLConfig;
+    (void)mEGLContext;
+    (void)mPlaceholderSurface;
+    (void)mProtectedEGLContext;
+    (void)mProtectedPlaceholderSurface;
+
+    sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+    LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+    GrContextOptions options;
+    options.fPreferExternalImagesOverES3 = true;
+    options.fDisableDistanceFieldPaths = true;
+    mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+
+    if (args.supportsBackgroundBlur) {
+        mBlurFilter = new BlurFilter();
+    }
+}
+
+base::unique_fd SkiaGLRenderEngine::flush() {
+    ATRACE_CALL();
+    if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+        return base::unique_fd();
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+        return base::unique_fd();
+    }
+
+    // native fence fd will not be populated until flush() is done.
+    glFlush();
+
+    // get the fence fd
+    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+    }
+
+    return fenceFd;
+}
+
+bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
+    if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
+        !gl::GLExtensions::getInstance().hasWaitSync()) {
+        return false;
+    }
+
+    // release the fd and transfer the ownership to EGLSync
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    // XXX: The spec draft is inconsistent as to whether this should return an
+    // EGLint or void.  Ignore the return value for now, as it's not strictly
+    // needed.
+    eglWaitSyncKHR(mEGLDisplay, sync, 0);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (error != EGL_SUCCESS) {
+        ALOGE("failed to wait for EGL native fence sync: %#x", error);
+        return false;
+    }
+
+    return true;
+}
+
+static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
+    return !!(desc.usage & usage);
+}
+
+static float toDegrees(uint32_t transform) {
+    switch (transform) {
+        case ui::Transform::ROT_90:
+            return 90.0;
+        case ui::Transform::ROT_180:
+            return 180.0;
+        case ui::Transform::ROT_270:
+            return 270.0;
+        default:
+            return 0.0;
+    }
+}
+
+static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
+    return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+                         matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+                         matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+                         matrix[3][3], 0);
+}
+
+static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
+    int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
+    int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+    // Treat unsupported dataspaces as srgb
+    if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+        destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+        destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+        destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+    }
+
+    if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+        sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+        sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+        sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+    }
+
+    const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+    const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+    const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+    const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+
+    return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
+            sourceTransfer != destTransfer;
+}
+
+void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    mImageCache.erase(bufferId);
+}
+
+status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
+                                        const std::vector<const LayerSettings*>& layers,
+                                        const sp<GraphicBuffer>& buffer,
+                                        const bool useFramebufferCache,
+                                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+    ATRACE_NAME("SkiaGL::drawLayers");
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    if (layers.empty()) {
+        ALOGV("Drawing empty layer stack");
+        return NO_ERROR;
+    }
+
+    if (bufferFence.get() >= 0) {
+        // Duplicate the fence for passing to waitFence.
+        base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+        if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+            ATRACE_NAME("Waiting before draw");
+            sync_wait(bufferFence.get(), -1);
+        }
+    }
+    if (buffer == nullptr) {
+        ALOGE("No output buffer provided. Aborting GPU composition.");
+        return BAD_VALUE;
+    }
+
+    AHardwareBuffer_Desc bufferDesc;
+    AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
+
+    LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
+                        "missing usage");
+
+    sk_sp<SkSurface> surface;
+    if (useFramebufferCache) {
+        auto iter = mSurfaceCache.find(buffer->getId());
+        if (iter != mSurfaceCache.end()) {
+            ALOGV("Cache hit!");
+            surface = iter->second;
+        }
+    }
+    if (!surface) {
+        surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(),
+                                                     GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+                                                     mUseColorManagement
+                                                             ? toColorSpace(display.outputDataspace)
+                                                             : SkColorSpace::MakeSRGB(),
+                                                     nullptr);
+        if (useFramebufferCache && surface) {
+            ALOGD("Adding to cache");
+            mSurfaceCache.insert({buffer->getId(), surface});
+        }
+    }
+    if (!surface) {
+        ALOGE("Failed to make surface");
+        return BAD_VALUE;
+    }
+
+    auto canvas = surface->getCanvas();
+    // Clear the entire canvas with a transparent black to prevent ghost images.
+    canvas->clear(SK_ColorTRANSPARENT);
+    canvas->save();
+
+    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+    // displays might have different scaling when compared to the physical screen.
+
+    canvas->clipRect(getSkRect(display.physicalDisplay));
+    SkMatrix screenTransform;
+    screenTransform.setTranslate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+    const auto clipWidth = display.clip.width();
+    const auto clipHeight = display.clip.height();
+    auto rotatedClipWidth = clipWidth;
+    auto rotatedClipHeight = clipHeight;
+    // Scale is contingent on the rotation result.
+    if (display.orientation & ui::Transform::ROT_90) {
+        std::swap(rotatedClipWidth, rotatedClipHeight);
+    }
+    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+            static_cast<SkScalar>(rotatedClipWidth);
+    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+            static_cast<SkScalar>(rotatedClipHeight);
+    screenTransform.preScale(scaleX, scaleY);
+
+    // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+    // back so that the top left corner of the clip is at (0, 0).
+    screenTransform.preTranslate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+    screenTransform.preRotate(toDegrees(display.orientation));
+    screenTransform.preTranslate(-clipWidth / 2, -clipHeight / 2);
+    screenTransform.preTranslate(-display.clip.left, -display.clip.top);
+    for (const auto& layer : layers) {
+        const SkMatrix drawTransform = getDrawTransform(layer, screenTransform);
+
+        SkPaint paint;
+        const auto& bounds = layer->geometry.boundaries;
+        const auto dest = getSkRect(bounds);
+        std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs;
+
+        if (mBlurFilter) {
+            const auto layerRect = drawTransform.mapRect(dest);
+            if (layer->backgroundBlurRadius > 0) {
+                ATRACE_NAME("BackgroundBlur");
+                auto blurredSurface =
+                        mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius, layerRect);
+                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+            }
+            if (layer->blurRegions.size() > 0) {
+                for (auto region : layer->blurRegions) {
+                    if (cachedBlurs[region.blurRadius]) {
+                        continue;
+                    }
+                    ATRACE_NAME("BlurRegion");
+                    auto blurredSurface =
+                            mBlurFilter->generate(canvas, surface, region.blurRadius, layerRect);
+                    cachedBlurs[region.blurRadius] = blurredSurface;
+                }
+            }
+        }
+
+        if (layer->source.buffer.buffer) {
+            ATRACE_NAME("DrawImage");
+            const auto& item = layer->source.buffer;
+            const auto bufferWidth = item.buffer->getBounds().width();
+            const auto bufferHeight = item.buffer->getBounds().height();
+            sk_sp<SkImage> image;
+            auto iter = mImageCache.find(item.buffer->getId());
+            if (iter != mImageCache.end()) {
+                image = iter->second;
+            } else {
+                image = SkImage::MakeFromAHardwareBuffer(
+                        item.buffer->toAHardwareBuffer(),
+                        item.isOpaque ? kOpaque_SkAlphaType
+                                      : (item.usePremultipliedAlpha ? kPremul_SkAlphaType
+                                                                    : kUnpremul_SkAlphaType),
+                        mUseColorManagement
+                                ? (needsToneMapping(layer->sourceDataspace, display.outputDataspace)
+                                           // If we need to map to linear space, then
+                                           // mark the source image with the same
+                                           // colorspace as the destination surface so
+                                           // that Skia's color management is a no-op.
+                                           ? toColorSpace(display.outputDataspace)
+                                           : toColorSpace(layer->sourceDataspace))
+                                : SkColorSpace::MakeSRGB());
+                mImageCache.insert({item.buffer->getId(), image});
+            }
+
+            SkMatrix matrix;
+            if (layer->geometry.roundedCornersRadius > 0) {
+                const auto roundedRect = getRoundedRect(layer);
+                matrix.setTranslate(roundedRect.getBounds().left() - dest.left(),
+                                    roundedRect.getBounds().top() - dest.top());
+            } else {
+                matrix.setIdentity();
+            }
+
+            auto texMatrix = getSkM44(item.textureTransform).asM33();
+
+            // b/171404534, scale to fix the layer
+            matrix.postScale(bounds.getWidth() / bufferWidth, bounds.getHeight() / bufferHeight);
+
+            // textureTansform was intended to be passed directly into a shader, so when
+            // building the total matrix with the textureTransform we need to first
+            // normalize it, then apply the textureTransform, then scale back up.
+            matrix.postScale(1.0f / bufferWidth, 1.0f / bufferHeight);
+
+            auto rotatedBufferWidth = bufferWidth;
+            auto rotatedBufferHeight = bufferHeight;
+
+            // Swap the buffer width and height if we're rotating, so that we
+            // scale back up by the correct factors post-rotation.
+            if (texMatrix.getSkewX() <= -0.5f || texMatrix.getSkewX() >= 0.5f) {
+                std::swap(rotatedBufferWidth, rotatedBufferHeight);
+                // TODO: clean this up.
+                // GLESRenderEngine specifies its texture coordinates in
+                // CW orientation under OpenGL conventions, when they probably should have
+                // been CCW instead. The net result is that orientation
+                // transforms are applied in the reverse
+                // direction to render the correct result, because SurfaceFlinger uses the inverse
+                // of the display transform to correct for that. But this means that
+                // the tex transform passed by SkiaGLRenderEngine will rotate
+                // individual layers in the reverse orientation. Hack around it
+                // by injected a 180 degree rotation, but ultimately this is
+                // a bug in how SurfaceFlinger invokes the RenderEngine
+                // interface, so the proper fix should live there, and GLESRenderEngine
+                // should be fixed accordingly.
+                matrix.postRotate(180, 0.5, 0.5);
+            }
+
+            matrix.postConcat(texMatrix);
+            matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
+            sk_sp<SkShader> shader = image->makeShader(matrix);
+
+            if (mUseColorManagement &&
+                needsToneMapping(layer->sourceDataspace, display.outputDataspace)) {
+                LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
+                                                   .outputDataspace = display.outputDataspace,
+                                                   .undoPremultipliedAlpha = !item.isOpaque &&
+                                                           item.usePremultipliedAlpha};
+                sk_sp<SkRuntimeEffect> runtimeEffect = buildRuntimeEffect(effect);
+                paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
+                                                         display.maxLuminance,
+                                                         layer->source.buffer.maxMasteringLuminance,
+                                                         layer->source.buffer.maxContentLuminance));
+            } else {
+                paint.setShader(shader);
+            }
+        } else {
+            ATRACE_NAME("DrawColor");
+            const auto color = layer->source.solidColor;
+            paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
+                                                       .fG = color.g,
+                                                       .fB = color.b,
+                                                       layer->alpha},
+                                             nullptr));
+        }
+
+        paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+
+        canvas->save();
+        canvas->concat(drawTransform);
+
+        for (const auto effectRegion : layer->blurRegions) {
+            drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]);
+        }
+
+        if (layer->shadow.length > 0) {
+            const auto rect = layer->geometry.roundedCornersRadius > 0
+                    ? getSkRect(layer->geometry.roundedCornersCrop)
+                    : dest;
+            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+        }
+
+        if (layer->geometry.roundedCornersRadius > 0) {
+            canvas->drawRRect(getRoundedRect(layer), paint);
+        } else {
+            canvas->drawRect(dest, paint);
+        }
+
+        canvas->restore();
+    }
+    {
+        ATRACE_NAME("flush surface");
+        surface->flush();
+    }
+    canvas->restore();
+
+    if (drawFence != nullptr) {
+        *drawFence = flush();
+    }
+
+    // If flush failed or we don't support native fences, we need to force the
+    // gl command stream to be executed.
+    bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+    if (requireSync) {
+        ATRACE_BEGIN("Submit(sync=true)");
+    } else {
+        ATRACE_BEGIN("Submit(sync=false)");
+    }
+    bool success = mGrContext->submit(requireSync);
+    ATRACE_END();
+    if (!success) {
+        ALOGE("Failed to flush RenderEngine commands");
+        // Chances are, something illegal happened (either the caller passed
+        // us bad parameters, or we messed up our shader generation).
+        return INVALID_OPERATION;
+    }
+
+    // checkErrors();
+    return NO_ERROR;
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
+    return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
+    return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
+    const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+    const auto cornerRadius = layer->geometry.roundedCornersRadius;
+    return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
+}
+
+inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
+    return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
+}
+
+inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
+    return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+                 matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+                 matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+                 matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+}
+
+inline SkMatrix SkiaGLRenderEngine::getDrawTransform(const LayerSettings* layer,
+                                                     const SkMatrix& screenTransform) {
+    // Layers have a local transform matrix that should be applied to them.
+    const auto layerTransform = getSkM44(layer->geometry.positionTransform).asM33();
+    return SkMatrix::Concat(screenTransform, layerTransform);
+}
+
+inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
+    return SkPoint3::Make(vector.x, vector.y, vector.z);
+}
+
+size_t SkiaGLRenderEngine::getMaxTextureSize() const {
+    return mGrContext->maxTextureSize();
+}
+
+size_t SkiaGLRenderEngine::getMaxViewportDims() const {
+    return mGrContext->maxRenderTargetSize();
+}
+
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+                                    const ShadowSettings& settings) {
+    ATRACE_CALL();
+    const float casterZ = settings.length / 2.0f;
+    const auto shadowShape = cornerRadius > 0
+            ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
+            : SkPath::Rect(casterRect);
+    const auto flags =
+            settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
+
+    SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+                              getSkPoint3(settings.lightPos), settings.lightRadius,
+                              getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
+                              flags);
+}
+
+void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+                                        const SkRect& layerBoundaries,
+                                        sk_sp<SkSurface> blurredSurface) {
+    ATRACE_CALL();
+    SkPaint paint;
+    paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
+    const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+                                       effectRegion.bottom);
+
+    const auto matrix = mBlurFilter->getShaderMatrix(
+            SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top()));
+    paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix));
+
+    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+        const SkVector radii[4] =
+                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+        SkRRect roundedRect;
+        roundedRect.setRectRadii(rect, radii);
+        canvas->drawRRect(roundedRect, paint);
+    } else {
+        canvas->drawRect(rect, paint);
+    }
+}
+
+EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+                                                EGLContext shareContext, bool useContextPriority,
+                                                Protection protection) {
+    EGLint renderableType = 0;
+    if (config == EGL_NO_CONFIG_KHR) {
+        renderableType = EGL_OPENGL_ES3_BIT;
+    } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+    }
+    EGLint contextClientVersion = 0;
+    if (renderableType & EGL_OPENGL_ES3_BIT) {
+        contextClientVersion = 3;
+    } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+        contextClientVersion = 2;
+    } else if (renderableType & EGL_OPENGL_ES_BIT) {
+        contextClientVersion = 1;
+    } else {
+        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+    }
+
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(7);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(contextClientVersion);
+    if (useContextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
+    if (protection == Protection::PROTECTED) {
+        contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        contextAttributes.push_back(EGL_TRUE);
+    }
+    contextAttributes.push_back(EGL_NONE);
+
+    EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+    if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+        // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+        // EGL_NO_CONTEXT so that we can abort.
+        if (config != EGL_NO_CONFIG_KHR) {
+            return context;
+        }
+        // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we
+        // should try to fall back to GLES 2.
+        contextAttributes[1] = 2;
+        context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+    }
+
+    return context;
+}
+
+EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
+                                                                  EGLConfig config, int hwcFormat,
+                                                                  Protection protection) {
+    EGLConfig placeholderConfig = config;
+    if (placeholderConfig == EGL_NO_CONFIG_KHR) {
+        placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+    }
+    std::vector<EGLint> attributes;
+    attributes.reserve(7);
+    attributes.push_back(EGL_WIDTH);
+    attributes.push_back(1);
+    attributes.push_back(EGL_HEIGHT);
+    attributes.push_back(1);
+    if (protection == Protection::PROTECTED) {
+        attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        attributes.push_back(EGL_TRUE);
+    }
+    attributes.push_back(EGL_NONE);
+
+    return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
+}
+
+void SkiaGLRenderEngine::cleanFramebufferCache() {
+    mSurfaceCache.clear();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
new file mode 100644
index 0000000..c65e431
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SF_SKIAGLRENDERENGINE_H_
+#define SF_SKIAGLRENDERENGINE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "EGL/egl.h"
+#include "SkiaRenderEngine.h"
+#include "filters/BlurFilter.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
+public:
+    static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
+    SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+                       EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
+                       EGLSurface protectedPlaceholder);
+    ~SkiaGLRenderEngine() override{};
+
+    void unbindExternalTextureBuffer(uint64_t bufferId) override;
+    status_t drawLayers(const DisplaySettings& display,
+                        const std::vector<const LayerSettings*>& layers,
+                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+    void cleanFramebufferCache() override;
+
+protected:
+    void dump(std::string& /*result*/) override{};
+    size_t getMaxTextureSize() const override;
+    size_t getMaxViewportDims() const override;
+
+private:
+    static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+    static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+                                       EGLContext shareContext, bool useContextPriority,
+                                       Protection protection);
+    static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                         int hwcFormat, Protection protection);
+    inline SkRect getSkRect(const FloatRect& layer);
+    inline SkRect getSkRect(const Rect& layer);
+    inline SkRRect getRoundedRect(const LayerSettings* layer);
+    inline SkColor getSkColor(const vec4& color);
+    inline SkM44 getSkM44(const mat4& matrix);
+    inline SkMatrix getDrawTransform(const LayerSettings* layer, const SkMatrix& screenTransform);
+    inline SkPoint3 getSkPoint3(const vec3& vector);
+
+    base::unique_fd flush();
+    bool waitFence(base::unique_fd fenceFd);
+    void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+                    const ShadowSettings& shadowSettings);
+    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds,
+                        sk_sp<SkSurface> blurrendSurface);
+
+    EGLDisplay mEGLDisplay;
+    EGLConfig mEGLConfig;
+    EGLContext mEGLContext;
+    EGLSurface mPlaceholderSurface;
+    EGLContext mProtectedEGLContext;
+    EGLSurface mProtectedPlaceholderSurface;
+    BlurFilter* mBlurFilter = nullptr;
+
+    const bool mUseColorManagement;
+
+    // Cache of GL images that we'll store per GraphicBuffer ID
+    std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
+    // Mutex guarding rendering operations, so that:
+    // 1. GL operations aren't interleaved, and
+    // 2. Internal state related to rendering that is potentially modified by
+    // multiple threads is guaranteed thread-safe.
+    std::mutex mRenderingMutex;
+
+    sp<Fence> mLastDrawFence;
+
+    sk_sp<GrDirectContext> mGrContext;
+
+    std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
similarity index 68%
rename from libs/ui/UiConfig.cpp
rename to libs/renderengine/skia/SkiaRenderEngine.cpp
index 0ac863d..81f0b6f 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-#include <ui/UiConfig.h>
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 namespace android {
-
-void appendUiConfigString(std::string& configStr) {
-    static const char* config =
-            " [libui]";
-    configStr.append(config);
-}
-
-
-}; // namespace android
+namespace renderengine {
+namespace skia {} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
new file mode 100644
index 0000000..2352c7e
--- /dev/null
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SF_SKIARENDERENGINE_H_
+#define SF_SKIARENDERENGINE_H_
+
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace skia {
+
+class BlurFilter;
+
+// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends
+// Currently mostly just handles all the no-op / missing APIs
+class SkiaRenderEngine : public RenderEngine {
+public:
+    static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+    ~SkiaRenderEngine() override {}
+
+    virtual void primeCache() const override{};
+    virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
+    virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
+    virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
+    virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){};
+
+    virtual bool isProtected() const override { return false; } // mInProtectedContext; }
+    virtual bool supportsProtectedContent() const override { return false; };
+    virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
+    virtual status_t drawLayers(const DisplaySettings& /*display*/,
+                                const std::vector<const LayerSettings*>& /*layers*/,
+                                const sp<GraphicBuffer>& /*buffer*/,
+                                const bool /*useFramebufferCache*/,
+                                base::unique_fd&& /*bufferFence*/,
+                                base::unique_fd* /*drawFence*/) override {
+        return 0;
+    };
+    virtual bool cleanupPostRender(CleanupMode) override { return true; };
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
new file mode 100644
index 0000000..dfb306f
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+BlurFilter::BlurFilter() {
+    SkString blurString(R"(
+        in shader input;
+        uniform float in_inverseScale;
+        uniform float2 in_blurOffset;
+
+        half4 main(float2 xy) {
+            float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
+
+            float4 c = float4(sample(input, scaled_xy));
+            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+
+            return half4(c.rgb * 0.2, 1.0);
+        }
+    )");
+
+    auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+    if (!blurEffect) {
+        LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+    }
+    mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                      const uint32_t blurRadius, SkRect rect) const {
+    ATRACE_CALL();
+
+    // Kawase is an approximation of Gaussian, but it behaves differently from it.
+    // A radius transformation is required for approximating them, and also to introduce
+    // non-integer steps, necessary to smoothly interpolate large radii.
+    float tmpRadius = (float)blurRadius / 6.0f;
+    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+    float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
+                                                        (float)input->height() * kInputScale);
+
+    SkRect scaledRect = {rect.fLeft * kInputScale, rect.fTop * kInputScale,
+                         rect.fRight * kInputScale, rect.fBottom * kInputScale};
+    auto drawSurface = canvas->makeSurface(scaledInfo);
+
+    const float stepX = radiusByPasses;
+    const float stepY = radiusByPasses;
+
+    // start by drawing and downscaling and doing the first blur pass
+    SkFilterOptions linear = {SkSamplingMode::kLinear, SkMipmapMode::kNone};
+    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+    blurBuilder.child("input") =
+            input->makeImageSnapshot()->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
+    blurBuilder.uniform("in_blurOffset") =
+            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+
+    {
+        // limit the lifetime of the input surface's snapshot to ensure that it goes out of
+        // scope before the surface is written into to avoid any copy-on-write behavior.
+        SkPaint paint;
+        paint.setShader(blurBuilder.makeShader(nullptr, false));
+        paint.setFilterQuality(kLow_SkFilterQuality);
+
+        drawSurface->getCanvas()->drawRect(scaledRect, paint);
+
+        blurBuilder.child("input") = nullptr;
+    }
+
+    // And now we'll ping pong between our surfaces, to accumulate the result of various offsets.
+    auto lastDrawTarget = drawSurface;
+    if (numberOfPasses > 1) {
+        auto readSurface = drawSurface;
+        drawSurface = canvas->makeSurface(scaledInfo);
+
+        for (auto i = 1; i < numberOfPasses; i++) {
+            const float stepScale = (float)i * kInputScale;
+
+            blurBuilder.child("input") =
+                    readSurface->makeImageSnapshot()->makeShader(SkTileMode::kClamp,
+                                                                 SkTileMode::kClamp, linear);
+            blurBuilder.uniform("in_inverseScale") = 1.0f;
+            blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
+
+            SkPaint paint;
+            paint.setShader(blurBuilder.makeShader(nullptr, false));
+            paint.setFilterQuality(kLow_SkFilterQuality);
+
+            drawSurface->getCanvas()->drawRect(scaledRect, paint);
+
+            // Swap buffers for next iteration
+            const auto tmp = drawSurface;
+            drawSurface = readSurface;
+            readSurface = tmp;
+            blurBuilder.child("input") = nullptr;
+        }
+        lastDrawTarget = readSurface;
+    }
+    lastDrawTarget->flushAndSubmit();
+    return lastDrawTarget;
+}
+
+sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                  const uint32_t blurRadius, SkRect rect) const {
+    ATRACE_CALL();
+    sk_sp<SkSurface> surface = generate(canvas, input, blurRadius, rect);
+    const auto image = surface->makeImageSnapshot();
+
+    SkPaint paint;
+    paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale)));
+    paint.setFilterQuality(kLow_SkFilterQuality);
+    paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+
+    canvas->drawRect(rect, paint);
+
+    return surface;
+}
+
+SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const {
+    SkMatrix matrix;
+    matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale));
+    return matrix;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
new file mode 100644
index 0000000..c0a92f6
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+    // Downsample FBO to improve performance
+    static constexpr float kInputScale = 0.25f;
+    // Downsample scale factor used to improve performance
+    static constexpr float kInverseInputScale = 1.0f / kInputScale;
+    // Maximum number of render passes
+    static constexpr uint32_t kMaxPasses = 4;
+    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+    // image, up to this radius.
+    static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+    explicit BlurFilter();
+    virtual ~BlurFilter(){};
+
+    // Execute blur, saving it to a texture
+    sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
+                              SkRect rect) const;
+    // Same as generate but also drawing to the screen
+    sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
+                          SkRect rect) const;
+    // Returns a matrix that should be applied to the blur shader
+    SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const;
+
+private:
+    sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
new file mode 100644
index 0000000..376abdf
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LinearEffect.h"
+
+#include <SkString.h>
+
+#include <optional>
+
+#include "log/log.h"
+#include "math/mat4.h"
+#include "ui/ColorSpace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 EOTF(float3 color) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+                    tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+                    return pow(tmp, 1.0 / float3(m1));
+                }
+            )");
+            break;
+        default:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateXYZTransforms(SkString& shader) {
+    shader.append(R"(
+        uniform float4x4 in_rgbToXyz;
+        uniform float4x4 in_xyzToRgb;
+        float3 ToXYZ(float3 rgb) {
+            return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
+        }
+
+        float3 ToRGB(float3 xyz) {
+            return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+        }
+    )");
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+                         SkString& shader) {
+    shader.append(R"(
+            uniform float in_displayMaxLuminance;
+            uniform float in_inputMaxLuminance;
+            uniform float in_maxContentLuminance;
+        )");
+    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * 10000.0;
+                    }
+                )");
+
+            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+                default:
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                float maxInLumi = in_inputMaxLuminance;
+                                float maxOutLumi = in_displayMaxLuminance;
+
+                                float nits = xyz.y;
+
+                                // clamp to max input luminance
+                                nits = clamp(nits, 0.0, maxInLumi);
+
+                                // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+                                if (maxInLumi <= maxOutLumi) {
+                                    return xyz * (maxOutLumi / maxInLumi);
+                                } else {
+                                    // three control points
+                                    const float x0 = 10.0;
+                                    const float y0 = 17.0;
+                                    float x1 = maxOutLumi * 0.75;
+                                    float y1 = x1;
+                                    float x2 = x1 + (maxInLumi - x1) / 2.0;
+                                    float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+                                    // horizontal distances between the last three control points
+                                    float h12 = x2 - x1;
+                                    float h23 = maxInLumi - x2;
+                                    // tangents at the last three control points
+                                    float m1 = (y2 - y1) / h12;
+                                    float m3 = (maxOutLumi - y2) / h23;
+                                    float m2 = (m1 + m3) / 2.0;
+
+                                    if (nits < x0) {
+                                        // scale [0.0, x0] to [0.0, y0] linearly
+                                        float slope = y0 / x0;
+                                        return xyz * slope;
+                                    } else if (nits < x1) {
+                                        // scale [x0, x1] to [y0, y1] linearly
+                                        float slope = (y1 - y0) / (x1 - x0);
+                                        nits = y0 + (nits - x0) * slope;
+                                    } else if (nits < x2) {
+                                        // scale [x1, x2] to [y1, y2] using Hermite interp
+                                        float t = (nits - x1) / h12;
+                                        nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+                                                (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                                    } else {
+                                        // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+                                        float t = (nits - x2) / h23;
+                                        nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+                                                (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+                                    }
+                                }
+
+                                // color.y is greater than x0 and is thus non-zero
+                                return xyz * (nits / xyz.y);
+                            }
+                        )");
+                    break;
+            }
+            break;
+        default:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * in_displayMaxLuminance;
+                    }
+
+                    float3 ToneMap(float3 xyz) {
+                        return xyz;
+                    }
+                )");
+            break;
+    }
+
+    switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / 10000.0;
+                    }
+                )");
+            break;
+        default:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / in_displayMaxLuminance;
+                    }
+                )");
+            break;
+    }
+
+    shader.append(R"(
+            float3 OOTF(float3 xyz) {
+                return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
+            }
+        )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 OETF(float3 xyz) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(xyz, float3(m1));
+                    tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+                    return pow(tmp, float3(m2));
+                }
+            )");
+            break;
+        default:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return linear <= 0.0031308 ?
+                            linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
+    shader.append(R"(
+        in shader input;
+        half4 main(float2 xy) {
+            float4 c = float4(sample(input, xy));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb / (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+        c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb * (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+            return c;
+        }
+    )");
+}
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            return ColorSpace::sRGB();
+            break;
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            return ColorSpace::DisplayP3();
+            break;
+        case HAL_DATASPACE_STANDARD_BT2020:
+            return ColorSpace::BT2020();
+            break;
+        default:
+            return ColorSpace::sRGB();
+            break;
+    }
+}
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+    SkString shaderString;
+    generateEOTF(linearEffect.inputDataspace, shaderString);
+    generateXYZTransforms(shaderString);
+    generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+    generateOETF(linearEffect.outputDataspace, shaderString);
+    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+
+    auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+    if (!shader) {
+        LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+    }
+    return shader;
+}
+
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+                                         sk_sp<SkRuntimeEffect> runtimeEffect,
+                                         float maxDisplayLuminance, float maxMasteringLuminance,
+                                         float maxContentLuminance) {
+    SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
+
+    effectBuilder.child("input") = shader;
+
+    ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+    ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+
+    effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
+    effectBuilder.uniform("in_xyzToRgb") = mat4(outputColorSpace.getXYZtoRGB());
+    effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+    effectBuilder.uniform("in_inputMaxLuminance") =
+            std::min(maxMasteringLuminance, maxContentLuminance);
+    return effectBuilder.makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
new file mode 100644
index 0000000..2615669
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "SkColorMatrix.h"
+#include "SkRuntimeEffect.h"
+#include "SkShader.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+    // Input dataspace of the source colors.
+    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+    // Working dataspace for the output surface, for conversion from linear space.
+    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+    // Sets whether alpha premultiplication must be undone.
+    // This is required if the source colors use premultiplied alpha and is not opaque.
+    const bool undoPremultipliedAlpha = false;
+};
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+
+// Generates a shader resulting from applying the a linear effect created from
+// LinearEffectARgs::buildEffect to an inputShader. 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 max mastering luminance is provided as the max luminance from the SMPTE 2086
+// standard.
+// * The max content luminance is provided as the max light level from the CTA 861.3
+// standard.
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
+                                         const LinearEffect& linearEffect,
+                                         sk_sp<SkRuntimeEffect> runtimeEffect,
+                                         float maxDisplayLuminance, float maxMasteringLuminance,
+                                         float maxContentLuminance);
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 77b6c0f..d20fcc4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -39,7 +39,7 @@
 
 struct RenderEngineTest : public ::testing::Test {
     static void SetUpTestSuite() {
-        sRE = renderengine::gl::GLESRenderEngine::create(
+        renderengine::RenderEngineCreationArgs reCreationArgs =
                 renderengine::RenderEngineCreationArgs::Builder()
                         .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
                         .setImageCacheSize(1)
@@ -49,13 +49,18 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
-                        .build());
+                        .build();
+        sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+
+        reCreationArgs.useColorManagement = true;
+        sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
 
     static void TearDownTestSuite() {
         // The ordering here is important - sCurrentBuffer must live longer
         // than RenderEngine to avoid a null reference on tear-down.
         sRE = nullptr;
+        sRECM = nullptr;
         sCurrentBuffer = nullptr;
     }
 
@@ -83,6 +88,10 @@
         }
         for (uint32_t texName : mTexNames) {
             sRE->deleteTextures(1, &texName);
+            EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
+        }
+        for (uint32_t texName : mTexNamesCM) {
+            sRECM->deleteTextures(1, &texName);
         }
     }
 
@@ -252,10 +261,11 @@
 
     void invokeDraw(renderengine::DisplaySettings settings,
                     std::vector<const renderengine::LayerSettings*> layers,
-                    sp<GraphicBuffer> buffer) {
+                    sp<GraphicBuffer> buffer, bool useColorManagement = false) {
         base::unique_fd fence;
-        status_t status =
-                sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
+        status_t status = useColorManagement
+                ? sRECM->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence)
+                : sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
         sCurrentBuffer = buffer;
 
         int fd = fence.release();
@@ -266,7 +276,11 @@
 
         ASSERT_EQ(NO_ERROR, status);
         if (layers.size() > 0) {
-            ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+            if (useColorManagement) {
+                ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
+            } else {
+                ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+            }
         }
     }
 
@@ -321,12 +335,15 @@
     void fillBufferLayerTransform();
 
     template <typename SourceVariant>
-    void fillBufferWithColorTransform();
+    void fillBufferWithColorTransform(bool useColorManagement = false);
 
     template <typename SourceVariant>
     void fillBufferColorTransform();
 
     template <typename SourceVariant>
+    void fillBufferColorTransformCM();
+
+    template <typename SourceVariant>
     void fillRedBufferWithRoundedCorners();
 
     template <typename SourceVariant>
@@ -365,6 +382,8 @@
     // For now, exercise the GL backend directly so that some caching specifics
     // can be tested without changing the interface.
     static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+    // renderengine object with Color Management enabled
+    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
     // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
     // be freed *after* RenderEngine is destroyed, so that the EGL image is
     // destroyed first.
@@ -373,14 +392,17 @@
     sp<GraphicBuffer> mBuffer;
 
     std::vector<uint32_t> mTexNames;
+    std::vector<uint32_t> mTexNamesCM;
 };
 
 std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
+
 sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
 
 struct ColorSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
-                          RenderEngineTest* /*fixture*/) {
+                          RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
         layer.source.solidColor = half3(r, g, b);
     }
 };
@@ -408,11 +430,16 @@
 template <typename OpaquenessVariant>
 struct BufferSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
-                          RenderEngineTest* fixture) {
+                          RenderEngineTest* fixture, bool useColorManagement = false) {
         sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
         uint32_t texName;
-        fixture->sRE->genTextures(1, &texName);
-        fixture->mTexNames.push_back(texName);
+        if (useColorManagement) {
+            fixture->sRECM->genTextures(1, &texName);
+            fixture->mTexNamesCM.push_back(texName);
+        } else {
+            fixture->sRE->genTextures(1, &texName);
+            fixture->mTexNames.push_back(texName);
+        }
 
         uint8_t* pixels;
         buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -642,7 +669,7 @@
 }
 
 template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform() {
+void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
@@ -651,12 +678,12 @@
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
-    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
     layer.alpha = 1.0f;
 
     // construct a fake color matrix
     // annihilate green and blue channels
-    settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+    settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
     // set red channel to red + green
     layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
 
@@ -665,13 +692,19 @@
 
     layers.push_back(&layer);
 
-    invokeDraw(settings, layers, mBuffer);
+    invokeDraw(settings, layers, mBuffer, useColorManagement);
 }
 
 template <typename SourceVariant>
 void RenderEngineTest::fillBufferColorTransform() {
     fillBufferWithColorTransform<SourceVariant>();
-    expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+    expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformCM() {
+    fillBufferWithColorTransform<SourceVariant>(true);
+    expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
 }
 
 template <typename SourceVariant>
@@ -918,7 +951,7 @@
 
 void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
     fillRedBufferWithoutPremultiplyAlpha();
-    expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+    expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
 }
 
 void RenderEngineTest::clearLeftRegion() {
@@ -928,7 +961,7 @@
     settings.clip = Rect(4, 4);
     settings.clearRegion = Region(Rect(2, 4));
     std::vector<const renderengine::LayerSettings*> layers;
-    // dummy layer, without bounds should not render anything
+    // fake layer, without bounds should not render anything
     renderengine::LayerSettings layer;
     layers.push_back(&layer);
     invokeDraw(settings, layers, mBuffer);
@@ -1072,7 +1105,11 @@
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
-    fillBufferLayerTransform<ColorSourceVariant>();
+    fillBufferColorTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
+    fillBufferColorTransformCM<ColorSourceVariant>();
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
@@ -1128,7 +1165,11 @@
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
-    fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+    fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
+    fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
@@ -1184,7 +1225,11 @@
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
-    fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+    fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
+    fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
 TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
@@ -1241,31 +1286,6 @@
     EXPECT_EQ(NO_ERROR, barrier->result);
 }
 
-TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
-    status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
-    ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
-    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
-    uint32_t texName;
-    sRE->genTextures(1, &texName);
-    mTexNames.push_back(texName);
-
-    sRE->bindExternalTextureBuffer(texName, buf, nullptr);
-    uint64_t bufferId = buf->getId();
-    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
-    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            sRE->unbindExternalTextureBufferForTesting(bufferId);
-    std::lock_guard<std::mutex> lock(barrier->mutex);
-    ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
-                                            [&]() REQUIRES(barrier->mutex) {
-                                                return barrier->isOpen;
-                                            }));
-    EXPECT_EQ(NO_ERROR, barrier->result);
-    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
-}
-
 TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
             sRE->cacheExternalTextureBufferForTesting(nullptr);
@@ -1422,10 +1442,44 @@
     if (fd >= 0) {
         sync_wait(fd, -1);
     }
-
     // Only cleanup the first time.
-    EXPECT_TRUE(sRE->cleanupPostRender());
-    EXPECT_FALSE(sRE->cleanupPostRender());
+    EXPECT_TRUE(sRE->cleanupPostRender(
+            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+    EXPECT_FALSE(sRE->cleanupPostRender(
+            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0;
+    layers.push_back(&layer);
+
+    base::unique_fd fence;
+    sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+
+    const int fd = fence.get();
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+    }
+
+    uint64_t bufferId = layer.source.buffer.buffer->getId();
+    uint32_t texName = layer.source.buffer.textureName;
+    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+
+    EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+
+    // Now check that our view of memory is good.
+    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+    EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
+    EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
 }
 
 } // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 69a0e19..ba5175d 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -62,21 +62,6 @@
     mThreadedRE->deleteTextures(1, &texName);
 }
 
-TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
-    EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
-            .WillOnce(Return(BAD_VALUE));
-    status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
-    ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
-    sp<GraphicBuffer> buf = new GraphicBuffer();
-    EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
-            .WillOnce(Return(NO_ERROR));
-    status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
-    ASSERT_EQ(NO_ERROR, result);
-}
-
 TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
     EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
     mThreadedRE->cacheExternalTextureBuffer(nullptr);
@@ -93,26 +78,6 @@
     mThreadedRE->unbindExternalTextureBuffer(0x0);
 }
 
-TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
-    std::unique_ptr<renderengine::Framebuffer> framebuffer;
-    EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
-    status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
-    ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
-    std::unique_ptr<renderengine::Framebuffer> framebuffer;
-    EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
-    status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
-    ASSERT_EQ(NO_ERROR, result);
-}
-
-TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
-    std::unique_ptr<renderengine::Framebuffer> framebuffer;
-    EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
-    mThreadedRE->unbindFrameBuffer(framebuffer.get());
-}
-
 TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
     size_t size = 20;
     EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
@@ -178,14 +143,20 @@
 }
 
 TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
-    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
-    status_t result = mThreadedRE->cleanupPostRender();
+    EXPECT_CALL(*mRenderEngine,
+                cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+            .WillOnce(Return(false));
+    status_t result =
+            mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
     ASSERT_EQ(false, result);
 }
 
 TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
-    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
-    status_t result = mThreadedRE->cleanupPostRender();
+    EXPECT_CALL(*mRenderEngine,
+                cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+            .WillOnce(Return(true));
+    status_t result =
+            mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
     ASSERT_EQ(true, result);
 }
 
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index ad61718..5453302 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -116,36 +116,6 @@
     result.assign(resultFuture.get());
 }
 
-bool RenderEngineThreaded::useNativeFenceSync() const {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
-            ATRACE_NAME("REThreaded::useNativeFenceSync");
-            bool returnValue = SyncFeatures::getInstance().useNativeFenceSync();
-            resultPromise.set_value(returnValue);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
-}
-
-bool RenderEngineThreaded::useWaitSync() const {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
-            ATRACE_NAME("REThreaded::useWaitSync");
-            bool returnValue = SyncFeatures::getInstance().useWaitSync();
-            resultPromise.set_value(returnValue);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
-}
-
 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
@@ -176,40 +146,6 @@
     resultFuture.wait();
 }
 
-void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
-    std::promise<void> resultPromise;
-    std::future<void> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push(
-                [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
-                    ATRACE_NAME("REThreaded::bindExternalTextureImage");
-                    instance.bindExternalTextureImage(texName, image);
-                    resultPromise.set_value();
-                });
-    }
-    mCondition.notify_one();
-    resultFuture.wait();
-}
-
-status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
-                                                         const sp<GraphicBuffer>& buffer,
-                                                         const sp<Fence>& fence) {
-    std::promise<status_t> resultPromise;
-    std::future<status_t> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push(
-                [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
-                    ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
-                    status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
-                    resultPromise.set_value(status);
-                });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
-}
-
 void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
@@ -240,36 +176,6 @@
     resultFuture.wait();
 }
 
-status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
-    std::promise<status_t> resultPromise;
-    std::future<status_t> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::bindFrameBuffer");
-            status_t status = instance.bindFrameBuffer(framebuffer);
-            resultPromise.set_value(status);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
-}
-
-void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
-    std::promise<void> resultPromise;
-    std::future<void> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::unbindFrameBuffer");
-            instance.unbindFrameBuffer(framebuffer);
-            resultPromise.set_value();
-        });
-    }
-    mCondition.notify_one();
-    resultFuture.wait();
-}
-
 size_t RenderEngineThreaded::getMaxTextureSize() const {
     std::promise<size_t> resultPromise;
     std::future<size_t> resultFuture = resultPromise.get_future();
@@ -346,29 +252,14 @@
     return resultFuture.get();
 }
 
-Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
-    std::promise<Framebuffer*> resultPromise;
-    std::future<Framebuffer*> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::getFramebufferForDrawing");
-            Framebuffer* framebuffer = instance.getFramebufferForDrawing();
-            resultPromise.set_value(framebuffer);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
-}
-
-bool RenderEngineThreaded::cleanupPostRender() {
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
     std::promise<bool> resultPromise;
     std::future<bool> resultFuture = resultPromise.get_future();
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::cleanupPostRender");
-            bool returnValue = instance.cleanupPostRender();
+            bool returnValue = instance.cleanupPostRender(mode);
             resultPromise.set_value(returnValue);
         });
     }
@@ -398,6 +289,21 @@
     return resultFuture.get();
 }
 
+void RenderEngineThreaded::cleanFramebufferCache() {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::cleanFramebufferCache");
+            instance.cleanFramebufferCache();
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
 } // namespace threaded
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index ec18e1f..cdfbd04 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -45,32 +45,24 @@
 
     void dump(std::string& result) override;
 
-    bool useNativeFenceSync() const override;
-    bool useWaitSync() const override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
-    void bindExternalTextureImage(uint32_t texName, const Image& image) override;
-    status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
-                                       const sp<Fence>& fence) override;
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     void unbindExternalTextureBuffer(uint64_t bufferId) override;
-    status_t bindFrameBuffer(Framebuffer* framebuffer) override;
-    void unbindFrameBuffer(Framebuffer* framebuffer) override;
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
 
     bool isProtected() const override;
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
-    bool cleanupPostRender() override;
+    bool cleanupPostRender(CleanupMode mode) override;
 
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
-protected:
-    Framebuffer* getFramebufferForDrawing() override;
+    void cleanFramebufferCache() override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
@@ -94,4 +86,4 @@
 };
 } // namespace threaded
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 8ed09f8..a6b0aaf 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -216,14 +216,25 @@
             int32_t type;
             Vector<float> floats;
             Vector<int32_t> ints;
+            uint32_t count;
 
             handle = data.readInt32();
             type = data.readInt32();
-            floats.resize(data.readUint32());
+
+            count = data.readUint32();
+            if (count > (data.dataAvail() / sizeof(float))) {
+              return BAD_VALUE;
+            }
+            floats.resize(count);
             for (auto &i : floats) {
                 i = data.readFloat();
             }
-            ints.resize(data.readUint32());
+
+            count = data.readUint32();
+            if (count > (data.dataAvail() / sizeof(int32_t))) {
+              return BAD_VALUE;
+            }
+            ints.resize(count);
             for (auto &i : ints) {
                 i = data.readInt32();
             }
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index f3edd3c..7c68aaa 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -51,22 +51,30 @@
     min_sdk_version: "apex_inherit",
 
     shared_libs: [
+        "libbase",
         "libutils",
     ],
 
     static_libs: [
+        "libarect",
         "libmath",
     ],
 
     srcs: [
         "ColorSpace.cpp",
+        "Rect.cpp",
+        "Region.cpp",
+        "Transform.cpp",
     ],
 
     export_include_dirs: [
+        "include",
+        "include_private",
         "include_types",
     ],
 
     export_static_lib_headers: [
+        "libarect",
         "libmath",
     ],
 
@@ -111,11 +119,7 @@
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
-        "Rect.cpp",
-        "Region.cpp",
         "Size.cpp",
-        "Transform.cpp",
-        "UiConfig.cpp",
     ],
 
     include_dirs: [
@@ -136,7 +140,7 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-unstable-ndk_platform",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
@@ -153,7 +157,7 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-unstable-ndk_platform",
         "android.hardware.graphics.mapper@4.0",
         "libgralloctypes",
     ],
@@ -239,6 +243,8 @@
     name: "libui_host_common",
     srcs: [
         "Rect.cpp",
-        "PixelFormat.cpp"
+        "Region.cpp",
+        "PixelFormat.cpp",
+        "Transform.cpp"
     ],
 }
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index f394635..1f006ce 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -321,10 +321,6 @@
     return std::string("Unknown RenderIntent");
 }
 
-std::string to_string(const android::Rect& rect) {
-    return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
-}
-
 std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
     using ModelYear = android::DeviceProductInfo::ModelYear;
     using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 943d13e..91d2d58 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -83,20 +83,17 @@
     KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
     uint64_t total = 0;
     result.append("GraphicBufferAllocator buffers:\n");
-    const size_t c = list.size();
-    for (size_t i=0 ; i<c ; i++) {
+    const size_t count = list.size();
+    StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+                  "W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
+    for (size_t i = 0; i < count; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
-        if (rec.size) {
-            StringAppendF(&result,
-                          "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
-                          list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height,
-                          rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
-        } else {
-            StringAppendF(&result,
-                          "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
-                          list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount,
-                          rec.format, rec.usage, rec.requestorName.c_str());
-        }
+        std::string sizeStr = (rec.size)
+                ? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
+                : "unknown";
+        StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+                      list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
+                      rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
         total += rec.size;
     }
     StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 203a739..b1317b1 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
 chrisforbes@google.com
+jreck@google.com
 lpy@google.com
 mathias@google.com
 romainguy@google.com
 stoza@google.com
-vhau@google.com
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index 70e3ce7..a6595cf 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -35,6 +35,8 @@
         case PublicFormat::RAW_SENSOR:
         case PublicFormat::RAW_DEPTH:
             return HAL_PIXEL_FORMAT_RAW16;
+        case PublicFormat::RAW_DEPTH10:
+            return HAL_PIXEL_FORMAT_RAW10;
         default:
             // Most formats map 1:1
             return static_cast<int>(f);
@@ -50,6 +52,7 @@
         case PublicFormat::DEPTH_POINT_CLOUD:
         case PublicFormat::DEPTH16:
         case PublicFormat::RAW_DEPTH:
+        case PublicFormat::RAW_DEPTH10:
             dataspace = Dataspace::DEPTH;
             break;
         case PublicFormat::RAW_SENSOR:
@@ -80,6 +83,13 @@
 PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
     Dataspace ds = static_cast<Dataspace>(dataSpace);
     switch (format) {
+        case HAL_PIXEL_FORMAT_RAW10:
+            switch (ds) {
+                case Dataspace::DEPTH:
+                    return PublicFormat::RAW_DEPTH10;
+                default:
+                    return PublicFormat::RAW10;
+            }
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
         case HAL_PIXEL_FORMAT_RGBA_FP16:
@@ -87,7 +97,6 @@
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
-        case HAL_PIXEL_FORMAT_RAW10:
         case HAL_PIXEL_FORMAT_RAW12:
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
         case HAL_PIXEL_FORMAT_YV12:
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 13fed3a..a8d6285 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/stringprintf.h>
 #include <system/graphics.h>
 #include <ui/Rect.h>
 
@@ -149,4 +150,13 @@
     return result;
 }
 
+std::string to_string(const android::Rect& rect) {
+    return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right,
+                                       rect.bottom);
+}
+
+void PrintTo(const Rect& rect, ::std::ostream* os) {
+    *os << to_string(rect);
+}
+
 }; // namespace android
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 06b6bfe..cd68c1c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "Transform"
+
 #include <math.h>
 
 #include <android-base/stringprintf.h>
@@ -22,8 +25,7 @@
 #include <ui/Transform.h>
 #include <utils/String8.h>
 
-namespace android {
-namespace ui {
+namespace android::ui {
 
 Transform::Transform() {
     reset();
@@ -55,11 +57,9 @@
             mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
             mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
             mMatrix[2][2] == other.mMatrix[2][2];
-    ;
 }
 
-Transform Transform::operator * (const Transform& rhs) const
-{
+Transform Transform::operator*(const Transform& rhs) const {
     if (CC_LIKELY(mType == IDENTITY))
         return rhs;
 
@@ -87,6 +87,19 @@
     return r;
 }
 
+Transform Transform::operator * (float value) const {
+    Transform r(*this);
+    const mat33& M(mMatrix);
+    mat33& R(r.mMatrix);
+    for (size_t i = 0; i < 3; i++) {
+        for (size_t j = 0; j < 2; j++) {
+            R[i][j] = M[i][j] * value;
+        }
+    }
+    r.type();
+    return r;
+}
+
 Transform& Transform::operator=(const Transform& other) {
     mMatrix = other.mMatrix;
     mType = other.mType;
@@ -105,14 +118,30 @@
     return mMatrix[2][1];
 }
 
-float Transform::sx() const {
+float Transform::dsdx() const {
     return mMatrix[0][0];
 }
 
-float Transform::sy() const {
+float Transform::dtdx() const {
+    return mMatrix[1][0];
+}
+
+float Transform::dtdy() const {
+    return mMatrix[0][1];
+}
+
+float Transform::dsdy() const {
     return mMatrix[1][1];
 }
 
+float Transform::getScaleX() const {
+    return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
+}
+
+float Transform::getScaleY() const {
+    return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy()));
+}
+
 void Transform::reset() {
     mType = IDENTITY;
     for(size_t i = 0; i < 3; i++) {
@@ -122,8 +151,7 @@
     }
 }
 
-void Transform::set(float tx, float ty)
-{
+void Transform::set(float tx, float ty) {
     mMatrix[2][0] = tx;
     mMatrix[2][1] = ty;
     mMatrix[2][2] = 1.0f;
@@ -135,8 +163,7 @@
     }
 }
 
-void Transform::set(float a, float b, float c, float d)
-{
+void Transform::set(float a, float b, float c, float d) {
     mat33& M(mMatrix);
     M[0][0] = a;    M[1][0] = b;
     M[0][1] = c;    M[1][1] = d;
@@ -144,8 +171,7 @@
     mType = UNKNOWN_TYPE;
 }
 
-status_t Transform::set(uint32_t flags, float w, float h)
-{
+status_t Transform::set(uint32_t flags, float w, float h) {
     if (flags & ROT_INVALID) {
         // that's not allowed!
         reset();
@@ -187,6 +213,15 @@
     return NO_ERROR;
 }
 
+void Transform::set(const std::array<float, 9>& matrix) {
+    mat33& M(mMatrix);
+    M[0][0] = matrix[0];  M[1][0] = matrix[1];  M[2][0] = matrix[2];
+    M[0][1] = matrix[3];  M[1][1] = matrix[4];  M[2][1] = matrix[5];
+    M[0][2] = matrix[6];  M[1][2] = matrix[7];  M[2][2] = matrix[8];
+    mType = UNKNOWN_TYPE;
+    type();
+}
+
 vec2 Transform::transform(const vec2& v) const {
     vec2 r;
     const mat33& M(mMatrix);
@@ -204,18 +239,15 @@
     return r;
 }
 
-vec2 Transform::transform(int x, int y) const
-{
-    return transform(vec2(x,y));
+vec2 Transform::transform(float x, float y) const {
+    return transform(vec2(x, y));
 }
 
-Rect Transform::makeBounds(int w, int h) const
-{
+Rect Transform::makeBounds(int w, int h) const {
     return transform( Rect(w, h) );
 }
 
-Rect Transform::transform(const Rect& bounds, bool roundOutwards) const
-{
+Rect Transform::transform(const Rect& bounds, bool roundOutwards) const {
     Rect r;
     vec2 lt( bounds.left,  bounds.top    );
     vec2 rt( bounds.right, bounds.top    );
@@ -242,8 +274,7 @@
     return r;
 }
 
-FloatRect Transform::transform(const FloatRect& bounds) const
-{
+FloatRect Transform::transform(const FloatRect& bounds) const {
     vec2 lt(bounds.left, bounds.top);
     vec2 rt(bounds.right, bounds.top);
     vec2 lb(bounds.left, bounds.bottom);
@@ -263,8 +294,7 @@
     return r;
 }
 
-Region Transform::transform(const Region& reg) const
-{
+Region Transform::transform(const Region& reg) const {
     Region out;
     if (CC_UNLIKELY(type() > TRANSLATE)) {
         if (CC_LIKELY(preserveRects())) {
@@ -284,8 +314,7 @@
     return out;
 }
 
-uint32_t Transform::type() const
-{
+uint32_t Transform::type() const {
     if (mType & UNKNOWN_TYPE) {
         // recompute what this transform is
 
@@ -380,16 +409,18 @@
     return type() & 0xFF;
 }
 
-uint32_t Transform::getOrientation() const
-{
+uint32_t Transform::getOrientation() const {
     return (type() >> 8) & 0xFF;
 }
 
-bool Transform::preserveRects() const
-{
+bool Transform::preserveRects() const {
     return (getOrientation() & ROT_INVALID) ? false : true;
 }
 
+bool Transform::needsBilinearFiltering() const {
+    return (!preserveRects() || getType() >= ui::Transform::SCALE);
+}
+
 mat4 Transform::asMatrix4() const {
     // Internally Transform uses a 3x3 matrix since the transform is meant for
     // two-dimensional values. An equivalent 4x4 matrix means inserting an extra
@@ -421,7 +452,43 @@
     return m;
 }
 
-void Transform::dump(std::string& out, const char* name) const {
+static std::string rotationToString(const uint32_t rotationFlags) {
+    switch (rotationFlags) {
+        case Transform::ROT_0:
+            return "ROT_0";
+        case Transform::FLIP_H:
+            return "FLIP_H";
+        case Transform::FLIP_V:
+            return "FLIP_V";
+        case Transform::ROT_90:
+            return "ROT_90";
+        case Transform::ROT_180:
+            return "ROT_180";
+        case Transform::ROT_270:
+            return "ROT_270";
+        case Transform::ROT_INVALID:
+        default:
+            return "ROT_INVALID";
+    }
+}
+
+static std::string transformToString(const uint32_t transform) {
+    if (transform == Transform::IDENTITY) {
+        return "IDENTITY";
+    }
+
+    if (transform == Transform::UNKNOWN) {
+        return "UNKNOWN";
+    }
+
+    std::string out;
+    if (transform & Transform::SCALE) out.append("SCALE ");
+    if (transform & Transform::ROTATE) out.append("ROTATE ");
+    if (transform & Transform::TRANSLATE) out.append("TRANSLATE");
+    return out;
+}
+
+void Transform::dump(std::string& out, const char* name, const char* prefix) const {
     using android::base::StringAppendF;
 
     type(); // Ensure the information in mType is up to date
@@ -429,40 +496,34 @@
     const uint32_t type = mType;
     const uint32_t orient = type >> 8;
 
-    StringAppendF(&out, "%s 0x%08x (", name, orient);
+    out += prefix;
+    out += name;
+    out += " ";
 
     if (orient & ROT_INVALID) {
-        out.append("ROT_INVALID ");
-    } else {
-        if (orient & ROT_90) {
-            out.append("ROT_90 ");
-        } else {
-            out.append("ROT_0 ");
-        }
-        if (orient & FLIP_V) out.append("FLIP_V ");
-        if (orient & FLIP_H) out.append("FLIP_H ");
+        StringAppendF(&out, "0x%08x ", orient);
+    }
+    out += "(" + rotationToString(orient) + ") ";
+
+    if (type & UNKNOWN) {
+        StringAppendF(&out, "0x%02x ", type);
+    }
+    out += "(" + transformToString(type) + ")\n";
+
+    if (type == IDENTITY) {
+        return;
     }
 
-    StringAppendF(&out, ") 0x%02x (", type);
-
-    if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
-    if (type & SCALE) out.append("SCALE ");
-    if (type & ROTATE) out.append("ROTATE ");
-    if (type & TRANSLATE) out.append("TRANSLATE ");
-
-    out.append(")\n");
-
     for (size_t i = 0; i < 3; i++) {
-        StringAppendF(&out, "    %.4f  %.4f  %.4f\n", static_cast<double>(mMatrix[0][i]),
+        StringAppendF(&out, "%s    %.4f  %.4f  %.4f\n", prefix, static_cast<double>(mMatrix[0][i]),
                       static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
     }
 }
 
-void Transform::dump(const char* name) const {
+void Transform::dump(const char* name, const char* prefix) const {
     std::string out;
-    dump(out, name);
+    dump(out, name, prefix);
     ALOGD("%s", out.c_str());
 }
 
-}  // namespace ui
-}  // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/BlurRegion.h
similarity index 67%
rename from libs/ui/include/ui/PhysicalDisplayId.h
rename to libs/ui/include/ui/BlurRegion.h
index 1a345ac..c5a5d47 100644
--- a/libs/ui/include/ui/PhysicalDisplayId.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,17 +16,21 @@
 
 #pragma once
 
-#include <cinttypes>
-#include <cstdint>
-
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
+#include <inttypes.h>
 
 namespace android {
 
-using PhysicalDisplayId = uint64_t;
+struct BlurRegion {
+    uint32_t blurRadius;
+    float cornerRadiusTL;
+    float cornerRadiusTR;
+    float cornerRadiusBL;
+    float cornerRadiusBR;
+    float alpha;
+    int left;
+    int top;
+    int right;
+    int bottom;
+};
 
-constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
-    return static_cast<uint8_t>(displayId);
-}
-
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 4685575..18cd487 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -34,5 +34,4 @@
 std::string decodeColorTransform(android_color_transform colorTransform);
 std::string decodePixelFormat(android::PixelFormat format);
 std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
-std::string to_string(const android::Rect& rect);
 std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
new file mode 100644
index 0000000..f196ab9
--- /dev/null
+++ b/libs/ui/include/ui/DisplayId.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace android {
+
+// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+// The encoding of the ID is type-specific for bits 0 to 61.
+struct DisplayId {
+    // Flag indicating that the display is virtual.
+    static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
+
+    // Flag indicating that the ID is stable across reboots.
+    static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
+    // TODO(b/162612135) Remove default constructor
+    DisplayId() = default;
+    constexpr DisplayId(const DisplayId&) = default;
+    DisplayId& operator=(const DisplayId&) = default;
+
+    uint64_t value;
+
+protected:
+    explicit constexpr DisplayId(uint64_t id) : value(id) {}
+};
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+    return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+    return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+    return std::to_string(displayId.value);
+}
+
+// DisplayId of a physical display, such as the internal display or externally connected display.
+struct PhysicalDisplayId : DisplayId {
+    static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+        if (id.value & FLAG_VIRTUAL) {
+            return std::nullopt;
+        }
+        return {PhysicalDisplayId(id)};
+    }
+
+    // Returns a stable ID based on EDID information.
+    static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
+                                                uint32_t modelHash) {
+        return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash);
+    }
+
+    // Returns an unstable ID. If EDID is available using "fromEdid" is preferred.
+    static constexpr PhysicalDisplayId fromPort(uint8_t port) {
+        constexpr uint16_t kManufacturerId = 0;
+        constexpr uint32_t kModelHash = 0;
+        return PhysicalDisplayId(0, port, kManufacturerId, kModelHash);
+    }
+
+    // TODO(b/162612135) Remove default constructor
+    PhysicalDisplayId() = default;
+    // TODO(b/162612135) Remove constructor
+    explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
+
+    constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
+
+    constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
+
+private:
+    constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
+                                uint32_t modelHash)
+          : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
+                      (static_cast<uint64_t>(modelHash) << 8) | port) {}
+
+    explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
+
+struct VirtualDisplayId : DisplayId {
+    using BaseId = uint32_t;
+    // Flag indicating that this virtual display is backed by the GPU.
+    static constexpr uint64_t FLAG_GPU = 1ULL << 61;
+
+    static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
+        if (id.value & FLAG_VIRTUAL) {
+            return {VirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+protected:
+    constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
+          : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
+
+    explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+struct HalVirtualDisplayId : VirtualDisplayId {
+    explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+
+    static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
+        if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
+            return {HalVirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+private:
+    explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+struct GpuVirtualDisplayId : VirtualDisplayId {
+    explicit constexpr GpuVirtualDisplayId(BaseId baseId)
+          : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+
+    static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
+        if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
+            return {GpuVirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+private:
+    explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+// HalDisplayId is the ID of a display which is managed by HWC.
+// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId.
+struct HalDisplayId : DisplayId {
+    constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
+    constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
+
+    static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
+        if (GpuVirtualDisplayId::tryCast(id)) {
+            return std::nullopt;
+        }
+        return {HalDisplayId(id)};
+    }
+
+private:
+    explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
+
+} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+    size_t operator()(android::DisplayId displayId) const {
+        return hash<uint64_t>()(displayId.value);
+    }
+};
+
+template <>
+struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalDisplayId> : hash<android::DisplayId> {};
+
+} // namespace std
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 64efc84..70a0d50 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -32,7 +32,7 @@
 struct DisplayState {
     LayerStack layerStack = NO_LAYER_STACK;
     Rotation orientation = ROTATION_0;
-    Size viewport;
+    Size layerStackSpaceRect;
 };
 
 static_assert(std::is_trivially_copyable_v<DisplayState>);
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 013505a..57be686 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -260,6 +260,9 @@
 
     uint64_t mId;
 
+    // Unused, but removing this may break GSI.
+    int32_t mBufferId = -1;
+
     // Stores the generation number of this buffer. If this number does not
     // match the BufferQueue's internal generation number (set through
     // IGBP::setGenerationNumber), attempts to attach the buffer will fail.
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index 1152cc5..22274a2 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -50,6 +50,7 @@
     JPEG = 0x100,
     DEPTH_POINT_CLOUD = 0x101,
     RAW_DEPTH = 0x1002, // @hide
+    RAW_DEPTH10 = 0x1003, // @hide
     YV12 = 0x32315659,
     Y8 = 0x20203859,
     Y16 = 0x20363159, // @hide
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2f2229e..58323e5 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -19,10 +19,10 @@
 
 #include <ostream>
 
+#include <log/log.h>
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
-#include <log/log.h>
 
 #include <ui/FloatRect.h>
 #include <ui/Point.h>
@@ -202,6 +202,15 @@
     // the input.
     Rect transform(uint32_t xform, int32_t width, int32_t height) const;
 
+    Rect scale(float scaleX, float scaleY) const {
+        return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY));
+    }
+
+    Rect& scaleSelf(float scaleX, float scaleY) {
+        set(scale(scaleX, scaleY));
+        return *this;
+    }
+
     // this calculates (Region(*this) - exclude).bounds() efficiently
     Rect reduce(const Rect& exclude) const;
 
@@ -216,11 +225,10 @@
     }
 };
 
+std::string to_string(const android::Rect& rect);
+
 // Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
-    *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
-        << ")";
-}
+void PrintTo(const Rect& rect, ::std::ostream* os);
 
 ANDROID_BASIC_TYPES_TRAITS(Rect)
 
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 89008f6..83d431d 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -41,6 +41,15 @@
     return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
 }
 
+constexpr Rotation operator-(Rotation lhs, Rotation rhs) {
+    constexpr auto N = toRotationInt(ROTATION_270) + 1;
+    return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N);
+}
+
+constexpr Rotation operator-(Rotation rotation) {
+    return ROTATION_0 - rotation;
+}
+
 constexpr const char* toCString(Rotation rotation) {
     switch (rotation) {
         case ROTATION_0:
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index c6bb598..a197b3b 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -18,10 +18,10 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <array>
 #include <ostream>
 #include <string>
 
-#include <hardware/hardware.h>
 #include <math/mat4.h>
 #include <math/vec2.h>
 #include <math/vec3.h>
@@ -44,9 +44,9 @@
 
     enum RotationFlags : uint32_t {
         ROT_0 = 0,
-        FLIP_H = HAL_TRANSFORM_FLIP_H,
-        FLIP_V = HAL_TRANSFORM_FLIP_V,
-        ROT_90 = HAL_TRANSFORM_ROT_90,
+        FLIP_H = 1, // HAL_TRANSFORM_FLIP_H
+        FLIP_V = 2, // HAL_TRANSFORM_FLIP_V
+        ROT_90 = 4, // HAL_TRANSFORM_ROT_90
         ROT_180 = FLIP_H | FLIP_V,
         ROT_270 = ROT_180 | ROT_90,
         ROT_INVALID = 0x80
@@ -61,32 +61,43 @@
     };
 
     // query the transform
-    bool        preserveRects() const;
-    uint32_t    getType() const;
-    uint32_t    getOrientation() const;
+    bool preserveRects() const;
+
+    // Returns if bilinear filtering is needed after applying this transform to avoid aliasing.
+    bool needsBilinearFiltering() const;
+
+    uint32_t getType() const;
+    uint32_t getOrientation() const;
     bool operator==(const Transform& other) const;
 
     const vec3& operator [] (size_t i) const;  // returns column i
     float   tx() const;
     float   ty() const;
-    float   sx() const;
-    float   sy() const;
+    float dsdx() const;
+    float dtdx() const;
+    float dtdy() const;
+    float dsdy() const;
+
+    float getScaleX() const;
+    float getScaleY() const;
 
     // modify the transform
     void        reset();
     void        set(float tx, float ty);
     void        set(float a, float b, float c, float d);
     status_t    set(uint32_t flags, float w, float h);
+    void        set(const std::array<float, 9>& matrix);
 
     // transform data
     Rect    makeBounds(int w, int h) const;
-    vec2    transform(int x, int y) const;
+    vec2    transform(float x, float y) const;
     Region  transform(const Region& reg) const;
     Rect    transform(const Rect& bounds,
                       bool roundOutwards = false) const;
     FloatRect transform(const FloatRect& bounds) const;
     Transform& operator = (const Transform& other);
     Transform operator * (const Transform& rhs) const;
+    Transform operator * (float value) const;
     // assumes the last row is < 0 , 0 , 1 >
     vec2 transform(const vec2& v) const;
     vec3 transform(const vec3& v) const;
@@ -97,10 +108,10 @@
     Transform inverse() const;
 
     // for debugging
-    void dump(std::string& result, const char* name) const;
-    void dump(const char* name) const;
+    void dump(std::string& result, const char* name, const char* prefix = "") const;
+    void dump(const char* name, const char* prefix = "") const;
 
-    static RotationFlags toRotationFlags(Rotation);
+    static constexpr RotationFlags toRotationFlags(Rotation);
 
 private:
     struct mat33 {
@@ -125,7 +136,7 @@
     *os << out;
 }
 
-inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
     switch (rotation) {
         case ROTATION_0:
             return ROT_0;
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h
deleted file mode 100644
index d1d6014..0000000
--- a/libs/ui/include/ui/UiConfig.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UI_CONFIG_H
-#define ANDROID_UI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libui configuration details to configStr.
-void appendUiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_UI_CONFIG_H*/
diff --git a/libs/ui/include_vndk/ui/DisplayId.h b/libs/ui/include_vndk/ui/DisplayId.h
new file mode 120000
index 0000000..73c9fe8
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayId.h
@@ -0,0 +1 @@
+../../include/ui/DisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
deleted file mode 120000
index 6e3fb1e..0000000
--- a/libs/ui/include_vndk/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/UiConfig.h b/libs/ui/include_vndk/ui/UiConfig.h
deleted file mode 120000
index f580ce1..0000000
--- a/libs/ui/include_vndk/ui/UiConfig.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/UiConfig.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 28ef77a..d005ce8 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
 }
 
 cc_test {
+    name: "DisplayId_test",
+    shared_libs: ["libui"],
+    srcs: ["DisplayId_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "FlattenableHelpers_test",
     shared_libs: ["libui"],
     srcs: ["FlattenableHelpers_test.cpp"],
@@ -85,6 +92,14 @@
 }
 
 cc_test {
+    name: "Rect_test",
+    test_suites: ["device-tests"],
+    shared_libs: ["libui"],
+    srcs: ["Rect_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "Size_test",
     test_suites: ["device-tests"],
     shared_libs: ["libui"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
new file mode 100644
index 0000000..1d908b8
--- /dev/null
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/DisplayId.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(DisplayIdTest, createPhysicalIdFromEdid) {
+    constexpr uint8_t port = 1;
+    constexpr uint16_t manufacturerId = 13;
+    constexpr uint32_t modelHash = 42;
+    PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+    EXPECT_EQ(port, id.getPort());
+    EXPECT_EQ(manufacturerId, id.getManufacturerId());
+    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+    EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createPhysicalIdFromPort) {
+    constexpr uint8_t port = 3;
+    PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
+    EXPECT_EQ(port, id.getPort());
+    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+    EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createGpuVirtualId) {
+    GpuVirtualDisplayId id(42);
+    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+    EXPECT_FALSE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createHalVirtualId) {
+    HalVirtualDisplayId id(42);
+    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+    EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
new file mode 100644
index 0000000..5499a5b
--- /dev/null
+++ b/libs/ui/tests/Rect_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <system/graphics.h>
+#include <ui/FloatRect.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(RectTest, constructDefault) {
+    const Rect rect;
+    EXPECT_FALSE(rect.isValid());
+    EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, constructFromWidthAndHeight) {
+    const Rect rect(100, 200);
+    EXPECT_TRUE(rect.isValid());
+    EXPECT_FALSE(rect.isEmpty());
+    EXPECT_EQ(0, rect.top);
+    EXPECT_EQ(0, rect.left);
+    EXPECT_EQ(100, rect.right);
+    EXPECT_EQ(200, rect.bottom);
+    EXPECT_EQ(100, rect.getWidth());
+    EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromSize) {
+    const Rect rect(Size(100, 200));
+    EXPECT_TRUE(rect.isValid());
+    EXPECT_FALSE(rect.isEmpty());
+    EXPECT_EQ(0, rect.top);
+    EXPECT_EQ(0, rect.left);
+    EXPECT_EQ(100, rect.right);
+    EXPECT_EQ(200, rect.bottom);
+    EXPECT_EQ(100, rect.getWidth());
+    EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromLTRB) {
+    const Rect rect(11, 12, 14, 14);
+    EXPECT_TRUE(rect.isValid());
+    EXPECT_FALSE(rect.isEmpty());
+    EXPECT_EQ(11, rect.left);
+    EXPECT_EQ(12, rect.top);
+    EXPECT_EQ(14, rect.right);
+    EXPECT_EQ(14, rect.bottom);
+    EXPECT_EQ(3, rect.getWidth());
+    EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromPoints) {
+    const Rect rect(Point(11, 12), Point(14, 14));
+    EXPECT_TRUE(rect.isValid());
+    EXPECT_FALSE(rect.isEmpty());
+    EXPECT_EQ(11, rect.left);
+    EXPECT_EQ(12, rect.top);
+    EXPECT_EQ(14, rect.right);
+    EXPECT_EQ(14, rect.bottom);
+    EXPECT_EQ(3, rect.getWidth());
+    EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromFloatRect) {
+    {
+        const Rect rect(FloatRect(10, 20, 30, 40));
+        EXPECT_TRUE(rect.isValid());
+        EXPECT_FALSE(rect.isEmpty());
+        EXPECT_EQ(10, rect.left);
+        EXPECT_EQ(20, rect.top);
+        EXPECT_EQ(30, rect.right);
+        EXPECT_EQ(40, rect.bottom);
+    }
+    // Construct with floating point error
+    {
+        constexpr float kError = 1e-3;
+        const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError));
+        EXPECT_TRUE(rect.isValid());
+        EXPECT_FALSE(rect.isEmpty());
+        EXPECT_EQ(10, rect.left);
+        EXPECT_EQ(20, rect.top);
+        EXPECT_EQ(30, rect.right);
+        EXPECT_EQ(40, rect.bottom);
+    }
+}
+
+TEST(RectTest, makeInvalid) {
+    Rect rect(10, 20, 60, 60);
+    EXPECT_TRUE(rect.isValid());
+    rect.makeInvalid();
+    EXPECT_FALSE(rect.isValid());
+}
+
+TEST(RectTest, clear) {
+    Rect rect(10, 20, 60, 60);
+    EXPECT_FALSE(rect.isEmpty());
+    rect.clear();
+    EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, getSize) {
+    const Rect rect(10, 20, 60, 60);
+    EXPECT_EQ(Size(50, 40), rect.getSize());
+}
+
+TEST(RectTest, getBounds) {
+    const Rect rect(10, 20, 60, 60);
+    const Rect bounds = rect.getBounds();
+    EXPECT_EQ(0, bounds.left);
+    EXPECT_EQ(0, bounds.top);
+    EXPECT_EQ(50, bounds.right);
+    EXPECT_EQ(40, bounds.bottom);
+    EXPECT_EQ(rect.getSize(), bounds.getSize());
+}
+
+TEST(RectTest, getCornerPoints) {
+    const Rect rect(10, 20, 50, 60);
+    EXPECT_EQ(Point(10, 20), rect.leftTop());
+    EXPECT_EQ(Point(10, 60), rect.leftBottom());
+    EXPECT_EQ(Point(50, 20), rect.rightTop());
+    EXPECT_EQ(Point(50, 60), rect.rightBottom());
+}
+
+TEST(RectTest, operatorEquals) {
+    const Rect rect(10, 20, 50, 60);
+    EXPECT_EQ(rect, rect);
+    EXPECT_NE(Rect(0, 20, 50, 60), rect);
+    EXPECT_NE(Rect(10, 0, 50, 60), rect);
+    EXPECT_NE(Rect(10, 20, 0, 60), rect);
+    EXPECT_NE(Rect(10, 20, 50, 0), rect);
+}
+
+TEST(RectTest, operatorsPlusMinus) {
+    Rect rect = Rect(10, 20, 50, 60) + Point(1, 2);
+    EXPECT_EQ(Rect(11, 22, 51, 62), rect);
+    rect -= Point(1, 2);
+    EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+
+    rect = Rect(10, 20, 50, 60) - Point(1, 2);
+    EXPECT_EQ(Rect(9, 18, 49, 58), rect);
+    rect += Point(1, 2);
+    EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+}
+
+TEST(RectTest, scale) {
+    Rect rect(10, 20, 50, 60);
+    EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f));
+    rect.scaleSelf(2.f, 3.f);
+    EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+
+    rect = Rect(10, 20, 50, 60);
+    constexpr float kError = 1e-3;
+    EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError));
+    rect.scaleSelf(2.f - kError, 3.f - kError);
+    EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+}
+
+TEST(RectTest, inset) {
+    Rect rect(10, 20, 50, 60);
+    rect.inset(0, 0, 0, 0);
+    EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+    rect.inset(1, 2, 3, 4);
+    EXPECT_EQ(Rect(11, 22, 47, 56), rect);
+}
+
+TEST(RectTest, intersect) {
+    const Rect rect(10, 20, 50, 60);
+    Rect intersection;
+
+    // Intersect with self is self
+    intersection.makeInvalid();
+    EXPECT_TRUE(rect.intersect(rect, &intersection));
+    EXPECT_EQ(Rect(10, 20, 50, 60), intersection);
+
+    // Intersect with rect contained in us
+    const Rect insideRect(11, 21, 45, 55);
+    intersection.makeInvalid();
+    EXPECT_TRUE(rect.intersect(insideRect, &intersection));
+    EXPECT_EQ(insideRect, intersection);
+
+    // Intersect with rect we are contained in
+    intersection.makeInvalid();
+    EXPECT_TRUE(insideRect.intersect(rect, &intersection));
+    EXPECT_EQ(insideRect, intersection);
+
+    // Empty intersection
+    intersection.makeInvalid();
+    EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection));
+    EXPECT_TRUE(intersection.isEmpty());
+
+    // Partial intersection
+    const Rect other(30, 40, 70, 80);
+    intersection.makeInvalid();
+    EXPECT_TRUE(rect.intersect(other, &intersection));
+    EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+
+    // Intersetion is commutative
+    intersection.makeInvalid();
+    EXPECT_TRUE(other.intersect(rect, &intersection));
+    EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+}
+
+TEST(RectTest, reduce) {
+    const Rect rect(10, 20, 50, 60);
+
+    // Reduce with self is empty
+    EXPECT_TRUE(rect.reduce(rect).isEmpty());
+
+    // Reduce with rect entirely inside is a noop
+    const Rect insideRect(11, 21, 45, 55);
+    EXPECT_EQ(rect, rect.reduce(insideRect));
+
+    // Reduce with rect entirely outside is empty
+    EXPECT_TRUE(insideRect.reduce(rect).isEmpty());
+
+    // Reduce with rect on the right
+    EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70)));
+
+    // Reduce with rect on the left
+    EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70)));
+
+    // Reduce with rect at the top
+    EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40)));
+
+    // Reduce with rect at the bottom
+    EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70)));
+}
+
+TEST(RectTest, transform) {
+    const int32_t width = 100, height = 200;
+    const Rect rect(1, 1, 2, 3);
+    EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height));
+    EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height));
+    EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height));
+    EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height));
+    EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height));
+}
+
+TEST(RectTest, toFloatRect) {
+    const Rect rect(10, 20, 50, 60);
+    const FloatRect floatRect = rect.toFloatRect();
+    EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 38f37ad..5f75aea 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -33,8 +33,7 @@
 
 #include <gtest/gtest.h>
 
-namespace android {
-namespace ui {
+namespace android::ui {
 
 TEST(SizeTest, BasicConstructionAndEqualityComparison) {
     Size s(123, 456);
@@ -215,5 +214,4 @@
     ClampTest(uint32_t(0), int32_t(0));
 }
 
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
index 7fcd7de..eece18e 100644
--- a/libs/ui/tests/TEST_MAPPING
+++ b/libs/ui/tests/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "Size_test"
+    },
+    {
+      "name": "Rect_test"
     }
   ]
 }
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index e95a080..1681fe2 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -14,6 +14,8 @@
 
 cc_library_shared {
     name: "libvibrator",
+    vendor_available: true,
+    double_loadable: true,
 
     shared_libs: [
         "libbinder",
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
new file mode 100644
index 0000000..749c568
--- /dev/null
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cstring>
+
+#include <math.h>
+
+#include <vibrator/ExternalVibrationUtils.h>
+
+namespace android::os {
+
+namespace {
+static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
+static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
+static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+
+float getHapticScaleGamma(HapticScale scale) {
+    switch (scale) {
+    case HapticScale::VERY_LOW:
+        return 2.0f;
+    case HapticScale::LOW:
+        return 1.5f;
+    case HapticScale::HIGH:
+        return 0.5f;
+    case HapticScale::VERY_HIGH:
+        return 0.25f;
+    default:
+        return 1.0f;
+    }
+}
+
+float getHapticMaxAmplitudeRatio(HapticScale scale) {
+    switch (scale) {
+    case HapticScale::VERY_LOW:
+        return HAPTIC_SCALE_VERY_LOW_RATIO;
+    case HapticScale::LOW:
+        return HAPTIC_SCALE_LOW_RATIO;
+    case HapticScale::NONE:
+    case HapticScale::HIGH:
+    case HapticScale::VERY_HIGH:
+        return 1.0f;
+    default:
+        return 0.0f;
+    }
+}
+
+} // namespace
+
+bool isValidHapticScale(HapticScale scale) {
+    switch (scale) {
+    case HapticScale::MUTE:
+    case HapticScale::VERY_LOW:
+    case HapticScale::LOW:
+    case HapticScale::NONE:
+    case HapticScale::HIGH:
+    case HapticScale::VERY_HIGH:
+        return true;
+    }
+    return false;
+}
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale) {
+    if (!isValidHapticScale(scale) || scale == HapticScale::NONE) {
+        return;
+    }
+    if (scale == HapticScale::MUTE) {
+        memset(buffer, 0, length * sizeof(float));
+        return;
+    }
+    float gamma = getHapticScaleGamma(scale);
+    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+    for (size_t i = 0; i < length; i++) {
+        float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+        buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+                * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+    }
+}
+
+} // namespace android::os
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
new file mode 100644
index 0000000..20045d0
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
+#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+
+#include <android/os/IExternalVibratorService.h>
+
+namespace android::os {
+
+enum class HapticScale {
+    MUTE = IExternalVibratorService::SCALE_MUTE,
+    VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
+    LOW = IExternalVibratorService::SCALE_LOW,
+    NONE = IExternalVibratorService::SCALE_NONE,
+    HIGH = IExternalVibratorService::SCALE_HIGH,
+    VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+};
+
+bool isValidHapticScale(HapticScale scale);
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale);
+
+} // namespace android::os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 2fcee7b..37c19d4 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -16,6 +16,12 @@
     name: "libbufferhub_headers",
     export_include_dirs: ["include"],
     vendor_available: true,  // TODO(b/112338314): Does shouldn't be available to vendor.
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 }
 
 sourceFiles = [
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 8cc7081..fab1097 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,7 +108,7 @@
   void ConnectProducer() {
     IGraphicBufferProducer::QueueBufferOutput output;
     // Can connect the first time.
-    ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+    ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi,
                                      kTestControlledByApp, &output));
   }
 
@@ -140,7 +140,7 @@
     return QueueBufferInputBuilder().build();
   }
 
-  const sp<IProducerListener> kDummyListener{new DummyProducerListener};
+  const sp<IProducerListener> kStubListener{new StubProducerListener};
 
   sp<BufferHubProducer> mProducer;
   sp<Surface> mSurface;
@@ -150,11 +150,11 @@
   IGraphicBufferProducer::QueueBufferOutput output;
 
   // NULL output returns BAD_VALUE
-  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
                                           kTestControlledByApp, nullptr));
 
   // Invalid API returns bad value
-  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid,
                                           kTestControlledByApp, &output));
 }
 
@@ -163,7 +163,7 @@
 
   // Can't connect when there is already a producer connected.
   IGraphicBufferProducer::QueueBufferOutput output;
-  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
                                           kTestControlledByApp, &output));
 }
 
@@ -554,18 +554,18 @@
   ProducerQueueParcelable producer_parcelable;
   EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE);
 
-  // Create a valid dummy producer parcelable.
-  auto dummy_channel_parcelable =
+  // Create a valid fake producer parcelable.
+  auto fake_channel_parcelable =
       std::make_unique<pdx::default_transport::ChannelParcelable>(
           LocalHandle(0), LocalHandle(0), LocalHandle(0));
-  EXPECT_TRUE(dummy_channel_parcelable->IsValid());
-  ProducerQueueParcelable dummy_producer_parcelable(
-      std::move(dummy_channel_parcelable));
-  EXPECT_TRUE(dummy_producer_parcelable.IsValid());
+  EXPECT_TRUE(fake_channel_parcelable->IsValid());
+  ProducerQueueParcelable fake_producer_parcelable(
+      std::move(fake_channel_parcelable));
+  EXPECT_TRUE(fake_producer_parcelable.IsValid());
 
   // Disconnect producer can be taken out, but only to an invalid parcelable.
   ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
-  EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
+  EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE);
   EXPECT_FALSE(producer_parcelable.IsValid());
   EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
   EXPECT_TRUE(producer_parcelable.IsValid());
@@ -583,7 +583,7 @@
 
   // But connect to API will fail.
   IGraphicBufferProducer::QueueBufferOutput output;
-  EXPECT_EQ(mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp,
+  EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp,
                                &output),
             BAD_VALUE);
 
@@ -592,8 +592,8 @@
   sp<BufferHubProducer> new_producer =
       BufferHubProducer::Create(std::move(producer_parcelable));
   ASSERT_TRUE(new_producer != nullptr);
-  EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
-                                  kTestControlledByApp, &output),
+  EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp,
+                                  &output),
             OK);
 }
 
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 81a9b2d..340d7bf 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -17,6 +17,12 @@
     name: "libdvr_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 }
 
 cc_library_headers {
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index 7b3717e..07e2121 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -308,14 +308,6 @@
 class DvrDisplayManagerTest : public Test {
  protected:
   void SetUp() override {
-    // dvr display manager test doesn't apply to standalone vr devices because
-    // tests cannot create display manager client on these devices.
-    if (property_get_bool("ro.boot.vr", false)) {
-      GTEST_SKIP()
-          << "All tests in DvrDisplayManagerTest test case are skipped "
-             "because the device boot to VR.";
-    }
-
     int ret;
     DvrDisplayManager* display_manager;
     DvrSurfaceState* surface_state;
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
index b7d94b3..7b477c4 100644
--- a/libs/vr/libpdx/encoder_performance_test.cpp
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -158,12 +158,12 @@
                                            size_t iterations,
                                            ResetFunc* write_reset,
                                            void* reset_data, size_t data_size) {
-  std::vector<uint8_t> dummy_data(data_size);
+  std::vector<uint8_t> fake_data(data_size);
   auto start = std::chrono::high_resolution_clock::now();
   for (size_t i = 0; i < iterations; i++) {
     write_reset(reset_data);
-    memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
-           dummy_data.data(), dummy_data.size());
+    memcpy(writer->GetNextWriteBufferSection(fake_data.size()),
+           fake_data.data(), fake_data.size());
   }
   auto stop = std::chrono::high_resolution_clock::now();
   return stop - start;
@@ -177,17 +177,17 @@
     MessageReader* reader, MessageWriter* writer, size_t iterations,
     ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
     size_t data_size) {
-  std::vector<uint8_t> dummy_data(data_size);
+  std::vector<uint8_t> fake_data(data_size);
   write_reset(reset_data);
-  memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
-         dummy_data.data(), dummy_data.size());
+  memcpy(writer->GetNextWriteBufferSection(fake_data.size()), fake_data.data(),
+         fake_data.size());
   auto start = std::chrono::high_resolution_clock::now();
   for (size_t i = 0; i < iterations; i++) {
     read_reset(reset_data);
     auto section = reader->GetNextReadBufferSection();
-    memcpy(dummy_data.data(), section.first, dummy_data.size());
+    memcpy(fake_data.data(), section.first, fake_data.size());
     reader->ConsumeReadBufferSectionData(
-        AdvancePointer(section.first, dummy_data.size()));
+        AdvancePointer(section.first, fake_data.size()));
   }
   auto stop = std::chrono::high_resolution_clock::now();
   return stop - start;
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
new file mode 100644
index 0000000..b36e0de
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -0,0 +1,62 @@
+cc_fuzz {
+    name: "libpdx_service_dispatcher_fuzzer",
+    clang: true,
+    srcs: [
+        "service_dispatcher_fuzzer.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpdx",
+    ],
+    shared_libs: [
+        "libutils",
+        "liblog",
+        "libcutils"
+    ],
+}
+
+cc_fuzz {
+    name: "libpdx_message_fuzzer",
+    clang: true,
+    srcs: [
+        "message_fuzzer.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpdx",
+    ],
+    shared_libs: [
+        "libutils",
+        "liblog",
+        "libcutils"
+    ],
+}
+
+cc_fuzz {
+    name: "libpdx_serialization_fuzzer",
+    clang: true,
+    srcs: [
+        "serialization_fuzzer.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpdx",
+    ],
+    shared_libs: [
+        "libutils",
+        "liblog",
+        "libcutils"
+    ],
+}
diff --git a/libs/vr/libpdx/fuzz/helpers.h b/libs/vr/libpdx/fuzz/helpers.h
new file mode 100644
index 0000000..83ec409
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/helpers.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          brian.balling@leviathansecurity.com
+
+#ifndef LEV_FUZZERS_LIBPDX_HELPERS_H_
+#define LEV_FUZZERS_LIBPDX_HELPERS_H_
+
+#define UNUSED(expr) \
+  do {               \
+    (void)(expr);    \
+  } while (0)
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <pdx/service_endpoint.h>
+#include <sys/eventfd.h>
+#include <memory>
+#include <vector>
+
+using namespace android::pdx;
+
+// Vector of operations we can call in the dispatcher.
+static const std::vector<std::function<void(
+    const std::unique_ptr<ServiceDispatcher>&, FuzzedDataProvider*)>>
+    dispatcher_operations = {
+        [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+           FuzzedDataProvider*) -> void { dispatcher->EnterDispatchLoop(); },
+        [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+           FuzzedDataProvider*) -> void { dispatcher->ReceiveAndDispatch(); },
+        [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+           FuzzedDataProvider* fdp) -> void {
+          dispatcher->ReceiveAndDispatch(fdp->ConsumeIntegral<int>());
+        }};
+
+// Most of the fuzzing occurs within the endpoint, which is derived from an
+// abstract class. So we are returning garbage data for most functions besides
+// the ones we added or need to actually use.
+class FuzzEndpoint : public Endpoint {
+ public:
+  explicit FuzzEndpoint(FuzzedDataProvider* fdp) {
+    _fdp = fdp;
+    _epoll_fd = eventfd(0, 0);
+  }
+
+  ~FuzzEndpoint() { close(_epoll_fd); }
+
+  // Returns an fd that can be used with epoll() to wait for incoming messages
+  // from this endpoint.
+  int epoll_fd() const { return _epoll_fd; }
+
+  // Associates a Service instance with an endpoint by setting the service
+  // context pointer to the address of the Service. Only one Service may be
+  // associated with a given endpoint.
+  Status<void> SetService(Service* service) {
+    _service = service;
+    return Status<void>(0);
+  }
+
+  // Set the channel context for the given channel.
+  Status<void> SetChannel(int channel_id, Channel* channel) {
+    UNUSED(channel_id);
+    _channel = std::shared_ptr<Channel>(channel);
+    return Status<void>(0);
+  }
+
+  // Receives a message on the given endpoint file descriptor.
+  // This is called by the dispatcher to determine what operations
+  // to make, so we are fuzzing the response.
+  Status<void> MessageReceive(Message* message) {
+    // Create a randomized MessageInfo struct.
+    MessageInfo info;
+    eventfd_t wakeup_val = 0;
+    info.pid = _fdp->ConsumeIntegral<int>();
+    info.tid = _fdp->ConsumeIntegral<int>();
+    info.cid = _fdp->ConsumeIntegral<int>();
+    info.mid = _fdp->ConsumeIntegral<int>();
+    info.euid = _fdp->ConsumeIntegral<int>();
+    info.egid = _fdp->ConsumeIntegral<int>();
+    info.op = _fdp->ConsumeIntegral<int32_t>();
+    info.flags = _fdp->ConsumeIntegral<uint32_t>();
+    info.service = _service;
+    info.channel = _channel.get();
+    info.send_len = _fdp->ConsumeIntegral<size_t>();
+    info.recv_len = _fdp->ConsumeIntegral<size_t>();
+    info.fd_count = _fdp->ConsumeIntegral<size_t>();
+    if (_fdp->remaining_bytes() >= 32) {
+      std::vector<uint8_t> impulse_vec = _fdp->ConsumeBytes<uint8_t>(32);
+      memcpy(info.impulse, impulse_vec.data(), 32);
+    }
+
+    *message = Message(info);
+    eventfd_read(_epoll_fd, &wakeup_val);
+
+    return Status<void>();
+  }
+
+  // Returns a tag that uniquely identifies a specific underlying IPC
+  // transport.
+  uint32_t GetIpcTag() const { return 0; }
+
+  // Close a channel, signaling the client file object and freeing the channel
+  // id. Once closed, the client side of the channel always returns the error
+  // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+  Status<void> CloseChannel(int channel_id) {
+    UNUSED(channel_id);
+    return Status<void>();
+  }
+
+  // Update the event bits for the given channel (given by id), using the
+  // given clear and set masks.
+  Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+                                   int set_mask) {
+    UNUSED(channel_id);
+    UNUSED(clear_mask);
+    UNUSED(set_mask);
+    return Status<void>();
+  }
+
+  // Create a new channel and push it as a file descriptor to the process
+  // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+  // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+  // sending process may change these later using fcntl()). The internal
+  // Channel instance associated with this channel is set to |channel|,
+  // which may be nullptr. The new channel id allocated for this channel is
+  // returned in |channel_id|, which may also be nullptr if not needed.
+  Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+                                          Channel* channel, int* channel_id) {
+    UNUSED(message);
+    UNUSED(flags);
+    UNUSED(channel);
+    UNUSED(channel_id);
+    return Status<RemoteChannelHandle>();
+  }
+
+  // Check whether the |ref| is a reference to a channel to the service
+  // represented by the |endpoint|. If the channel reference in question is
+  // valid, the Channel object is returned in |channel| when non-nullptr and
+  // the channel ID is returned through the Status object.
+  Status<int> CheckChannel(const Message* message, ChannelReference ref,
+                           Channel** channel) {
+    UNUSED(message);
+    UNUSED(ref);
+    UNUSED(channel);
+    return Status<int>();
+  }
+
+  // Replies to the message with a return code.
+  Status<void> MessageReply(Message* message, int return_code) {
+    UNUSED(message);
+    UNUSED(return_code);
+    return Status<void>();
+  }
+
+  // Replies to the message with a file descriptor.
+  Status<void> MessageReplyFd(Message* message, unsigned int push_fd) {
+    UNUSED(message);
+    UNUSED(push_fd);
+    return Status<void>();
+  }
+
+  // Replies to the message with a local channel handle.
+  Status<void> MessageReplyChannelHandle(Message* message,
+                                         const LocalChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<void>();
+  }
+
+  // Replies to the message with a borrowed local channel handle.
+  Status<void> MessageReplyChannelHandle(Message* message,
+                                         const BorrowedChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<void>();
+  }
+
+  // Replies to the message with a remote channel handle.
+  Status<void> MessageReplyChannelHandle(Message* message,
+                                         const RemoteChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<void>();
+  }
+
+  // Reads message data into an array of memory buffers.
+  Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+                                 size_t vector_length) {
+    UNUSED(message);
+    UNUSED(vector);
+    UNUSED(vector_length);
+    return Status<size_t>();
+  }
+
+  // Sends reply data for message.
+  Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+                                  size_t vector_length) {
+    UNUSED(message);
+    UNUSED(vector);
+    UNUSED(vector_length);
+    return Status<size_t>();
+  }
+
+  // Records a file descriptor into the message buffer and returns the
+  // remapped reference to be sent to the remote process.
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const LocalHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<FileReference>();
+  }
+
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const BorrowedHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<FileReference>();
+  }
+
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const RemoteHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<FileReference>();
+  }
+
+  Status<ChannelReference> PushChannelHandle(Message* message,
+                                             const LocalChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<ChannelReference>();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<ChannelReference>();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      Message* message, const RemoteChannelHandle& handle) {
+    UNUSED(message);
+    UNUSED(handle);
+    return Status<ChannelReference>();
+  }
+
+  // Obtains a file descriptor/channel handle from a message for the given
+  // reference.
+  LocalHandle GetFileHandle(Message* message, FileReference ref) const {
+    UNUSED(message);
+    UNUSED(ref);
+    return LocalHandle();
+  }
+
+  LocalChannelHandle GetChannelHandle(Message* message,
+                                      ChannelReference ref) const {
+    UNUSED(message);
+    UNUSED(ref);
+    return LocalChannelHandle();
+  }
+
+  // Transport-specific message state management.
+  void* AllocateMessageState() { return nullptr; }
+
+  void FreeMessageState(void* state) { UNUSED(state); }
+
+  // Cancels the endpoint, unblocking any receiver threads waiting for a
+  // message.
+  Status<void> Cancel() { return Status<void>(); }
+
+ private:
+  FuzzedDataProvider* _fdp;
+  std::shared_ptr<Channel> _channel;
+  Service* _service;
+  int _epoll_fd;
+};
+
+#endif  // LEV_FUZZERS_LIBPDX_HELPERS_H_
diff --git a/libs/vr/libpdx/fuzz/message_fuzzer.cpp b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
new file mode 100644
index 0000000..b627045
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Fuzzer for Message object functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+  FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+  std::shared_ptr<Service> service(
+      new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+  std::shared_ptr<Channel> channel(nullptr);
+
+  // Generate a random Message object to call functions in.
+  MessageInfo info;
+  info.pid = fdp.ConsumeIntegral<int>();
+  info.tid = fdp.ConsumeIntegral<int>();
+  info.cid = fdp.ConsumeIntegral<int>();
+  info.mid = fdp.ConsumeIntegral<int>();
+  info.euid = fdp.ConsumeIntegral<int>();
+  info.egid = fdp.ConsumeIntegral<int>();
+  info.op = fdp.ConsumeIntegral<int32_t>();
+  info.flags = fdp.ConsumeIntegral<uint32_t>();
+  info.service = service.get();
+  info.channel = channel.get();
+  info.send_len = fdp.ConsumeIntegral<size_t>();
+  info.recv_len = fdp.ConsumeIntegral<size_t>();
+  info.fd_count = fdp.ConsumeIntegral<size_t>();
+  if (fdp.remaining_bytes() >= 32) {
+    std::vector<uint8_t> impulse_vec = fdp.ConsumeBytes<uint8_t>(32);
+    memcpy(info.impulse, impulse_vec.data(), 32);
+  }
+
+  Message message = Message(info);
+
+  // A bunch of getters that probably won't do much, but might as well
+  // get coverage, while we are here.
+  message.GetProcessId();
+  message.GetThreadId();
+  message.GetEffectiveUserId();
+  message.GetEffectiveGroupId();
+  message.GetChannelId();
+  message.GetMessageId();
+  message.GetOp();
+  message.GetFlags();
+  message.GetSendLength();
+  message.GetReceiveLength();
+  message.GetFileDescriptorCount();
+  message.ImpulseEnd();
+  message.replied();
+  message.IsChannelExpired();
+  message.IsServiceExpired();
+  message.GetState();
+  message.GetState();
+
+  // Some misc. functions.
+  unsigned int fd = fdp.ConsumeIntegral<unsigned int>();
+  int clear_mask = fdp.ConsumeIntegral<int>();
+  int set_mask = fdp.ConsumeIntegral<int>();
+  Status<void> status = {};
+  message.ModifyChannelEvents(clear_mask, set_mask);
+
+  // Fuzz the handle functions.
+  LocalHandle l_handle = {};
+  BorrowedHandle b_handle = {};
+  RemoteHandle r_handle = {};
+  LocalChannelHandle lc_handle = {};
+  BorrowedChannelHandle bc_handle = {};
+  RemoteChannelHandle rc_handle = {};
+  FileReference f_ref = fdp.ConsumeIntegral<int32_t>();
+  ChannelReference c_ref = fdp.ConsumeIntegral<int32_t>();
+
+  // These don't actually modify any state in the Message or params.
+  // They can be called in any order.
+  message.PushFileHandle(b_handle);
+  message.PushFileHandle(r_handle);
+  message.PushChannelHandle(lc_handle);
+  message.PushChannelHandle(bc_handle);
+  message.PushChannelHandle(rc_handle);
+  message.GetFileHandle(f_ref, &l_handle);
+  message.GetChannelHandle(c_ref, &lc_handle);
+
+  // Can only reply once, pick at random.
+  switch (fdp.ConsumeIntegral<uint8_t>()) {
+    case 0:
+      message.ReplyFileDescriptor(fd);
+      break;
+    case 1:
+      message.Reply(status);
+      break;
+    case 2:
+      message.Reply(l_handle);
+      break;
+    case 3:
+      message.Reply(b_handle);
+      break;
+    case 4:
+      message.Reply(r_handle);
+      break;
+    case 5:
+      message.Reply(lc_handle);
+      break;
+    case 6:
+      message.Reply(bc_handle);
+      break;
+    case 7:
+      message.Reply(rc_handle);
+  }
+
+  // Fuzz the channel functions.
+  int flags = fdp.ConsumeIntegral<int>();
+  int channel_id = 0;
+  message.PushChannel(flags, channel, &channel_id);
+  message.CheckChannel(service.get(), c_ref, &channel);
+  message.CheckChannel(c_ref, &channel);
+  message.PushChannel(service.get(), flags, channel, &channel_id);
+  size_t iovec_size = sizeof(iovec);
+  struct iovec* iovecs = nullptr;
+
+  // Fuzz the read/write functions. Needs at least one iovec, plus one byte.
+  if (fdp.remaining_bytes() >= iovec_size + 1) {
+    std::vector<uint8_t> tmp_vec = fdp.ConsumeBytes<uint8_t>(iovec_size);
+    struct iovec* vector = reinterpret_cast<struct iovec*>(tmp_vec.data());
+    std::vector<uint8_t> tmp_buf =
+        fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+    void* buf = reinterpret_cast<void*>(tmp_buf.data());
+    size_t buf_size = fdp.ConsumeIntegral<size_t>();
+
+    // Capping num_vecs to 1024 so it doesn't allocate too much memory.
+    size_t num_vecs = fdp.ConsumeIntegralInRange<size_t>(0, 1024);
+
+    if (num_vecs > 0)
+      iovecs = new struct iovec[num_vecs];
+    for (size_t i = 0; i < num_vecs; i++) {
+      iovecs[i] = *vector;
+    }
+
+    message.ReadAll(vector, buf_size);
+    message.WriteAll(buf, buf_size);
+    message.ReadVectorAll(vector, num_vecs);
+    message.WriteVectorAll(vector, num_vecs);
+    message.ReadVector(vector, buf_size);
+    message.WriteVector(vector, buf_size);
+  }
+
+  if (iovecs != nullptr)
+    delete[] iovecs;
+  return 0;
+}
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
new file mode 100644
index 0000000..f5c5a5a
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          brian.balling@leviathansecurity.com
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+struct FuzzType {
+  int a;
+  float b;
+  std::string c;
+
+  FuzzType() {}
+  FuzzType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(FuzzType, a, b, c);
+};
+
+// Fuzzer for Serialization operations, this is mostly just lifted from the
+// existing test cases to use fuzzed values as inputs.
+void FuzzSerializeDeserialize(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload result;
+
+  // Currently, only fuzzing subset of types. In the future, may want
+  // to add more difficult to generate types like array, map, enum, etc...
+  bool b_val = fdp.ConsumeBool();
+  uint8_t u8_val = fdp.ConsumeIntegral<uint8_t>();
+  uint16_t u16_val = fdp.ConsumeIntegral<uint16_t>();
+  uint32_t u32_val = fdp.ConsumeIntegral<uint32_t>();
+  uint64_t u64_val = fdp.ConsumeIntegral<uint64_t>();
+  int8_t i8_val = fdp.ConsumeIntegral<int8_t>();
+  int16_t i16_val = fdp.ConsumeIntegral<uint16_t>();
+  int32_t i32_val = fdp.ConsumeIntegral<uint32_t>();
+  int64_t i64_val = fdp.ConsumeIntegral<uint64_t>();
+  float f_val = fdp.ConsumeFloatingPoint<float>();
+  double d_val = fdp.ConsumeFloatingPoint<double>();
+  std::string s_val = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+  std::vector<uint8_t> vec_val =
+      fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+  FuzzType t1_val{reinterpret_cast<int>(i32_val), f_val, s_val};
+
+  // Types need to be individually fuzzed because code path changes depending
+  // on which type is being serialized/deserialized.
+  Serialize(b_val, &result);
+  Deserialize(&b_val, &result);
+  Serialize(u8_val, &result);
+  Deserialize(&u8_val, &result);
+  Serialize(u16_val, &result);
+  Deserialize(&u16_val, &result);
+  Serialize(u32_val, &result);
+  Deserialize(&u32_val, &result);
+  Serialize(u64_val, &result);
+  Deserialize(&u64_val, &result);
+  Serialize(i8_val, &result);
+  Deserialize(&i8_val, &result);
+  Serialize(i16_val, &result);
+  Deserialize(&i16_val, &result);
+  Serialize(i32_val, &result);
+  Deserialize(&i32_val, &result);
+  Serialize(i64_val, &result);
+  Deserialize(&i64_val, &result);
+  Serialize(f_val, &result);
+  Deserialize(&f_val, &result);
+  Serialize(d_val, &result);
+  Deserialize(&d_val, &result);
+  Serialize(s_val, &result);
+  Deserialize(&s_val, &result);
+  Serialize(WrapString(s_val), &result);
+  Deserialize(&s_val, &result);
+  Serialize(vec_val, &result);
+  Deserialize(&vec_val, &result);
+  Serialize(t1_val, &result);
+  Deserialize(&t1_val, &result);
+}
+
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+  std::uint8_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::uint16_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::uint32_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {
+      ENCODING_TYPE_UINT64,           fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>()};
+  std::uint64_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+  std::int8_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int16_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int32_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT64,
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int64_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  float floatResult;
+  Deserialize(&floatResult, &buffer);
+
+  buffer.Rewind();
+  double doubleResult;
+  Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {
+      ENCODING_TYPE_FLOAT64,          fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>()};
+  double result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  std::string s_val = fdp.ConsumeRemainingBytesAsString();
+  Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+  for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+       iter++) {
+    buffer.Append(1, *iter);
+  }
+  std::string result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+  // Fill the map with the fuzzed data, not attempting to
+  // make a valid map
+  while (fdp.remaining_bytes() > 0) {
+    buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+  }
+
+  std::map<std::uint32_t, std::uint32_t> result;
+  Deserialize(&result, &buffer);
+
+  buffer.Rewind();
+  std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+  Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT16,
+                    ENCODING_TYPE_FLOAT32,
+                    ENCODING_TYPE_FIXSTR_MAX,
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  // Add the rest of the data as a string
+  std::string s_val = fdp.ConsumeRemainingBytesAsString();
+  for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+       iter++) {
+    buffer.Append(1, *iter);
+  }
+  Variant<int, float, std::string> result;
+  Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+  FuzzDeserializeUint8(data, size);
+  FuzzDeserializeUint16(data, size);
+  FuzzDeserializeUint32(data, size);
+  FuzzDeserializeUint64(data, size);
+  FuzzDeserializeInt8(data, size);
+  FuzzDeserializeInt16(data, size);
+  FuzzDeserializeInt32(data, size);
+  FuzzDeserializeInt64(data, size);
+  FuzzDeserializeFloat32(data, size);
+  FuzzDeserializeFloat64(data, size);
+  FuzzDeserializeFixstr(data, size);
+  FuzzDeserializeFixmap(data, size);
+  FuzzDeserializeVariant(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzSerializeDeserialize(data, size);
+  FuzzDeserialize(data, size);
+
+  return 0;
+}
diff --git a/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
new file mode 100644
index 0000000..3a3bfd9
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Dispatch fuzzer entry point. This fuzzer creates a ServiceDispatcher
+// and creates an endpoint that returns fuzzed messages that are passed
+// to the ReceiveAndDispatch and DispatchLoop functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  eventfd_t wakeup_val = 1;
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+  // Endpoint is only used to be immediately wrapped as a unique_ptr,
+  // so it is ok to be using a raw ptr and new here without freeing.
+  FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+  std::unique_ptr<ServiceDispatcher> dispatcher = ServiceDispatcher::Create();
+  std::shared_ptr<Channel> channel(nullptr);
+  std::shared_ptr<Client> client(nullptr);
+  std::shared_ptr<Service> service(
+      new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+
+  service->SetChannel(0, std::shared_ptr<Channel>(channel));
+  dispatcher->AddService(service);
+
+  // Dispatcher blocks, so needs to run in its own thread.
+  std::thread run_dispatcher([&]() {
+    uint8_t opt = 0;
+
+    // Right now the only operations block, so the while loop is pointless
+    // but leaving it in, just in case that ever changes.
+    while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+      opt = fdp.ConsumeIntegral<uint8_t>() % dispatcher_operations.size();
+      dispatcher_operations[opt](dispatcher, &fdp);
+    }
+  });
+
+  // Continuously wake up the epoll so the dispatcher can run.
+  while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+    eventfd_write(endpoint->epoll_fd(), wakeup_val);
+  }
+
+  // Cleanup the dispatcher and thread.
+  dispatcher->SetCanceled(true);
+  if (run_dispatcher.joinable())
+    run_dispatcher.join();
+  dispatcher->RemoveService(service);
+
+  return 0;
+}
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
index aeae9d3..99325b5 100644
--- a/libs/vr/libpdx/private/pdx/rpc/macros.h
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -28,7 +28,7 @@
 // Clears any remaining contents wrapped in parentheses.
 #define _PDX_CLEAR(...)
 
-// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+// Introduces a first stub argument and _PDX_CLEAR as second argument.
 #define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
 
 // Returns the first argument of a list.
@@ -45,7 +45,7 @@
 // Returns next_func if the next element is not (), or _PDX_CLEAR
 // otherwise.
 //
-// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+// _PDX_CLEAR_IF_LAST inserts an extra first stub argument if peek is ().
 #define _PDX_NEXT_FUNC(next_element, next_func) \
   _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
 
diff --git a/libs/vr/libpdx/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp
index b112fa3..ba0d69c 100644
--- a/libs/vr/libpdx/service_dispatcher.cpp
+++ b/libs/vr/libpdx/service_dispatcher.cpp
@@ -92,9 +92,9 @@
   if (thread_count_ > 0)
     return -EBUSY;
 
-  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
+  epoll_event ee;  // See BUGS in man 2 epoll_ctl.
   if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(),
-                &dummy) < 0) {
+                &ee) < 0) {
     ALOGE("Failed to remove service from dispatcher because: %s\n",
           strerror(errno));
     return -errno;
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 9bc70ea..810eb19 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -334,8 +334,8 @@
 
   int channel_fd = iter->second.data_fd.Get();
   Status<void> status;
-  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) {
+  epoll_event ee;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &ee) < 0) {
     status.SetError(errno);
     ALOGE(
         "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
index 1cf5f17..0d5eb80 100644
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -68,8 +68,8 @@
   ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
   std::lock_guard<std::mutex> lock(lock_);
 
-  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
+  epoll_event ee;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
     const int error = errno;
     ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
     return pdx::ErrorStatus(error);
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index b771538..70f303b 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -47,7 +47,6 @@
 namespace {
 
 const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-const char kDvrStandaloneProperty[] = "ro.boot.vr";
 
 const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
 
@@ -159,8 +158,6 @@
     return false;
   }
 
-  is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false);
-
   request_display_callback_ = request_display_callback;
 
   primary_display_ = GetDisplayParams(composer, primary_display_id, true);
@@ -206,8 +203,6 @@
     return;
   boot_finished_ = true;
   post_thread_wait_.notify_one();
-  if (is_standalone_device_)
-    request_display_callback_(true);
 }
 
 // Update the post thread quiescent state based on idle and suspended inputs.
@@ -267,17 +262,11 @@
   layers_.clear();
 
   // Phones create a new composer client on resume and destroy it on pause.
-  // Standalones only create the composer client once and then use SetPowerMode
-  // to control the screen on pause/resume.
-  if (!is_standalone_device_) {
-    if (composer_callback_ != nullptr) {
-      composer_callback_->SetVsyncService(nullptr);
-      composer_callback_ = nullptr;
-    }
-    composer_.reset(nullptr);
-  } else {
-    EnableDisplay(*target_display_, false);
+  if (composer_callback_ != nullptr) {
+    composer_callback_->SetVsyncService(nullptr);
+    composer_callback_ = nullptr;
   }
+  composer_.reset(nullptr);
 
   // Trigger target-specific performance mode change.
   property_set(kDvrPerformanceProperty, "idle");
@@ -588,7 +577,7 @@
     surfaces_changed_ = true;
   }
 
-  if (request_display_callback_ && !is_standalone_device_)
+  if (request_display_callback_)
     request_display_callback_(!display_idle);
 
   // Set idle state based on whether there are any surfaces to handle.
@@ -773,28 +762,6 @@
 
   VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
 
-  if (is_standalone_device_) {
-    // First, wait until boot finishes.
-    std::unique_lock<std::mutex> lock(post_thread_mutex_);
-    if (PostThreadCondWait(lock, -1, [this] { return boot_finished_; })) {
-      return;
-    }
-
-    // Then, wait until we're either leaving the quiescent state, or the boot
-    // finished display off timeout expires.
-    if (PostThreadCondWait(lock, kBootFinishedDisplayOffTimeoutSec,
-                           [this] { return !post_thread_quiescent_; })) {
-      return;
-    }
-
-    LOG_ALWAYS_FATAL_IF(post_thread_state_ & PostThreadState::Suspended,
-                        "Vr flinger should own the display by now.");
-    post_thread_resumed_ = true;
-    post_thread_ready_.notify_all();
-    if (!composer_)
-      CreateComposer();
-  }
-
   while (1) {
     ATRACE_NAME("HardwareComposer::PostThread");
 
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index efd6d1d..ac44f74 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -43,9 +43,6 @@
 // completed.
 constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
 
-// How long to wait for a device that boots to VR to have vr flinger ready.
-constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30);
-
 // A Binder connection to surface flinger.
 class SurfaceFlingerConnection {
  public:
@@ -153,11 +150,6 @@
     return;
   }
 
-  // This test doesn't apply to standalone vr devices.
-  if (property_get_bool("ro.boot.vr", false)) {
-    return;
-  }
-
   auto surface_flinger_connection = SurfaceFlingerConnection::Create();
   ASSERT_NE(surface_flinger_connection, nullptr);
 
@@ -230,31 +222,5 @@
       SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
 }
 
-// This test runs only on devices that boot to vr. Such a device should boot to
-// a state where vr flinger is running, and the test verifies this after a
-// delay.
-TEST(BootVrFlingerTest, BootsToVrFlinger) {
-  // Exit if we are not running on a device that boots to vr.
-  if (!property_get_bool("ro.boot.vr", false)) {
-    return;
-  }
-
-  auto surface_flinger_connection = SurfaceFlingerConnection::Create();
-  ASSERT_NE(surface_flinger_connection, nullptr);
-
-  // Verify that vr flinger is enabled.
-  ASSERT_TRUE(surface_flinger_connection->IsAlive());
-  auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
-  ASSERT_TRUE(vr_flinger_active.has_value());
-
-  bool active_value = vr_flinger_active.value();
-  if (!active_value) {
-    // Try again, but delay up to 30 seconds.
-    ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true,
-        kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout),
-        SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
-  }
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 9ca8b0b..393ced7 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -53,25 +53,25 @@
 }
 
 llndk_library {
-    name: "libEGL",
+    name: "libEGL.llndk",
     symbol_file: "libs/libEGL.map.txt",
     export_include_dirs: ["include"],
 }
 
 llndk_library {
-    name: "libGLESv1_CM",
+    name: "libGLESv1_CM.llndk",
     symbol_file: "libs/libGLESv1_CM.map.txt",
     export_include_dirs: ["include"],
 }
 
 llndk_library {
-    name: "libGLESv2",
+    name: "libGLESv2.llndk",
     symbol_file: "libs/libGLESv2.map.txt",
     export_include_dirs: ["include"],
 }
 
 llndk_library {
-    name: "libGLESv3",
+    name: "libGLESv3.llndk",
     symbol_file: "libs/libGLESv3.map.txt",
     export_include_dirs: ["include"],
 }
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 1f1bcb3..e753e0d 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -186,6 +186,26 @@
 #define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
 #endif /* EGL_ANGLE_create_context_extensions_enabled */
 
+#ifndef EGL_ANGLE_feature_control
+#define EGL_ANGLE_feature_control 1
+#define EGL_FEATURE_NAME_ANGLE 0x3460
+#define EGL_FEATURE_CATEGORY_ANGLE 0x3461
+#define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462
+#define EGL_FEATURE_BUG_ANGLE 0x3463
+#define EGL_FEATURE_STATUS_ANGLE 0x3464
+#define EGL_FEATURE_COUNT_ANGLE 0x3465
+#define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466
+#define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467
+#define EGL_FEATURE_CONDITION_ANGLE 0x3468
+#define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGIANGLEPROC) (EGLDisplay dpy, EGLint name, EGLint index);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBANGLEPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI const char *EGLAPIENTRY eglQueryStringiANGLE(EGLDisplay dpy, EGLint name, EGLint index);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribANGLE(EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#endif
+#endif /* EGL_ANGLE_feature_control */
+
 // clang-format on
 
 #endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 3c76c62..77d887c 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -128,6 +128,7 @@
 cc_library_shared {
     name: "libEGL",
     defaults: ["egl_libs_defaults"],
+    llndk_stubs: "libEGL.llndk",
     srcs: [
         "EGL/egl_tls.cpp",
         "EGL/egl_cache.cpp",
@@ -193,6 +194,7 @@
 cc_library_shared {
     name: "libGLESv1_CM",
     defaults: ["gles_libs_defaults"],
+    llndk_stubs: "libGLESv1_CM.llndk",
     srcs: ["GLES_CM/gl.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
     version_script: "libGLESv1_CM.map.txt",
@@ -204,6 +206,7 @@
 cc_library_shared {
     name: "libGLESv2",
     defaults: ["gles_libs_defaults"],
+    llndk_stubs: "libGLESv2.llndk",
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv2\""],
 
@@ -218,6 +221,7 @@
 cc_library_shared {
     name: "libGLESv3",
     defaults: ["gles_libs_defaults"],
+    llndk_stubs: "libGLESv3.llndk",
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 74fb019..beca7f1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -18,11 +18,11 @@
 
 #include "BlobCache.h"
 
+#include <android-base/properties.h>
 #include <errno.h>
 #include <inttypes.h>
-
-#include <android-base/properties.h>
 #include <log/log.h>
+
 #include <chrono>
 
 namespace android {
@@ -36,8 +36,8 @@
 // BlobCache::Header::mDeviceVersion value
 static const uint32_t blobCacheDeviceVersion = 1;
 
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
-        mMaxTotalSize(maxTotalSize),
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize)
+      : mMaxTotalSize(maxTotalSize),
         mMaxKeySize(maxKeySize),
         mMaxValueSize(maxValueSize),
         mTotalSize(0) {
@@ -52,21 +52,21 @@
     ALOGV("initializing random seed using %lld", (unsigned long long)now);
 }
 
-void BlobCache::set(const void* key, size_t keySize, const void* value,
-        size_t valueSize) {
+void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
-                keySize, mMaxKeySize);
+        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize,
+              mMaxKeySize);
         return;
     }
     if (mMaxValueSize < valueSize) {
-        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
-                valueSize, mMaxValueSize);
+        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize,
+              mMaxValueSize);
         return;
     }
     if (mMaxTotalSize < keySize + valueSize) {
         ALOGV("set: not caching because the combined key/value size is too "
-                "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+              "large: %zu (limit: %zu)",
+              keySize + valueSize, mMaxTotalSize);
         return;
     }
     if (keySize == 0) {
@@ -78,12 +78,12 @@
         return;
     }
 
-    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, nullptr);
+    std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
+    CacheEntry cacheEntry(cacheKey, nullptr);
 
     while (true) {
-        auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
-        if (index == mCacheEntries.end() || dummyEntry < *index) {
+        auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry);
+        if (index == mCacheEntries.end() || cacheEntry < *index) {
             // Create a new cache entry.
             std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true));
             std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -95,16 +95,16 @@
                     continue;
                 } else {
                     ALOGV("set: not caching new key/value pair because the "
-                            "total cache size limit would be exceeded: %zu "
-                            "(limit: %zu)",
-                            keySize + valueSize, mMaxTotalSize);
+                          "total cache size limit would be exceeded: %zu "
+                          "(limit: %zu)",
+                          keySize + valueSize, mMaxTotalSize);
                     break;
                 }
             }
             mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
             mTotalSize = newTotalSize;
-            ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
-                    keySize, valueSize);
+            ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize,
+                  valueSize);
         } else {
             // Update the existing cache entry.
             std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -117,31 +117,31 @@
                     continue;
                 } else {
                     ALOGV("set: not caching new value because the total cache "
-                            "size limit would be exceeded: %zu (limit: %zu)",
-                            keySize + valueSize, mMaxTotalSize);
+                          "size limit would be exceeded: %zu (limit: %zu)",
+                          keySize + valueSize, mMaxTotalSize);
                     break;
                 }
             }
             index->setValue(valueBlob);
             mTotalSize = newTotalSize;
             ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
-                    "value", keySize, valueSize);
+                  "value",
+                  keySize, valueSize);
         }
         break;
     }
 }
 
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
-        size_t valueSize) {
+size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
-                keySize, mMaxKeySize);
+        ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize,
+              mMaxKeySize);
         return 0;
     }
-    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, nullptr);
-    auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
-    if (index == mCacheEntries.end() || dummyEntry < *index) {
+    std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
+    CacheEntry cacheEntry(cacheKey, nullptr);
+    auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry);
+    if (index == mCacheEntries.end() || cacheEntry < *index) {
         ALOGV("get: no cache entry found for key of size %zu", keySize);
         return 0;
     }
@@ -154,8 +154,8 @@
         ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
         memcpy(value, valueBlob->getData(), valueBlobSize);
     } else {
-        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
-                valueSize, valueBlobSize);
+        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize,
+              valueBlobSize);
     }
     return valueBlobSize;
 }
@@ -167,7 +167,7 @@
 size_t BlobCache::getFlattenedSize() const {
     auto buildId = base::GetProperty("ro.build.id", "");
     size_t size = align4(sizeof(Header) + buildId.size());
-    for (const CacheEntry& e :  mCacheEntries) {
+    for (const CacheEntry& e : mCacheEntries) {
         std::shared_ptr<Blob> const& keyBlob = e.getKey();
         std::shared_ptr<Blob> const& valueBlob = e.getValue();
         size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
@@ -193,7 +193,7 @@
     // Write cache entries
     uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
     off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
-    for (const CacheEntry& e :  mCacheEntries) {
+    for (const CacheEntry& e : mCacheEntries) {
         std::shared_ptr<Blob> const& keyBlob = e.getKey();
         std::shared_ptr<Blob> const& valueBlob = e.getValue();
         size_t keySize = keyBlob->getSize();
@@ -259,8 +259,7 @@
             return -EINVAL;
         }
 
-        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
-                &byteBuffer[byteOffset]);
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(&byteBuffer[byteOffset]);
         size_t keySize = eheader->mKeySize;
         size_t valueSize = eheader->mValueSize;
         size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
@@ -304,10 +303,8 @@
     return mTotalSize > mMaxTotalSize / 2;
 }
 
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
-        mData(copyData ? malloc(size) : data),
-        mSize(size),
-        mOwnsData(copyData) {
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData)
+      : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) {
     if (data != nullptr && copyData) {
         memcpy(const_cast<void*>(mData), data, size);
     }
@@ -335,19 +332,13 @@
     return mSize;
 }
 
-BlobCache::CacheEntry::CacheEntry() {
-}
+BlobCache::CacheEntry::CacheEntry() {}
 
-BlobCache::CacheEntry::CacheEntry(
-        const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
-        mKey(key),
-        mValue(value) {
-}
+BlobCache::CacheEntry::CacheEntry(const std::shared_ptr<Blob>& key,
+                                  const std::shared_ptr<Blob>& value)
+      : mKey(key), mValue(value) {}
 
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
-        mKey(ce.mKey),
-        mValue(ce.mValue) {
-}
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce) : mKey(ce.mKey), mValue(ce.mValue) {}
 
 bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
     return *mKey < *rhs.mKey;
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index e5c5e5b..50b4e4c 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -54,8 +54,7 @@
     //   0 < keySize
     //   value != NULL
     //   0 < valueSize
-    void set(const void* key, size_t keySize, const void* value,
-            size_t valueSize);
+    void set(const void* key, size_t keySize, const void* value, size_t valueSize);
 
     // get retrieves from the cache the binary value associated with a given
     // binary key.  If the key is present in the cache then the length of the
@@ -75,7 +74,6 @@
     //   0 <= valueSize
     size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
 
-
     // getFlattenedSize returns the number of bytes needed to store the entire
     // serialized cache.
     size_t getFlattenedSize() const;
@@ -168,7 +166,6 @@
         void setValue(const std::shared_ptr<Blob>& value);
 
     private:
-
         // mKey is the key that identifies the cache entry.
         std::shared_ptr<Blob> mKey;
 
@@ -245,6 +242,6 @@
     std::vector<CacheEntry> mCacheEntries;
 };
 
-}
+} // namespace android
 
 #endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index cf67cf4..d31373b 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -14,25 +14,24 @@
  ** limitations under the License.
  */
 
+#include "BlobCache.h"
+
 #include <fcntl.h>
+#include <gtest/gtest.h>
 #include <stdio.h>
 
 #include <memory>
 
-#include <gtest/gtest.h>
-
-#include "BlobCache.h"
-
 namespace android {
 
-template<typename T> using sp = std::shared_ptr<T>;
+template <typename T>
+using sp = std::shared_ptr<T>;
 
 class BlobCacheTest : public ::testing::Test {
 protected:
-
     enum {
         OK = 0,
-        BAD_VALUE = -EINVAL
+        BAD_VALUE = -EINVAL,
     };
 
     enum {
@@ -41,19 +40,15 @@
         MAX_TOTAL_SIZE = 13,
     };
 
-    virtual void SetUp() {
-        mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
-    }
+    virtual void SetUp() { mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); }
 
-    virtual void TearDown() {
-        mBC.reset();
-    }
+    virtual void TearDown() { mBC.reset(); }
 
     std::unique_ptr<BlobCache> mBC;
 };
 
 TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
     ASSERT_EQ('e', buf[0]);
@@ -63,7 +58,7 @@
 }
 
 TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
-    unsigned char buf[2] = { 0xee, 0xee };
+    unsigned char buf[2] = {0xee, 0xee};
     mBC->set("ab", 2, "cd", 2);
     mBC->set("ef", 2, "gh", 2);
     ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
@@ -75,9 +70,9 @@
 }
 
 TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
-    unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4));
     ASSERT_EQ(0xee, buf[0]);
     ASSERT_EQ('e', buf[1]);
     ASSERT_EQ('f', buf[2]);
@@ -87,7 +82,7 @@
 }
 
 TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
-    unsigned char buf[3] = { 0xee, 0xee, 0xee };
+    unsigned char buf[3] = {0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
     ASSERT_EQ(0xee, buf[0]);
@@ -101,7 +96,7 @@
 }
 
 TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
     mBC->set("abcd", 4, "ijkl", 4);
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
@@ -112,9 +107,9 @@
 }
 
 TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
-    unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
     ASSERT_EQ('e', buf[0]);
     ASSERT_EQ('f', buf[1]);
@@ -123,13 +118,13 @@
 }
 
 TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
-    char key[MAX_KEY_SIZE+1];
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+    char key[MAX_KEY_SIZE + 1];
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
+    for (int i = 0; i < MAX_KEY_SIZE + 1; i++) {
         key[i] = 'a';
     }
-    mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
-    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+    mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4));
     ASSERT_EQ(0xee, buf[0]);
     ASSERT_EQ(0xee, buf[1]);
     ASSERT_EQ(0xee, buf[2]);
@@ -137,16 +132,16 @@
 }
 
 TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
-    char buf[MAX_VALUE_SIZE+1];
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+    char buf[MAX_VALUE_SIZE + 1];
+    for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
         buf[i] = 'b';
     }
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+    for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
         buf[i] = 0xee;
     }
-    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE + 1));
+    for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
         SCOPED_TRACE(i);
         ASSERT_EQ(0xee, buf[i]);
     }
@@ -174,7 +169,7 @@
 
 TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
     char key[MAX_KEY_SIZE];
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     for (int i = 0; i < MAX_KEY_SIZE; i++) {
         key[i] = 'a';
     }
@@ -195,8 +190,7 @@
     for (int i = 0; i < MAX_VALUE_SIZE; i++) {
         buf[i] = 0xee;
     }
-    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
-            MAX_VALUE_SIZE));
+    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE));
     for (int i = 0; i < MAX_VALUE_SIZE; i++) {
         SCOPED_TRACE(i);
         ASSERT_EQ('b', buf[i]);
@@ -223,7 +217,7 @@
 }
 
 TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
-    unsigned char buf[1] = { 0xee };
+    unsigned char buf[1] = {0xee};
     mBC->set("x", 1, "y", 1);
     ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
     ASSERT_EQ('y', buf[0]);
@@ -258,13 +252,13 @@
     }
     // Count the number of entries in the cache.
     int numCached = 0;
-    for (int i = 0; i < maxEntries+1; i++) {
+    for (int i = 0; i < maxEntries + 1; i++) {
         uint8_t k = i;
         if (mBC->get(&k, 1, nullptr, 0) == 1) {
             numCached++;
         }
     }
-    ASSERT_EQ(maxEntries/2 + 1, numCached);
+    ASSERT_EQ(maxEntries / 2 + 1, numCached);
 }
 
 class BlobCacheFlattenTest : public BlobCacheTest {
@@ -291,7 +285,7 @@
 };
 
 TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
     roundTrip();
     ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
@@ -359,7 +353,7 @@
 }
 
 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
 
     size_t size = mBC->getFlattenedSize();
@@ -376,7 +370,7 @@
 }
 
 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
 
     size_t size = mBC->getFlattenedSize();
@@ -395,7 +389,7 @@
 }
 
 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
 
     size_t size = mBC->getFlattenedSize();
@@ -414,7 +408,7 @@
 }
 
 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
     mBC->set("abcd", 4, "efgh", 4);
 
     size_t size = mBC->getFlattenedSize();
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
index 0e2a9b3..b7fdf97 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,9 @@
 
 #pragma once
 
-#include <log/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
+
 #include <memory>
 
 class CallStack {
@@ -30,9 +31,8 @@
         if (backtrace->Unwind(2)) {
             for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
                 __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
-                        backtrace->FormatFrameData(i).c_str());
+                                    backtrace->FormatFrameData(i).c_str());
             }
         }
     }
 };
-
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 3284778..751f3be 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -70,7 +70,7 @@
             return;
         }
 
-        // Sanity check the size before trying to mmap it.
+        // Check the size before trying to mmap it.
         size_t fileSize = statBuf.st_size;
         if (fileSize > mMaxTotalSize * 2) {
             ALOGE("cache file is too large: %#" PRIx64,
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 85e2c15..76fd7f0 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -364,7 +364,7 @@
             f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
 
             /*
-             * GL_EXT_debug_label is special, we always report it as
+             * GL_EXT_debug_marker is special, we always report it as
              * supported, it's handled by GLES_trace. If GLES_trace is not
              * enabled, then these are no-ops.
              */
@@ -514,6 +514,8 @@
         if (so) {
             return so;
         }
+        ALOGE("Could not load %s from updatable gfx driver namespace: %s.", name.c_str(),
+              dlerror());
     }
     return nullptr;
 }
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 7b2d7c9..81742ab 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -1,29 +1,26 @@
-/* 
+/*
  ** Copyright 2009, 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 
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
  **
- **     http://www.apache.org/licenses/LICENSE-2.0 
+ **     http://www.apache.org/licenses/LICENSE-2.0
  **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- ** See the License for the specific language governing permissions and 
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
 
 #ifndef ANDROID_EGL_LOADER_H
 #define ANDROID_EGL_LOADER_H
 
+#include <EGL/egl.h>
 #include <stdint.h>
 
-#include <EGL/egl.h>
-
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 struct egl_connection_t;
 
@@ -62,16 +59,12 @@
     void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
     void init_angle_backend(void* dso, egl_connection_t* cnx);
 
-    static __attribute__((noinline))
-    void init_api(void* dso,
-            char const * const * api,
-            char const * const * ref_api,
-            __eglMustCastToProperFunctionPointerType* curr,
-            getProcAddressType getProcAddress);
+    static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
+                                                   const char* const* ref_api,
+                                                   __eglMustCastToProperFunctionPointerType* curr,
+                                                   getProcAddressType getProcAddress);
 };
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif /* ANDROID_EGL_LOADER_H */
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 43f7a07..e5b9e14 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,45 +14,35 @@
  ** limitations under the License.
  */
 
+#include <EGL/egl.h>
+#include <android-base/properties.h>
+#include <log/log.h>
 #include <stdlib.h>
 
-#include <EGL/egl.h>
-
-#include <android-base/properties.h>
-
-#include <log/log.h>
-
 #include "../egl_impl.h"
-
-#include "egldefs.h"
-#include "egl_tls.h"
-#include "egl_display.h"
-#include "egl_object.h"
-#include "egl_layers.h"
 #include "CallStack.h"
 #include "Loader.h"
+#include "egl_display.h"
+#include "egl_layers.h"
+#include "egl_object.h"
+#include "egl_tls.h"
+#include "egldefs.h"
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 egl_connection_t gEGLImpl;
 gl_hooks_t gHooks[2];
 gl_hooks_t gHooksNoContext;
 pthread_key_t gGLWrapperKey = -1;
 
-// ----------------------------------------------------------------------------
-
-void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+void setGLHooksThreadSpecific(gl_hooks_t const* value) {
     setGlThreadSpecific(value);
 }
 
-/*****************************************************************************/
-
 static int gl_no_context() {
     if (egl_tls_t::logNoContextCall()) {
-        char const* const error = "call to OpenGL ES API with "
-                "no current context (logged once per thread)";
+        const char* const error = "call to OpenGL ES API with "
+                                  "no current context (logged once per thread)";
         if (LOG_NDEBUG) {
             ALOGE(error);
         } else {
@@ -65,10 +55,9 @@
     return 0;
 }
 
-static void early_egl_init(void)
-{
+static void early_egl_init(void) {
     int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
-    EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
+    EGLFuncPointer* iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
     for (int hook = 0; hook < numHooks; ++hook) {
         *(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
     }
@@ -76,75 +65,40 @@
     setGLHooksThreadSpecific(&gHooksNoContext);
 }
 
-static pthread_once_t once_control = PTHREAD_ONCE_INIT;
-static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
-
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy) {
-    egl_display_ptr dp = get_display(dpy);
-    if (!dp)
-        return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
-    if (!dp->isReady())
-        return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
-
-    return dp;
-}
-
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
-        egl_connection_t*& cnx) {
-    cnx = nullptr;
-    egl_display_ptr dp = validate_display(dpy);
-    if (!dp)
-        return dp;
-    cnx = &gEGLImpl;
-    if (cnx->dso == nullptr) {
-        return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
-    }
-    return dp;
-}
-
-// ----------------------------------------------------------------------------
-
-const GLubyte * egl_get_string_for_current_context(GLenum name) {
+const GLubyte* egl_get_string_for_current_context(GLenum name) {
     // NOTE: returning NULL here will fall-back to the default
     // implementation.
 
     EGLContext context = egl_tls_t::getContext();
-    if (context == EGL_NO_CONTEXT)
-        return nullptr;
+    if (context == EGL_NO_CONTEXT) return nullptr;
 
-    egl_context_t const * const c = get_context(context);
+    const egl_context_t* const c = get_context(context);
     if (c == nullptr) // this should never happen, by construction
         return nullptr;
 
-    if (name != GL_EXTENSIONS)
-        return nullptr;
+    if (name != GL_EXTENSIONS) return nullptr;
 
-    return (const GLubyte *)c->gl_extensions.c_str();
+    return (const GLubyte*)c->gl_extensions.c_str();
 }
 
-const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
+const GLubyte* egl_get_string_for_current_context(GLenum name, GLuint index) {
     // NOTE: returning NULL here will fall-back to the default
     // implementation.
 
     EGLContext context = egl_tls_t::getContext();
-    if (context == EGL_NO_CONTEXT)
-        return nullptr;
+    if (context == EGL_NO_CONTEXT) return nullptr;
 
-    egl_context_t const * const c = get_context(context);
+    const egl_context_t* const c = get_context(context);
     if (c == nullptr) // this should never happen, by construction
         return nullptr;
 
-    if (name != GL_EXTENSIONS)
-        return nullptr;
+    if (name != GL_EXTENSIONS) return nullptr;
 
     // if index is out of bounds, assume it will be in the default
     // implementation too, so we don't have to generate a GL error here
-    if (index >= c->tokenized_gl_extensions.size())
-        return nullptr;
+    if (index >= c->tokenized_gl_extensions.size()) return nullptr;
 
-    return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
+    return (const GLubyte*)c->tokenized_gl_extensions[index].c_str();
 }
 
 GLint egl_get_num_extensions_for_current_context() {
@@ -152,10 +106,9 @@
     // implementation.
 
     EGLContext context = egl_tls_t::getContext();
-    if (context == EGL_NO_CONTEXT)
-        return -1;
+    if (context == EGL_NO_CONTEXT) return -1;
 
-    egl_context_t const * const c = get_context(context);
+    const egl_context_t* const c = get_context(context);
     if (c == nullptr) // this should never happen, by construction
         return -1;
 
@@ -166,7 +119,8 @@
     return &gEGLImpl;
 }
 
-// ----------------------------------------------------------------------------
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
 
 static EGLBoolean egl_init_drivers_locked() {
     if (sEarlyInitState) {
@@ -194,7 +148,6 @@
     return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
-
 // this mutex protects driver load logic as a critical section since it accesses to global variable
 // like gEGLImpl
 static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
@@ -228,13 +181,10 @@
     }
 }
 
-void gl_noop() {
-}
+void gl_noop() {}
 
-// ----------------------------------------------------------------------------
-
-void setGlThreadSpecific(gl_hooks_t const *value) {
-    gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+void setGlThreadSpecific(gl_hooks_t const* value) {
+    gl_hooks_t const* volatile* tls_hooks = get_tls_hooks();
     tls_hooks[TLS_SLOT_OPENGL_API] = value;
 }
 
@@ -270,8 +220,4 @@
 #undef GL_ENTRY
 #undef EGL_ENTRY
 
-
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c51a129..502c14f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -20,7 +20,6 @@
 #include <EGL/eglext.h>
 
 #include "../egl_impl.h"
-
 #include "egl_layers.h"
 #include "egl_platform_entries.h"
 #include "egl_tls.h"
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 97dc0f1..dc8e587 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,22 +16,26 @@
 
 #if defined(__ANDROID__)
 
-#include "Loader.h"
 #include "egl_angle_platform.h"
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <EGL/Platform.h>
 #pragma GCC diagnostic pop
-
 #include <android/dlext.h>
 #include <dlfcn.h>
 #include <graphicsenv/GraphicsEnv.h>
-#include <time.h>
 #include <log/log.h>
+#include <time.h>
+#include <vndksupport/linker.h>
+
+#include "Loader.h"
 
 namespace angle {
 
+constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
+constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
+
 static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
 static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
 
@@ -101,11 +105,22 @@
 bool initializeAnglePlatform(EGLDisplay dpy) {
     // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
     android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
-    const android_dlextinfo dlextinfo = {
-            .flags = ANDROID_DLEXT_USE_NAMESPACE,
-            .library_namespace = ns,
-    };
-    void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+    void* so = nullptr;
+    if (ns) {
+        const android_dlextinfo dlextinfo = {
+                .flags = ANDROID_DLEXT_USE_NAMESPACE,
+                .library_namespace = ns,
+        };
+        so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+    } else {
+        // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
+        so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
+    }
+    if (!so) {
+        ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
+        return false;
+    }
+
     angleGetDisplayPlatform =
             reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
 
@@ -114,14 +129,12 @@
         return false;
     }
 
-    angleResetDisplayPlatform =
-            reinterpret_cast<ResetDisplayPlatformFunc>(
-                    eglGetProcAddress("ANGLEResetDisplayPlatform"));
+    angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>(
+            eglGetProcAddress("ANGLEResetDisplayPlatform"));
 
     PlatformMethods* platformMethods = nullptr;
-    if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
-                                                              g_NumPlatformMethods, nullptr,
-                                                              &platformMethods))) {
+    if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
+                                    &platformMethods))) {
         ALOGE("ANGLEGetDisplayPlatform call failed!");
         return false;
     }
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index bcf4961..efa67db 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -16,17 +16,14 @@
 
 #include "egl_cache.h"
 
-#include "../egl_impl.h"
-
-#include "egl_display.h"
-
+#include <log/log.h>
 #include <private/EGL/cache.h>
-
 #include <unistd.h>
 
 #include <thread>
 
-#include <log/log.h>
+#include "../egl_impl.h"
+#include "egl_display.h"
 
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
@@ -36,9 +33,7 @@
 // The time in seconds to wait before saving newly inserted cache entries.
 static const unsigned int deferredSaveDelay = 4;
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 #define BC_EXT_STR "EGL_ANDROID_blob_cache"
 
@@ -50,25 +45,22 @@
 //
 // Callback functions passed to EGL.
 //
-static void setBlob(const void* key, EGLsizeiANDROID keySize,
-        const void* value, EGLsizeiANDROID valueSize) {
+static void setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+                    EGLsizeiANDROID valueSize) {
     egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
 }
 
-static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
-        void* value, EGLsizeiANDROID valueSize) {
+static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+                               EGLsizeiANDROID valueSize) {
     return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
 }
 
 //
 // egl_cache_t definition
 //
-egl_cache_t::egl_cache_t() :
-        mInitialized(false) {
-}
+egl_cache_t::egl_cache_t() : mInitialized(false) {}
 
-egl_cache_t::~egl_cache_t() {
-}
+egl_cache_t::~egl_cache_t() {}
 
 egl_cache_t egl_cache_t::sCache;
 
@@ -76,7 +68,7 @@
     return &sCache;
 }
 
-void egl_cache_t::initialize(egl_display_t *display) {
+void egl_cache_t::initialize(egl_display_t* display) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     egl_connection_t* const cnx = &gEGLImpl;
@@ -85,28 +77,26 @@
         size_t bcExtLen = strlen(BC_EXT_STR);
         size_t extsLen = strlen(exts);
         bool equal = !strcmp(BC_EXT_STR, exts);
-        bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
-        bool atEnd = (bcExtLen+1) < extsLen &&
-                !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+        bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen + 1);
+        bool atEnd = (bcExtLen + 1) < extsLen &&
+                !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen + 1));
         bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
         if (equal || atStart || atEnd || inMiddle) {
             PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
-            eglSetBlobCacheFuncsANDROID =
-                    reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
-                            cnx->egl.eglGetProcAddress(
-                                    "eglSetBlobCacheFuncsANDROID"));
+            eglSetBlobCacheFuncsANDROID = reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+                    cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncsANDROID"));
             if (eglSetBlobCacheFuncsANDROID == nullptr) {
                 ALOGE("EGL_ANDROID_blob_cache advertised, "
-                        "but unable to get eglSetBlobCacheFuncsANDROID");
+                      "but unable to get eglSetBlobCacheFuncsANDROID");
                 return;
             }
 
-            eglSetBlobCacheFuncsANDROID(display->disp.dpy,
-                    android::setBlob, android::getBlob);
+            eglSetBlobCacheFuncsANDROID(display->disp.dpy, android::setBlob, android::getBlob);
             EGLint err = cnx->egl.eglGetError();
             if (err != EGL_SUCCESS) {
                 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
-                        "%#x", err);
+                      "%#x",
+                      err);
             }
         }
     }
@@ -122,8 +112,8 @@
     mBlobCache = nullptr;
 }
 
-void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
-        const void* value, EGLsizeiANDROID valueSize) {
+void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+                          EGLsizeiANDROID valueSize) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
@@ -150,8 +140,8 @@
     }
 }
 
-EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
-        void* value, EGLsizeiANDROID valueSize) {
+EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+                                     EGLsizeiANDROID valueSize) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
@@ -178,6 +168,4 @@
     return mBlobCache.get();
 }
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 7382b91..d10a615 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,21 +20,18 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include "FileBlobCache.h"
-
 #include <memory>
 #include <mutex>
 #include <string>
 
-// ----------------------------------------------------------------------------
+#include "FileBlobCache.h"
+
 namespace android {
-// ----------------------------------------------------------------------------
 
 class egl_display_t;
 
 class EGLAPI egl_cache_t {
 public:
-
     // get returns a pointer to the singleton egl_cache_t object.  This
     // singleton object will never be destroyed.
     static egl_cache_t* get();
@@ -117,8 +114,6 @@
     static egl_cache_t sCache;
 };
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif // ANDROID_EGL_CACHE_H
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 8c6f284..0b755aa 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,7 +14,6 @@
  ** limitations under the License.
  */
 
-#define __STDC_LIMIT_MACROS 1
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "egl_display.h"
@@ -39,21 +38,17 @@
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
-static char const * const sVendorString     = "Android";
-static char const* const sVersionString14 = "1.4 Android META-EGL";
-static char const* const sVersionString15 = "1.5 Android META-EGL";
-static char const * const sClientApiString  = "OpenGL_ES";
+static const char* const sVendorString = "Android";
+static const char* const sVersionString14 = "1.4 Android META-EGL";
+static const char* const sVersionString15 = "1.5 Android META-EGL";
+static const char* const sClientApiString = "OpenGL_ES";
 
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
 
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-
-// ----------------------------------------------------------------------------
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
 
 bool findExtension(const char* exts, const char* name, size_t nameLen) {
     if (exts) {
@@ -79,11 +74,15 @@
     return eglDisplay ? eglDisplay->getRefsCount() : 0;
 }
 
-egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
+std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap;
+std::mutex egl_display_t::displayMapLock;
 
-egl_display_t::egl_display_t() :
-    magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
-}
+egl_display_t::egl_display_t()
+      : magic('_dpy'),
+        finishOnSwap(false),
+        traceGpuCompletion(false),
+        refs(0),
+        eglIsInitialized(false) {}
 
 egl_display_t::~egl_display_t() {
     magic = 0;
@@ -95,11 +94,12 @@
         return nullptr;
     }
 
-    uintptr_t index = uintptr_t(dpy)-1U;
-    if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
+    const std::lock_guard<std::mutex> lock(displayMapLock);
+    auto search = displayMap.find(dpy);
+    if (search == displayMap.end() || !search->second->isValid()) {
         return nullptr;
     }
-    return &sDisplay[index];
+    return search->second.get();
 }
 
 void egl_display_t::addObject(egl_object_t* object) {
@@ -125,10 +125,9 @@
 
 EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
                                                const EGLAttrib* attrib_list) {
-    if (uintptr_t(disp) >= NUM_DISPLAYS)
-        return nullptr;
+    if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
 
-    return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
+    return getPlatformDisplay(disp, attrib_list);
 }
 
 static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
@@ -144,6 +143,16 @@
                 attrs.push_back(attr[1]);
             }
         }
+        const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
+        std::vector<const char*> features;
+        if (eglFeatures.size() > 0) {
+            for (const std::string& eglFeature : eglFeatures) {
+                features.push_back(eglFeature.c_str());
+            }
+            features.push_back(0);
+            attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
+            attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+        }
 
         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
@@ -173,7 +182,6 @@
 
 EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
                                              const EGLAttrib* attrib_list) {
-    std::lock_guard<std::mutex> _l(lock);
     ATRACE_CALL();
 
     // get our driver loader
@@ -201,7 +209,7 @@
             // It is possible that eglGetPlatformDisplay does not have a
             // working implementation for Android platform; in that case,
             // one last fallback to eglGetDisplay
-            if(dpy == EGL_NO_DISPLAY) {
+            if (dpy == EGL_NO_DISPLAY) {
                 if (attrib_list) {
                     ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
                 }
@@ -209,17 +217,23 @@
             }
         }
 
-        disp.dpy = dpy;
         if (dpy == EGL_NO_DISPLAY) {
             loader.close(cnx);
+        } else {
+            const std::lock_guard<std::mutex> lock(displayMapLock);
+            if (displayMap.find(dpy) == displayMap.end()) {
+                auto d = std::make_unique<egl_display_t>();
+                d->disp.dpy = dpy;
+                displayMap[dpy] = std::move(d);
+            }
+            return dpy;
         }
     }
 
-    return EGLDisplay(uintptr_t(display) + 1U);
+    return nullptr;
 }
 
-EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
-
+EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
     { // scope for refLock
         std::unique_lock<std::mutex> _l(refLock);
         refs++;
@@ -227,7 +241,7 @@
             // We don't know what to report until we know what the
             // driver supports. Make sure we are initialized before
             // returning the version info.
-            while(!eglIsInitialized) {
+            while (!eglIsInitialized) {
                 refCond.wait(_l);
             }
             egl_connection_t* const cnx = &gEGLImpl;
@@ -240,7 +254,7 @@
             if (minor != nullptr) *minor = cnx->minor;
             return EGL_TRUE;
         }
-        while(eglIsInitialized) {
+        while (eglIsInitialized) {
             refCond.wait(_l);
         }
     }
@@ -260,40 +274,31 @@
         if (cnx->dso) {
             EGLDisplay idpy = disp.dpy;
             if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
-                //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+                // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
                 //        idpy, cnx->major, cnx->minor, cnx);
 
                 // display is now initialized
                 disp.state = egl_display_t::INITIALIZED;
 
                 // get the query-strings for this display for each implementation
-                disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
-                        EGL_VENDOR);
-                disp.queryString.version = cnx->egl.eglQueryString(idpy,
-                        EGL_VERSION);
-                disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
-                        EGL_EXTENSIONS);
-                disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
-                        EGL_CLIENT_APIS);
+                disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR);
+                disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION);
+                disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
+                disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
 
             } else {
                 ALOGW("eglInitialize(%p) failed (%s)", idpy,
-                        egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+                      egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
             }
         }
 
         if (cnx->minor == 5) {
             // full list in egl_entries.in
-            if (!cnx->egl.eglCreateImage ||
-                !cnx->egl.eglDestroyImage ||
-                !cnx->egl.eglGetPlatformDisplay ||
-                !cnx->egl.eglCreatePlatformWindowSurface ||
-                !cnx->egl.eglCreatePlatformPixmapSurface ||
-                !cnx->egl.eglCreateSync ||
-                !cnx->egl.eglDestroySync ||
-                !cnx->egl.eglClientWaitSync ||
-                !cnx->egl.eglGetSyncAttrib ||
-                !cnx->egl.eglWaitSync) {
+            if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage ||
+                !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface ||
+                !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync ||
+                !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync ||
+                !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) {
                 ALOGE("Driver indicates EGL 1.5 support, but does not have "
                       "a critical API");
                 cnx->minor = 4;
@@ -388,7 +393,6 @@
 }
 
 EGLBoolean egl_display_t::terminate() {
-
     { // scope for refLock
         std::unique_lock<std::mutex> _rl(refLock);
         if (refs == 0) {
@@ -421,7 +425,7 @@
             }
             if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
                 ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
-                        egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+                      egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
             }
             // REVISIT: it's unclear what to do if eglTerminate() fails
             disp.state = egl_display_t::TERMINATED;
@@ -454,8 +458,7 @@
     return res;
 }
 
-void egl_display_t::loseCurrent(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrent(egl_context_t* cur_c) {
     if (cur_c) {
         egl_display_t* display = cur_c->getDisplay();
         if (display) {
@@ -464,8 +467,7 @@
     }
 }
 
-void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) {
     // by construction, these are either 0 or valid (possibly terminated)
     // it should be impossible for these to be invalid
     ContextRef _cur_c(cur_c);
@@ -475,7 +477,6 @@
     { // scope for the lock
         std::lock_guard<std::mutex> _l(lock);
         cur_c->onLooseCurrent();
-
     }
 
     // This cannot be called with the lock held because it might end-up
@@ -486,10 +487,9 @@
     _cur_d.release();
 }
 
-EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
-        EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
-        EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
-{
+EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw,
+                                      EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw,
+                                      EGLSurface impl_read, EGLContext impl_ctx) {
     EGLBoolean result;
 
     // by construction, these are either 0 or valid (possibly terminated)
@@ -501,14 +501,12 @@
     { // scope for the lock
         std::lock_guard<std::mutex> _l(lock);
         if (c) {
-            result = c->cnx->egl.eglMakeCurrent(
-                    disp.dpy, impl_draw, impl_read, impl_ctx);
+            result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 c->onMakeCurrent(draw, read);
             }
         } else {
-            result = cur_c->cnx->egl.eglMakeCurrent(
-                    disp.dpy, impl_draw, impl_read, impl_ctx);
+            result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 cur_c->onLooseCurrent();
             }
@@ -534,6 +532,23 @@
     return findExtension(mExtensionString.c_str(), name, nameLen);
 }
 
-// ----------------------------------------------------------------------------
+egl_display_t* validate_display(EGLDisplay dpy) {
+    egl_display_t* const dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr);
+    if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr);
+
+    return dp;
+}
+
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) {
+    *outCnx = nullptr;
+    egl_display_t* dp = validate_display(dpy);
+    if (!dp) return dp;
+    *outCnx = &gEGLImpl;
+    if ((*outCnx)->dso == nullptr) {
+        return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr);
+    }
+    return dp;
+}
+
 }; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e117314..87c2176 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -17,26 +17,22 @@
 #ifndef ANDROID_EGL_DISPLAY_H
 #define ANDROID_EGL_DISPLAY_H
 
-
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
 #include <stddef.h>
+#include <stdint.h>
 
 #include <condition_variable>
+#include <map>
+#include <memory>
 #include <mutex>
 #include <string>
 #include <unordered_set>
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <cutils/compiler.h>
-
-#include "egldefs.h"
 #include "../hooks.h"
+#include "egldefs.h"
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 class egl_object_t;
 class egl_context_t;
@@ -45,25 +41,25 @@
 bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
 bool needsAndroidPEglMitigation();
 
-// ----------------------------------------------------------------------------
-
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
-    static egl_display_t sDisplay[NUM_DISPLAYS];
+    static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
+    static std::mutex displayMapLock;
     EGLDisplay getDisplay(EGLNativeDisplayType display);
-    EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
-    void loseCurrentImpl(egl_context_t * cur_c);
+    static EGLDisplay getPlatformDisplay(EGLNativeDisplayType display,
+                                         const EGLAttrib* attrib_list);
+    void loseCurrentImpl(egl_context_t* cur_c);
 
 public:
     enum {
         NOT_INITIALIZED = 0,
-        INITIALIZED     = 1,
-        TERMINATED      = 2
+        INITIALIZED = 1,
+        TERMINATED = 2,
     };
 
     egl_display_t();
     ~egl_display_t();
 
-    EGLBoolean initialize(EGLint *major, EGLint *minor);
+    EGLBoolean initialize(EGLint* major, EGLint* minor);
     EGLBoolean terminate();
 
     // add object to this display's list
@@ -76,123 +72,69 @@
     static egl_display_t* get(EGLDisplay dpy);
     static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
 
-    EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
-            EGLSurface draw, EGLSurface read, EGLContext ctx,
-            EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx);
-    static void loseCurrent(egl_context_t * cur_c);
+    EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read,
+                           EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read,
+                           EGLContext impl_ctx);
+    static void loseCurrent(egl_context_t* cur_c);
 
     inline bool isReady() const { return (refs > 0); }
     inline bool isValid() const { return magic == '_dpy'; }
     inline bool isAlive() const { return isValid(); }
 
-    char const * getVendorString() const { return mVendorString.c_str(); }
-    char const * getVersionString() const { return mVersionString.c_str(); }
-    char const * getClientApiString() const { return mClientApiString.c_str(); }
-    char const * getExtensionString() const { return mExtensionString.c_str(); }
+    char const* getVendorString() const { return mVendorString.c_str(); }
+    char const* getVersionString() const { return mVersionString.c_str(); }
+    char const* getClientApiString() const { return mClientApiString.c_str(); }
+    char const* getExtensionString() const { return mExtensionString.c_str(); }
 
     bool haveExtension(const char* name, size_t nameLen = 0) const;
 
     inline uint32_t getRefsCount() const { return refs; }
 
     struct strings_t {
-        char const * vendor;
-        char const * version;
-        char const * clientApi;
-        char const * extensions;
+        char const* vendor;
+        char const* version;
+        char const* clientApi;
+        char const* extensions;
     };
 
     struct DisplayImpl {
-        DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
-        EGLDisplay  dpy;
-        EGLint      state;
-        strings_t   queryString;
+        DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) {}
+        EGLDisplay dpy;
+        EGLint state;
+        strings_t queryString;
     };
 
 private:
-    uint32_t        magic;
+    uint32_t magic;
 
 public:
-    DisplayImpl     disp;
-    bool    finishOnSwap;       // property: debug.egl.finish
-    bool    traceGpuCompletion; // property: debug.egl.traceGpuCompletion
-    bool    hasColorSpaceSupport;
+    DisplayImpl disp;
+    bool finishOnSwap;       // property: debug.egl.finish
+    bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
+    bool hasColorSpaceSupport;
 
 private:
-    friend class egl_display_ptr;
-
-            uint32_t                    refs;
-            bool                        eglIsInitialized;
-    mutable std::mutex                  lock;
-    mutable std::mutex                  refLock;
-    mutable std::condition_variable     refCond;
-            std::unordered_set<egl_object_t*> objects;
-            std::string mVendorString;
-            std::string mVersionString;
-            std::string mClientApiString;
-            std::string mExtensionString;
+    uint32_t refs;
+    bool eglIsInitialized;
+    mutable std::mutex lock;
+    mutable std::mutex refLock;
+    mutable std::condition_variable refCond;
+    std::unordered_set<egl_object_t*> objects;
+    std::string mVendorString;
+    std::string mVersionString;
+    std::string mClientApiString;
+    std::string mExtensionString;
 };
 
-// ----------------------------------------------------------------------------
-
-// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
-// It doesn't refcount the egl_display_t, but does ensure that the underlying
-// EGL implementation is "awake" (not hibernating) and ready for use as long
-// as the egl_display_ptr exists.
-class egl_display_ptr {
-public:
-    explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {}
-
-    // We only really need a C++11 move constructor, not a copy constructor.
-    // A move constructor would save an enter()/leave() pair on every EGL API
-    // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
-    // can't use a move constructor until those are cleaned up.
-    //
-    // egl_display_ptr(egl_display_ptr&& other) {
-    //     mDpy = other.mDpy;
-    //     other.mDpy = NULL;
-    // }
-    //
-    egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {}
-
-    ~egl_display_ptr() {}
-
-    const egl_display_t* operator->() const { return mDpy; }
-          egl_display_t* operator->()       { return mDpy; }
-
-    const egl_display_t* get() const { return mDpy; }
-          egl_display_t* get()       { return mDpy; }
-
-    operator bool() const { return mDpy != nullptr; }
-
-private:
-    egl_display_t* mDpy;
-
-    // non-assignable
-    egl_display_ptr& operator=(const egl_display_ptr&);
-};
-
-// ----------------------------------------------------------------------------
-
-inline egl_display_ptr get_display(EGLDisplay dpy) {
-    return egl_display_ptr(egl_display_t::get(dpy));
-}
-
-// Does not ensure EGL is unhibernated. Use with caution: calls into the
-// underlying EGL implementation are not safe.
-inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
+inline egl_display_t* get_display(EGLDisplay dpy) {
     return egl_display_t::get(dpy);
 }
 
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy);
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
-        egl_connection_t*& cnx);
+egl_display_t* validate_display(EGLDisplay dpy);
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx);
 EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
 EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif // ANDROID_EGL_DISPLAY_H
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2921d51..1c91f1d 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -104,11 +104,6 @@
 EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
 EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
 
-/* IMG extensions */
-
-EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
-EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
-
 /* Partial update extensions */
 
 EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ea86c9a..9752e38 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -85,17 +85,21 @@
 
         // Look up which GPA we should use
         int gpaIndex = func_indices["eglGetProcAddress"];
-        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
+        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name,
+              gpaIndex);
         EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
-        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
-
+        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this "
+              "address",
+              name, gpaIndex, (unsigned long long)gpaNext);
 
         // Call it for the requested function
         typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
         PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
 
         val = reinterpret_cast<EGLFuncPointer>(next(name));
-        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
+        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from "
+              "GPA",
+              name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
 
         // We should store it now, but to do that, we need to move func_idx to the class so we can
         // increment it separately
@@ -105,7 +109,9 @@
 
     int index = func_indices[name];
     val = (*next_layer_funcs)[index];
-    ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+    ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known "
+          "entry",
+          name, index, (unsigned long long)val);
     return reinterpret_cast<void*>(val);
 }
 
@@ -117,20 +123,26 @@
         // Some names overlap, only fill with initial entry
         // This does mean that some indices will not be used
         if (func_indices.find(name) == func_indices.end()) {
-            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
+            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning "
+                  "now",
+                  name, func_idx);
             func_names[func_idx] = name;
             func_indices[name] = func_idx;
         } else {
-            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
+            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name,
+                  func_idx);
         }
 
         // Populate layer_functions once with initial value
         // These values will arrive in priority order, starting with platform entries
         if (functions[func_idx] == nullptr) {
-            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
+            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning "
+                  "(%llu)",
+                  name, func_idx, (unsigned long long)*curr);
             functions[func_idx] = *curr;
         } else {
-            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
+            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name,
+                  func_idx, (unsigned long long)functions[func_idx]);
         }
 
         entries++;
@@ -380,8 +392,8 @@
                 auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
                 if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
                     char* error_message = nullptr;
-                    dlhandle_ = OpenNativeLibraryInNamespace(
-                        app_namespace, layer.c_str(), &native_bridge_, &error_message);
+                    dlhandle_ = OpenNativeLibraryInNamespace(app_namespace, layer.c_str(),
+                                                             &native_bridge_, &error_message);
                     if (!dlhandle_) {
                         ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
                               error_message);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index 1e2783f..705525d 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -17,19 +17,18 @@
 #ifndef ANDROID_EGL_LAYERS_H
 #define ANDROID_EGL_LAYERS_H
 
+#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
 #include <string>
 #include <unordered_map>
 #include <vector>
 
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#include <EGL/egldefs.h>
 #include "egl_platform_entries.h"
 
-#include <nativebridge/native_bridge.h>
-#include <nativeloader/native_loader.h>
-
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
 namespace android {
@@ -46,8 +45,8 @@
 
     void LoadLayers();
     void InitLayers(egl_connection_t*);
-    void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
-    void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+    void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
+    void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
     bool Initialized();
     std::string GetDebugLayers();
 
@@ -59,18 +58,23 @@
     std::vector<layer_setup_func> layer_setup_;
 
 private:
-    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
+    LayerLoader()
+          : layers_loaded_(false),
+            initialized_(false),
+            current_layer_(0),
+            dlhandle_(nullptr),
+            native_bridge_(false){};
     bool layers_loaded_;
     bool initialized_;
     unsigned current_layer_;
     void* dlhandle_;
     bool native_bridge_;
 
-    template<typename Func = void*>
+    template <typename Func = void*>
     Func GetTrampoline(const char* name) const {
         if (native_bridge_) {
-            return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
-                dlhandle_, name, nullptr, 0));
+            return reinterpret_cast<Func>(
+                    android::NativeBridgeGetTrampoline(dlhandle_, name, nullptr, 0));
         }
         return reinterpret_cast<Func>(dlsym(dlhandle_, name));
     }
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index ff4fe2d..847b351 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,19 +18,14 @@
 
 #include <sstream>
 
-
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
-egl_object_t::egl_object_t(egl_display_t* disp) :
-    display(disp), count(1) {
+egl_object_t::egl_object_t(egl_display_t* disp) : display(disp), count(1) {
     // NOTE: this does an implicit incRef
     display->addObject(this);
 }
 
-egl_object_t::~egl_object_t() {
-}
+egl_object_t::~egl_object_t() {}
 
 void egl_object_t::terminate() {
     // this marks the object as "terminated"
@@ -53,8 +48,6 @@
     return display->getObject(object);
 }
 
-// ----------------------------------------------------------------------------
-
 egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win,
                              EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx)
       : egl_object_t(dpy),
@@ -66,10 +59,10 @@
         colorSpace(colorSpace),
         egl_smpte2086_dirty(false),
         egl_cta861_3_dirty(false) {
-    egl_smpte2086_metadata.displayPrimaryRed = { EGL_DONT_CARE, EGL_DONT_CARE };
-    egl_smpte2086_metadata.displayPrimaryGreen = { EGL_DONT_CARE, EGL_DONT_CARE };
-    egl_smpte2086_metadata.displayPrimaryBlue = { EGL_DONT_CARE, EGL_DONT_CARE };
-    egl_smpte2086_metadata.whitePoint = { EGL_DONT_CARE, EGL_DONT_CARE };
+    egl_smpte2086_metadata.displayPrimaryRed = {EGL_DONT_CARE, EGL_DONT_CARE};
+    egl_smpte2086_metadata.displayPrimaryGreen = {EGL_DONT_CARE, EGL_DONT_CARE};
+    egl_smpte2086_metadata.displayPrimaryBlue = {EGL_DONT_CARE, EGL_DONT_CARE};
+    egl_smpte2086_metadata.whitePoint = {EGL_DONT_CARE, EGL_DONT_CARE};
     egl_smpte2086_metadata.maxLuminance = EGL_DONT_CARE;
     egl_smpte2086_metadata.minLuminance = EGL_DONT_CARE;
     egl_cta861_3_metadata.maxFrameAverageLightLevel = EGL_DONT_CARE;
@@ -173,16 +166,30 @@
         return EGL_FALSE;
     }
 
-    metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / EGL_METADATA_SCALING_EXT;
-    metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / EGL_METADATA_SCALING_EXT;
-    metadata.displayPrimaryGreen.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / EGL_METADATA_SCALING_EXT;
-    metadata.displayPrimaryGreen.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / EGL_METADATA_SCALING_EXT;
-    metadata.displayPrimaryBlue.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / EGL_METADATA_SCALING_EXT;
-    metadata.displayPrimaryBlue.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / EGL_METADATA_SCALING_EXT;
-    metadata.whitePoint.x = static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
-    metadata.whitePoint.y = static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
-    metadata.maxLuminance = static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
-    metadata.minLuminance = static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryGreen.x =
+            static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryGreen.y =
+            static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryBlue.x =
+            static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.displayPrimaryBlue.y =
+            static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.whitePoint.x =
+            static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
+    metadata.whitePoint.y =
+            static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
+    metadata.maxLuminance =
+            static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
+    metadata.minLuminance =
+            static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
 
     return EGL_TRUE;
 }
@@ -196,13 +203,15 @@
         return EGL_FALSE;
     }
 
-    metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / EGL_METADATA_SCALING_EXT;
-    metadata.maxFrameAverageLightLevel = static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / EGL_METADATA_SCALING_EXT;
+    metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) /
+            EGL_METADATA_SCALING_EXT;
+    metadata.maxFrameAverageLightLevel =
+            static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) /
+            EGL_METADATA_SCALING_EXT;
 
     return EGL_TRUE;
 }
 
-
 EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
     if (attribute == EGL_GL_COLORSPACE_KHR) {
         *value = colorSpace;
@@ -211,7 +220,7 @@
     return EGL_FALSE;
 }
 
-EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint* value) const {
     switch (attribute) {
         case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
             *value = egl_smpte2086_metadata.displayPrimaryRed.x;
@@ -257,7 +266,7 @@
     return EGL_FALSE;
 }
 
-EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint* value) const {
     switch (attribute) {
         case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
             *value = egl_cta861_3_metadata.maxContentLightLevel;
@@ -276,13 +285,16 @@
     egl_object_t::terminate();
 }
 
-// ----------------------------------------------------------------------------
-
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-        egl_connection_t const* cnx, int version) :
-    egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
-            config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
-}
+                             egl_connection_t const* cnx, int version)
+      : egl_object_t(get_display(dpy)),
+        dpy(dpy),
+        context(context),
+        config(config),
+        read(nullptr),
+        draw(nullptr),
+        cnx(cnx),
+        version(version) {}
 
 void egl_context_t::onLooseCurrent() {
     read = nullptr;
@@ -297,31 +309,39 @@
      * Here we cache the GL_EXTENSIONS string for this context and we
      * add the extensions always handled by the wrapper
      */
+    if (!gl_extensions.empty()) return;
 
-    if (gl_extensions.empty()) {
-        // call the implementation's glGetString(GL_EXTENSIONS)
-        const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+    // call the implementation's glGetString(GL_EXTENSIONS)
+    const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+    if (!exts) return;
 
-        // If this context is sharing with another context, and the other context was reset
-        // e.g. due to robustness failure, this context might also be reset and glGetString can
-        // return NULL.
-        if (exts) {
-            gl_extensions = exts;
-            if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
-                gl_extensions.insert(0, "GL_EXT_debug_marker ");
-            }
+    // If this context is sharing with another context, and the other context was reset
+    // e.g. due to robustness failure, this context might also be reset and glGetString can
+    // return NULL.
+    gl_extensions = exts;
+    if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+        gl_extensions.insert(0, "GL_EXT_debug_marker ");
+        // eglGetProcAddress could return function pointers to these
+        // functions while they actually don't work. Fix them now.
+        __eglMustCastToProperFunctionPointerType* f;
+        f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+                    ->gl.glInsertEventMarkerEXT;
+        if (*f != gl_noop) *f = gl_noop;
+        f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+                    ->gl.glPushGroupMarkerEXT;
+        if (*f != gl_noop) *f = gl_noop;
+        f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+                    ->gl.glPopGroupMarkerEXT;
+        if (*f != gl_noop) *f = gl_noop;
+    }
 
-            // tokenize the supported extensions for the glGetStringi() wrapper
-            std::stringstream ss;
-            std::string str;
-            ss << gl_extensions;
-            while (ss >> str) {
-                tokenized_gl_extensions.push_back(str);
-            }
-        }
+    // tokenize the supported extensions for the glGetStringi() wrapper
+    std::stringstream ss;
+    std::string str;
+    ss << gl_extensions;
+    while (ss >> str) {
+        tokenized_gl_extensions.push_back(str);
     }
 }
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index fb2bdf4..e593b1c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,30 +17,25 @@
 #ifndef ANDROID_EGL_OBJECT_H
 #define ANDROID_EGL_OBJECT_H
 
-#include <atomic>
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
 #include <stddef.h>
+#include <stdint.h>
+#include <system/window.h>
 
+#include <atomic>
 #include <string>
 #include <vector>
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <system/window.h>
-
-#include <log/log.h>
-
 #include "egl_display.h"
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 class egl_display_t;
 
 class egl_object_t {
-    egl_display_t *display;
+    egl_display_t* display;
     mutable std::atomic_size_t count;
 
 protected:
@@ -64,6 +59,7 @@
         egl_object_t* ref;
         LocalRef() = delete;
         LocalRef(const LocalRef* rhs) = delete;
+
     public:
         ~LocalRef();
         explicit LocalRef(egl_object_t* rhs);
@@ -73,9 +69,7 @@
                 ref = native;
             }
         }
-        inline N* get() {
-            return static_cast<N*>(ref);
-        }
+        inline N* get() { return static_cast<N*>(ref); }
         void acquire() const;
         void release() const;
         void terminate();
@@ -84,7 +78,7 @@
     friend class LocalRef;
 };
 
-template<typename N, typename T>
+template <typename N, typename T>
 egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) {
     if (ref) {
         ref->incRef();
@@ -92,21 +86,21 @@
 }
 
 template <typename N, typename T>
-egl_object_t::LocalRef<N,T>::~LocalRef() {
+egl_object_t::LocalRef<N, T>::~LocalRef() {
     if (ref) {
         ref->destroy();
     }
 }
 
 template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::acquire() const {
+void egl_object_t::LocalRef<N, T>::acquire() const {
     if (ref) {
         ref->incRef();
     }
 }
 
 template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::release() const {
+void egl_object_t::LocalRef<N, T>::release() const {
     if (ref) {
         if (ref->decRef() == 1) {
             // shouldn't happen because this is called from LocalRef
@@ -116,7 +110,7 @@
 }
 
 template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::terminate() {
+void egl_object_t::LocalRef<N, T>::terminate() {
     if (ref) {
         ref->terminate();
     }
@@ -128,6 +122,7 @@
 protected:
     ~egl_surface_t();
     void terminate() override;
+
 public:
     typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
 
@@ -151,10 +146,13 @@
     // it's not hard to imagine native games accessing them.
     EGLSurface surface;
     EGLConfig config;
+
 private:
     ANativeWindow* win;
+
 public:
     egl_connection_t const* cnx;
+
 private:
     bool connected;
     void disconnect();
@@ -186,14 +184,15 @@
     egl_cta861_3_metadata egl_cta861_3_metadata;
 };
 
-class egl_context_t: public egl_object_t {
+class egl_context_t : public egl_object_t {
 protected:
     ~egl_context_t() {}
+
 public:
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
 
-    egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            egl_connection_t const* cnx, int version);
+    egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx,
+                  int version);
 
     void onLooseCurrent();
     void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -209,30 +208,22 @@
     std::vector<std::string> tokenized_gl_extensions;
 };
 
-// ----------------------------------------------------------------------------
+typedef egl_surface_t::Ref SurfaceRef;
+typedef egl_context_t::Ref ContextRef;
 
-typedef egl_surface_t::Ref  SurfaceRef;
-typedef egl_context_t::Ref  ContextRef;
-
-// ----------------------------------------------------------------------------
-
-template<typename NATIVE, typename EGL>
+template <typename NATIVE, typename EGL>
 static inline NATIVE* egl_to_native_cast(EGL arg) {
     return reinterpret_cast<NATIVE*>(arg);
 }
 
-static inline
-egl_surface_t* get_surface(EGLSurface surface) {
+static inline egl_surface_t* get_surface(EGLSurface surface) {
     return egl_to_native_cast<egl_surface_t>(surface);
 }
 
-static inline
-egl_context_t* get_context(EGLContext context) {
+static inline egl_context_t* get_context(EGLContext context) {
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif // ANDROID_EGL_OBJECT_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 570d36b..398efc0 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -76,71 +76,70 @@
  * NOTE: Both strings MUST have a single space as the last character.
  */
 
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
 
 // clang-format off
 // Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
-        "EGL_KHR_get_all_proc_addresses "
-        "EGL_ANDROID_presentation_time "
-        "EGL_KHR_swap_buffers_with_damage "
-        "EGL_ANDROID_get_native_client_buffer "
+const char* const gBuiltinExtensionString =
         "EGL_ANDROID_front_buffer_auto_refresh "
         "EGL_ANDROID_get_frame_timestamps "
-        "EGL_EXT_surface_SMPTE2086_metadata "
+        "EGL_ANDROID_get_native_client_buffer "
+        "EGL_ANDROID_presentation_time "
         "EGL_EXT_surface_CTA861_3_metadata "
+        "EGL_EXT_surface_SMPTE2086_metadata "
+        "EGL_KHR_get_all_proc_addresses "
+        "EGL_KHR_swap_buffers_with_damage "
         ;
 
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString  =
-        "EGL_KHR_image "                        // mandatory
-        "EGL_KHR_image_base "                   // mandatory
+// Allowed list of extensions exposed to applications if implemented in the vendor driver.
+const char* const gExtensionString  =
+        "EGL_ANDROID_image_native_buffer "      // mandatory
+        "EGL_ANDROID_native_fence_sync "        // strongly recommended
+        "EGL_ANDROID_recordable "               // mandatory
+        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
+        "EGL_EXT_create_context_robustness "
         "EGL_EXT_image_gl_colorspace "
-        "EGL_KHR_image_pixmap "
-        "EGL_KHR_lock_surface "
+        "EGL_EXT_pixel_format_float "
+        "EGL_EXT_protected_content "
+        "EGL_EXT_yuv_surface "
+        "EGL_IMG_context_priority "
+        "EGL_KHR_config_attribs "
+        "EGL_KHR_create_context "
+        "EGL_KHR_create_context_no_error "
+        "EGL_KHR_fence_sync "
         "EGL_KHR_gl_colorspace "
+        "EGL_KHR_gl_renderbuffer_image "
         "EGL_KHR_gl_texture_2D_image "
         "EGL_KHR_gl_texture_3D_image "
         "EGL_KHR_gl_texture_cubemap_image "
-        "EGL_KHR_gl_renderbuffer_image "
+        "EGL_KHR_image "                        // mandatory
+        "EGL_KHR_image_base "                   // mandatory
+        "EGL_KHR_image_pixmap "
+        "EGL_KHR_lock_surface "
+        "EGL_KHR_mutable_render_buffer "
+        "EGL_KHR_no_config_context "
+        "EGL_KHR_partial_update "               // strongly recommended
         "EGL_KHR_reusable_sync "
-        "EGL_KHR_fence_sync "
-        "EGL_KHR_create_context "
-        "EGL_KHR_config_attribs "
-        "EGL_KHR_surfaceless_context "
         "EGL_KHR_stream "
-        "EGL_KHR_stream_fifo "
-        "EGL_KHR_stream_producer_eglsurface "
         "EGL_KHR_stream_consumer_gltexture "
         "EGL_KHR_stream_cross_process_fd "
-        "EGL_EXT_create_context_robustness "
-        "EGL_NV_system_time "
-        "EGL_ANDROID_image_native_buffer "      // mandatory
+        "EGL_KHR_stream_fifo "
+        "EGL_KHR_stream_producer_eglsurface "
+        "EGL_KHR_surfaceless_context "
         "EGL_KHR_wait_sync "                    // strongly recommended
-        "EGL_ANDROID_recordable "               // mandatory
-        "EGL_KHR_partial_update "               // strongly recommended
-        "EGL_EXT_pixel_format_float "
-        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
-        "EGL_KHR_create_context_no_error "
-        "EGL_KHR_mutable_render_buffer "
-        "EGL_EXT_yuv_surface "
-        "EGL_EXT_protected_content "
-        "EGL_IMG_context_priority "
-        "EGL_KHR_no_config_context "
+        "EGL_NV_system_time "
         ;
 
-char const * const gClientExtensionString =
+const char* const gClientExtensionString =
+        "EGL_ANDROID_GLES_layers "
+        "EGL_ANGLE_platform_angle "
         "EGL_EXT_client_extensions "
         "EGL_KHR_platform_android "
-        "EGL_ANGLE_platform_angle "
-        "EGL_ANDROID_GLES_layers";
-// clang-format on
+        ;
 
 // extensions not exposed to applications but used by the ANDROID system
 //      "EGL_ANDROID_blob_cache "               // strongly recommended
-//      "EGL_IMG_hibernate_process "            // optional
-//      "EGL_ANDROID_native_fence_sync "        // strongly recommended
 //      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
 
 /*
@@ -150,105 +149,69 @@
  */
 static const extension_map_t sExtensionMap[] = {
     // EGL_KHR_lock_surface
-    { "eglLockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
-    { "eglUnlockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+    { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+    { "eglUnlockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
 
     // EGL_KHR_image, EGL_KHR_image_base
-    { "eglCreateImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
-    { "eglDestroyImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+    { "eglCreateImageKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+    { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
 
     // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
-    { "eglCreateSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
-    { "eglDestroySyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
-    { "eglClientWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
-    { "eglSignalSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
-    { "eglGetSyncAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+    { "eglCreateSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+    { "eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+    { "eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+    { "eglSignalSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+    { "eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
 
     // EGL_NV_system_time
-    { "eglGetSystemTimeFrequencyNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
-    { "eglGetSystemTimeNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+    { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+    { "eglGetSystemTimeNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
 
     // EGL_KHR_wait_sync
-    { "eglWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+    { "eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
 
     // EGL_ANDROID_presentation_time
-    { "eglPresentationTimeANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+    { "eglPresentationTimeANDROID", (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
 
     // EGL_KHR_swap_buffers_with_damage
-    { "eglSwapBuffersWithDamageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+    { "eglSwapBuffersWithDamageKHR", (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
 
     // EGL_ANDROID_get_native_client_buffer
-    { "eglGetNativeClientBufferANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+    { "eglGetNativeClientBufferANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
 
     // EGL_KHR_partial_update
-    { "eglSetDamageRegionKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+    { "eglSetDamageRegionKHR", (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
 
-    { "eglCreateStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
-    { "eglDestroyStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
-    { "eglStreamAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
-    { "eglQueryStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
-    { "eglQueryStreamu64KHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
-    { "eglQueryStreamTimeKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
-    { "eglCreateStreamProducerSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
-    { "eglStreamConsumerGLTextureExternalKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
-    { "eglStreamConsumerAcquireKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
-    { "eglStreamConsumerReleaseKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
-    { "eglGetStreamFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
-    { "eglCreateStreamFromFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+    { "eglCreateStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+    { "eglDestroyStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+    { "eglStreamAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+    { "eglQueryStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+    { "eglQueryStreamu64KHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+    { "eglQueryStreamTimeKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+    { "eglCreateStreamProducerSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+    { "eglStreamConsumerGLTextureExternalKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+    { "eglStreamConsumerAcquireKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+    { "eglStreamConsumerReleaseKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+    { "eglGetStreamFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+    { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
 
     // EGL_ANDROID_get_frame_timestamps
-    { "eglGetNextFrameIdANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
-    { "eglGetCompositorTimingANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
-    { "eglGetCompositorTimingSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
-    { "eglGetFrameTimestampsANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
-    { "eglGetFrameTimestampSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+    { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+    { "eglGetCompositorTimingANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+    { "eglGetCompositorTimingSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+    { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+    { "eglGetFrameTimestampSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
 
     // EGL_ANDROID_native_fence_sync
-    { "eglDupNativeFenceFDANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+    { "eglDupNativeFenceFDANDROID", (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
 };
+// clang-format on
 
 /*
  * These extensions entry-points should not be exposed to applications.
  * They're used internally by the Android EGL layer.
  */
-#define FILTER_EXTENSIONS(procname) \
-        (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
-         !strcmp((procname), "eglHibernateProcessIMG")      ||    \
-         !strcmp((procname), "eglAwakenProcessIMG"))
+#define FILTER_EXTENSIONS(procname) (!strcmp((procname), "eglSetBlobCacheFuncsANDROID"))
 
 // accesses protected by sExtensionMapMutex
 static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
@@ -257,9 +220,8 @@
 static int sGLExtensionSlot = 0;
 static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
 
-static void(*findProcAddress(const char* name,
-        const extension_map_t* map, size_t n))() {
-    for (uint32_t i=0 ; i<n ; i++) {
+static void (*findProcAddress(const char* name, const extension_map_t* map, size_t n))() {
+    for (uint32_t i = 0; i < n; i++) {
         if (!strcmp(name, map[i].name)) {
             return map[i].address;
         }
@@ -269,14 +231,17 @@
 
 // ----------------------------------------------------------------------------
 
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
 extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType
+        gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
 extern gl_hooks_t gHooksTrace;
 
 // ----------------------------------------------------------------------------
 
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+static inline EGLContext getContext() {
+    return egl_tls_t::getContext();
+}
 
 // ----------------------------------------------------------------------------
 
@@ -309,9 +274,8 @@
 // Initialization
 // ----------------------------------------------------------------------------
 
-EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
-    egl_display_ptr dp = get_display(dpy);
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+    egl_display_t* dp = get_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->initialize(major, minor);
@@ -319,13 +283,12 @@
     return res;
 }
 
-EGLBoolean eglTerminateImpl(EGLDisplay dpy)
-{
+EGLBoolean eglTerminateImpl(EGLDisplay dpy) {
     // NOTE: don't unload the drivers b/c some APIs can be called
     // after eglTerminate() has been called. eglTerminate() only
     // terminates an EGLDisplay, not a EGL itself.
 
-    egl_display_ptr dp = get_display(dpy);
+    egl_display_t* dp = get_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->terminate();
@@ -337,14 +300,12 @@
 // configuration
 // ----------------------------------------------------------------------------
 
-EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
-                             EGLConfig *configs,
-                             EGLint config_size, EGLint *num_config)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+                             EGLint* num_config) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    if (num_config==nullptr) {
+    if (num_config == nullptr) {
         return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
@@ -353,96 +314,88 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso) {
-        res = cnx->egl.eglGetConfigs(
-                dp->disp.dpy, configs, config_size, num_config);
+        res = cnx->egl.eglGetConfigs(dp->disp.dpy, configs, config_size, num_config);
     }
 
     return res;
 }
 
-EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
-                                EGLConfig *configs, EGLint config_size,
-                                EGLint *num_config)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglChooseConfigImpl(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+                               EGLint config_size, EGLint* num_config) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    if (num_config==nullptr) {
+    if (num_config == nullptr) {
         return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
-    EGLBoolean res = EGL_FALSE;
     *num_config = 0;
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        if (attrib_list) {
-            if (base::GetBoolProperty("debug.egl.force_msaa", false)) {
-                size_t attribCount = 0;
-                EGLint attrib = attrib_list[0];
+    if (!cnx->dso) return EGL_FALSE;
 
-                // Only enable MSAA if the context is OpenGL ES 2.0 and
-                // if no caveat is requested
-                const EGLint *attribRendererable = nullptr;
-                const EGLint *attribCaveat = nullptr;
+    if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false))
+        return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size,
+                                        num_config);
 
-                // Count the number of attributes and look for
-                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
-                while (attrib != EGL_NONE) {
-                    attrib = attrib_list[attribCount];
-                    switch (attrib) {
-                        case EGL_RENDERABLE_TYPE:
-                            attribRendererable = &attrib_list[attribCount];
-                            break;
-                        case EGL_CONFIG_CAVEAT:
-                            attribCaveat = &attrib_list[attribCount];
-                            break;
-                        default:
-                            break;
-                    }
-                    attribCount++;
-                }
+    // Force 4x MSAA
+    size_t attribCount = 0;
+    EGLint attrib = attrib_list[0];
 
-                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
-                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+    // Only enable MSAA if the context is OpenGL ES 2.0 and
+    // if no caveat is requested
+    const EGLint* attribRendererable = nullptr;
+    const EGLint* attribCaveat = nullptr;
 
-                    // Insert 2 extra attributes to force-enable MSAA 4x
-                    EGLint aaAttribs[attribCount + 4];
-                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
-                    aaAttribs[1] = 1;
-                    aaAttribs[2] = EGL_SAMPLES;
-                    aaAttribs[3] = 4;
-
-                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
-                    EGLint numConfigAA;
-                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
-                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
-                    if (resAA == EGL_TRUE && numConfigAA > 0) {
-                        ALOGD("Enabling MSAA 4x");
-                        *num_config = numConfigAA;
-                        return resAA;
-                    }
-                }
-            }
+    // Count the number of attributes and look for
+    // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+    while (attrib != EGL_NONE) {
+        attrib = attrib_list[attribCount];
+        switch (attrib) {
+            case EGL_RENDERABLE_TYPE:
+                attribRendererable = &attrib_list[attribCount];
+                break;
+            case EGL_CONFIG_CAVEAT:
+                attribCaveat = &attrib_list[attribCount];
+                break;
+            default:
+                break;
         }
-
-        res = cnx->egl.eglChooseConfig(
-                dp->disp.dpy, attrib_list, configs, config_size, num_config);
+        attribCount++;
     }
-    return res;
+
+    if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+        // Insert 2 extra attributes to force-enable MSAA 4x
+        EGLint aaAttribs[attribCount + 4];
+        aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+        aaAttribs[1] = 1;
+        aaAttribs[2] = EGL_SAMPLES;
+        aaAttribs[3] = 4;
+
+        memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+        EGLint numConfigAA;
+        EGLBoolean resAA = cnx->egl.eglChooseConfig(dp->disp.dpy, aaAttribs, configs, config_size,
+                                                    &numConfigAA);
+
+        if (resAA == EGL_TRUE && numConfigAA > 0) {
+            ALOGD("Enabling MSAA 4x");
+            *num_config = numConfigAA;
+            return resAA;
+        }
+    }
+
+    return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, num_config);
 }
 
-EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
-        EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attribute,
+                                  EGLint* value) {
     egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    const egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (!dp) return EGL_FALSE;
 
-    return cnx->egl.eglGetConfigAttrib(
-            dp->disp.dpy, config, attribute, value);
+    return cnx->egl.eglGetConfigAttrib(dp->disp.dpy, config, attribute, value);
 }
 
 // ----------------------------------------------------------------------------
@@ -480,7 +433,7 @@
 }
 
 // Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+static std::vector<EGLint> getDriverColorSpaces(egl_display_t* dp) {
     std::vector<EGLint> colorSpaces;
 
     // sRGB and linear are always supported when color space support is present.
@@ -505,7 +458,8 @@
     if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
         colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
     }
-    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+    if (findExtension(dp->disp.queryString.extensions,
+                      "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
         colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
     }
     return colorSpaces;
@@ -515,7 +469,7 @@
 // If there is no color space attribute in attrib_list, colorSpace is left
 // unmodified.
 template <typename AttrType>
-static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+static EGLBoolean processAttributes(egl_display_t* dp, ANativeWindow* window,
                                     const AttrType* attrib_list, EGLint* colorSpace,
                                     std::vector<AttrType>* strippedAttribList) {
     for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
@@ -695,7 +649,7 @@
 }
 
 template <typename AttrType, typename CreateFuncType>
-EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, EGLConfig config,
                                       ANativeWindow* window, const AttrType* attrib_list,
                                       CreateFuncType createWindowSurfaceFunc) {
     const AttrType* origAttribList = attrib_list;
@@ -764,7 +718,7 @@
 
     EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
     if (surface != EGL_NO_SURFACE) {
-        egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+        egl_surface_t* s = new egl_surface_t(dp, config, window, surface,
                                              getReportedColorSpace(colorSpace), cnx);
         return s;
     }
@@ -785,8 +739,8 @@
 
 EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
                                       const EGLint* attrib_list) {
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    egl_connection_t* cnx = nullptr;
+    egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (dp) {
         return eglCreateWindowSurfaceTmpl<
                 EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
@@ -797,8 +751,8 @@
 
 EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
                                               const EGLAttrib* attrib_list) {
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    egl_connection_t* cnx = nullptr;
+    egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (dp) {
         if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
             if (cnx->egl.eglCreatePlatformWindowSurface) {
@@ -838,8 +792,8 @@
     // belongs to the Android platform. Any such call fails and generates
     // an EGL_BAD_PARAMETER error.
 
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    egl_connection_t* cnx = nullptr;
+    const egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (dp) {
         return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
     }
@@ -849,7 +803,7 @@
 EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
                                       NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
     egl_connection_t* cnx = nullptr;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    const egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (dp) {
         return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
     }
@@ -859,36 +813,33 @@
 EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
                                        const EGLint* attrib_list) {
     egl_connection_t* cnx = nullptr;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
+    egl_display_t* dp = validate_display_connection(dpy, &cnx);
+    if (!dp) return EGL_NO_SURFACE;
 
-        // Select correct colorspace based on user's attribute list
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return EGL_NO_SURFACE;
-        }
-        attrib_list = strippedAttribList.data();
+    EGLDisplay iDpy = dp->disp.dpy;
+    android_pixel_format format;
+    getNativePixelFormat(iDpy, cnx, config, &format);
 
-        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
-                                                 getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
+    // Select correct colorspace based on user's attribute list
+    EGLint colorSpace = EGL_UNKNOWN;
+    std::vector<EGLint> strippedAttribList;
+    if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+        ALOGE("error invalid colorspace: %d", colorSpace);
+        return EGL_NO_SURFACE;
     }
-    return EGL_NO_SURFACE;
+    attrib_list = strippedAttribList.data();
+
+    EGLSurface surface = cnx->egl.eglCreatePbufferSurface(iDpy, config, attrib_list);
+    if (surface == EGL_NO_SURFACE) return surface;
+
+    return new egl_surface_t(dp, config, nullptr, surface, getReportedColorSpace(colorSpace), cnx);
 }
 
 EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t* const s = get_surface(surface);
@@ -901,10 +852,10 @@
 
 EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
                                EGLint* value) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const* const s = get_surface(surface);
@@ -919,12 +870,12 @@
 }
 
 void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return;
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
     }
@@ -934,14 +885,13 @@
 // Contexts
 // ----------------------------------------------------------------------------
 
-EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
-                                EGLContext share_list, const EGLint *attrib_list)
-{
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+                                const EGLint* attrib_list) {
     egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    const egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (dp) {
         if (share_list != EGL_NO_CONTEXT) {
-            if (!ContextRef(dp.get(), share_list).get()) {
+            if (!ContextRef(dp, share_list).get()) {
                 return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
             }
             egl_context_t* const c = get_context(share_list);
@@ -963,8 +913,8 @@
                 }
             };
         }
-        EGLContext context = cnx->egl.eglCreateContext(
-                dp->disp.dpy, config, share_list, attrib_list);
+        EGLContext context =
+                cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
         if (context != EGL_NO_CONTEXT) {
             // figure out if it's a GLESv1 or GLESv2
             int version = egl_connection_t::GLESv1_INDEX;
@@ -988,17 +938,14 @@
     return EGL_NO_CONTEXT;
 }
 
-EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
-{
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp)
-        return EGL_FALSE;
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) {
+    const egl_display_t* dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
 
-    ContextRef _c(dp.get(), ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    ContextRef _c(dp, ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
-    egl_context_t * const c = get_context(ctx);
+    egl_context_t* const c = get_context(ctx);
     EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
     if (result == EGL_TRUE) {
         _c.terminate();
@@ -1006,24 +953,21 @@
     return result;
 }
 
-EGLBoolean eglMakeCurrentImpl(  EGLDisplay dpy, EGLSurface draw,
-                                EGLSurface read, EGLContext ctx)
-{
-    egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglMakeCurrentImpl(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+    egl_display_t* dp = validate_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
     // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
     // a valid but uninitialized display.
-    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
-         (draw != EGL_NO_SURFACE) ) {
+    if ((ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || (draw != EGL_NO_SURFACE)) {
         if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
 
     // get a reference to the object passed in
-    ContextRef _c(dp.get(), ctx);
-    SurfaceRef _d(dp.get(), draw);
-    SurfaceRef _r(dp.get(), read);
+    ContextRef _c(dp, ctx);
+    SurfaceRef _d(dp, draw);
+    SurfaceRef _r(dp, read);
 
     // validate the context (if not EGL_NO_CONTEXT)
     if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -1032,17 +976,17 @@
     }
 
     // these are the underlying implementation's object
-    EGLContext impl_ctx  = EGL_NO_CONTEXT;
+    EGLContext impl_ctx = EGL_NO_CONTEXT;
     EGLSurface impl_draw = EGL_NO_SURFACE;
     EGLSurface impl_read = EGL_NO_SURFACE;
 
     // these are our objects structs passed in
-    egl_context_t       * c = nullptr;
-    egl_surface_t const * d = nullptr;
-    egl_surface_t const * r = nullptr;
+    egl_context_t* c = nullptr;
+    egl_surface_t const* d = nullptr;
+    egl_surface_t const* r = nullptr;
 
     // these are the current objects structs
-    egl_context_t * cur_c = get_context(getContext());
+    egl_context_t* cur_c = get_context(getContext());
 
     if (ctx != EGL_NO_CONTEXT) {
         c = get_context(ctx);
@@ -1074,10 +1018,7 @@
         impl_read = r->surface;
     }
 
-
-    EGLBoolean result = dp->makeCurrent(c, cur_c,
-            draw, read, ctx,
-            impl_draw, impl_read, impl_ctx);
+    EGLBoolean result = dp->makeCurrent(c, cur_c, draw, read, ctx, impl_draw, impl_read, impl_ctx);
 
     if (result == EGL_TRUE) {
         if (c) {
@@ -1098,81 +1039,72 @@
     return result;
 }
 
-EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
-                                EGLint attribute, EGLint *value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryContextImpl(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    ContextRef _c(dp.get(), ctx);
+    ContextRef _c(dp, ctx);
     if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
-    egl_context_t * const c = get_context(ctx);
-    return c->cnx->egl.eglQueryContext(
-            dp->disp.dpy, c->context, attribute, value);
-
+    egl_context_t* const c = get_context(ctx);
+    return c->cnx->egl.eglQueryContext(dp->disp.dpy, c->context, attribute, value);
 }
 
-EGLContext eglGetCurrentContextImpl(void)
-{
+EGLContext eglGetCurrentContextImpl(void) {
     // could be called before eglInitialize(), but we wouldn't have a context
     // then, and this function would correctly return EGL_NO_CONTEXT.
     EGLContext ctx = getContext();
     return ctx;
 }
 
-EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
-{
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) {
     // could be called before eglInitialize(), but we wouldn't have a context
     // then, and this function would correctly return EGL_NO_SURFACE.
 
     EGLContext ctx = getContext();
     if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
+        egl_context_t const* const c = get_context(ctx);
         if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
         switch (readdraw) {
-            case EGL_READ: return c->read;
-            case EGL_DRAW: return c->draw;
-            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+            case EGL_READ:
+                return c->read;
+            case EGL_DRAW:
+                return c->draw;
+            default:
+                return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
         }
     }
     return EGL_NO_SURFACE;
 }
 
-EGLDisplay eglGetCurrentDisplayImpl(void)
-{
+EGLDisplay eglGetCurrentDisplayImpl(void) {
     // could be called before eglInitialize(), but we wouldn't have a context
     // then, and this function would correctly return EGL_NO_DISPLAY.
 
     EGLContext ctx = getContext();
     if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
+        egl_context_t const* const c = get_context(ctx);
         if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
         return c->dpy;
     }
     return EGL_NO_DISPLAY;
 }
 
-EGLBoolean eglWaitGLImpl(void)
-{
+EGLBoolean eglWaitGLImpl(void) {
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitGL();
 }
 
-EGLBoolean eglWaitNativeImpl(EGLint engine)
-{
+EGLBoolean eglWaitNativeImpl(EGLint engine) {
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitNative(engine);
 }
 
-EGLint eglGetErrorImpl(void)
-{
+EGLint eglGetErrorImpl(void) {
     EGLint err = EGL_SUCCESS;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso) {
@@ -1184,8 +1116,7 @@
     return err;
 }
 
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
-        const char* procname) {
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(const char* procname) {
     const egl_connection_t* cnx = &gEGLImpl;
     void* proc = nullptr;
 
@@ -1201,8 +1132,7 @@
     return nullptr;
 }
 
-__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) {
     if (FILTER_EXTENSIONS(procname)) {
         return nullptr;
     }
@@ -1249,13 +1179,10 @@
         // Ensure we have room to track it
         const int slot = sGLExtensionSlot;
         if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
-
             if (cnx->dso && cnx->egl.eglGetProcAddress) {
-
                 // Extensions are independent of the bound context
                 addr = cnx->egl.eglGetProcAddress(procname);
                 if (addr) {
-
                     // purposefully track the bottom of the stack in extensionMap
                     extensionMap[name] = addr;
 
@@ -1264,7 +1191,7 @@
 
                     // Track the top most entry point return the extension forwarder
                     cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
-                    cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+                            cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
                     addr = gExtensionForwarders[slot];
 
                     // Remember the slot for this extension
@@ -1296,7 +1223,7 @@
 
         // Track the top most entry point and return the extension forwarder
         cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
-        cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
         addr = gExtensionForwarders[ext_slot];
     }
 
@@ -1306,7 +1233,6 @@
 
 class FrameCompletionThread {
 public:
-
     static void queueSync(EGLSyncKHR sync) {
         static FrameCompletionThread thread;
 
@@ -1323,7 +1249,6 @@
     }
 
 private:
-
     FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
         std::thread thread(&FrameCompletionThread::loop, this);
         thread.detach();
@@ -1378,15 +1303,13 @@
     std::mutex mMutex;
 };
 
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
-        EGLint *rects, EGLint n_rects)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+                                           EGLint n_rects) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), draw);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, draw);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     if (n_rects < 0 || (n_rects > 0 && rects == NULL))
         return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
@@ -1402,11 +1325,11 @@
 
     if (CC_UNLIKELY(dp->finishOnSwap)) {
         uint32_t pixel;
-        egl_context_t * const c = get_context( egl_tls_t::getContext() );
+        egl_context_t* const c = get_context(egl_tls_t::getContext());
         if (c) {
             // glReadPixels() ensures that the frame is complete
-            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
-                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+            s->cnx->hooks[c->version]->gl.glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+                                                       &pixel);
         }
     }
 
@@ -1441,41 +1364,35 @@
     }
 
     if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
-        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
-    } else {
-        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, rects, n_rects);
     }
+
+    return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
 }
 
-EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) {
     return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
 }
 
-EGLBoolean eglCopyBuffersImpl(  EGLDisplay dpy, EGLSurface surface,
-                                NativePixmapType target)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglCopyBuffersImpl(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
 }
 
-const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
-{
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) {
     if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
         // Return list of client extensions
         return gClientExtensionString;
     }
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) nullptr;
+    const egl_display_t* dp = validate_display(dpy);
+    if (!dp) return (const char*)nullptr;
 
     switch (name) {
         case EGL_VENDOR:
@@ -1489,13 +1406,12 @@
         default:
             break;
     }
-    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+    return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
 }
 
-EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
-{
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) nullptr;
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) {
+    const egl_display_t* dp = validate_display(dpy);
+    if (!dp) return (const char*)nullptr;
 
     switch (name) {
         case EGL_VENDOR:
@@ -1509,24 +1425,22 @@
         default:
             break;
     }
-    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+    return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
 }
 
 // ----------------------------------------------------------------------------
 // EGL 1.1
 // ----------------------------------------------------------------------------
 
-EGLBoolean eglSurfaceAttribImpl(
-        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+                                EGLint value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t * const s = get_surface(surface);
+    egl_surface_t* const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
         if (!s->getNativeWindow()) {
@@ -1549,51 +1463,41 @@
     } else if (s->setCta8613Attribute(attribute, value)) {
         return EGL_TRUE;
     } else if (s->cnx->egl.eglSurfaceAttrib) {
-        return s->cnx->egl.eglSurfaceAttrib(
-                dp->disp.dpy, s->surface, attribute, value);
+        return s->cnx->egl.eglSurfaceAttrib(dp->disp.dpy, s->surface, attribute, value);
     }
     return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
-EGLBoolean eglBindTexImageImpl(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglBindTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     if (s->cnx->egl.eglBindTexImage) {
-        return s->cnx->egl.eglBindTexImage(
-                dp->disp.dpy, s->surface, buffer);
+        return s->cnx->egl.eglBindTexImage(dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
-EGLBoolean eglReleaseTexImageImpl(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglReleaseTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     if (s->cnx->egl.eglReleaseTexImage) {
-        return s->cnx->egl.eglReleaseTexImage(
-                dp->disp.dpy, s->surface, buffer);
+        return s->cnx->egl.eglReleaseTexImage(dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
-EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean res = EGL_TRUE;
@@ -1605,16 +1509,13 @@
     return res;
 }
 
-
 // ----------------------------------------------------------------------------
 // EGL 1.2
 // ----------------------------------------------------------------------------
 
-EGLBoolean eglWaitClientImpl(void)
-{
+EGLBoolean eglWaitClientImpl(void) {
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res;
     if (cnx->egl.eglWaitClient) {
@@ -1625,8 +1526,7 @@
     return res;
 }
 
-EGLBoolean eglBindAPIImpl(EGLenum api)
-{
+EGLBoolean eglBindAPIImpl(EGLenum api) {
     // bind this API on all EGLs
     EGLBoolean res = EGL_TRUE;
     egl_connection_t* const cnx = &gEGLImpl;
@@ -1636,8 +1536,7 @@
     return res;
 }
 
-EGLenum eglQueryAPIImpl(void)
-{
+EGLenum eglQueryAPIImpl(void) {
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglQueryAPI) {
         return cnx->egl.eglQueryAPI();
@@ -1647,8 +1546,7 @@
     return EGL_OPENGL_ES_API;
 }
 
-EGLBoolean eglReleaseThreadImpl(void)
-{
+EGLBoolean eglReleaseThreadImpl(void) {
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglReleaseThread) {
         cnx->egl.eglReleaseThread();
@@ -1661,16 +1559,15 @@
     return EGL_TRUE;
 }
 
-EGLSurface eglCreatePbufferFromClientBufferImpl(
-          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
-          EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBufferImpl(EGLDisplay dpy, EGLenum buftype,
+                                                EGLClientBuffer buffer, EGLConfig config,
+                                                const EGLint* attrib_list) {
     egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    const egl_display_t* dp = validate_display_connection(dpy, &cnx);
     if (!dp) return EGL_FALSE;
     if (cnx->egl.eglCreatePbufferFromClientBuffer) {
-        return cnx->egl.eglCreatePbufferFromClientBuffer(
-                dp->disp.dpy, buftype, buffer, config, attrib_list);
+        return cnx->egl.eglCreatePbufferFromClientBuffer(dp->disp.dpy, buftype, buffer, config,
+                                                         attrib_list);
     }
     return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
 }
@@ -1679,34 +1576,28 @@
 // EGL_EGLEXT_VERSION 3
 // ----------------------------------------------------------------------------
 
-EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
-        const EGLint *attrib_list)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     if (s->cnx->egl.eglLockSurfaceKHR) {
-        return s->cnx->egl.eglLockSurfaceKHR(
-                dp->disp.dpy, s->surface, attrib_list);
+        return s->cnx->egl.eglLockSurfaceKHR(dp->disp.dpy, s->surface, attrib_list);
     }
     return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 }
 
-EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    SurfaceRef _s(dp, surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     if (s->cnx->egl.eglUnlockSurfaceKHR) {
         return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
     }
@@ -1719,7 +1610,7 @@
 EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
                                EGLClientBuffer buffer, const AttrType* attrib_list,
                                FuncType eglCreateImageFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_IMAGE_KHR;
 
     std::vector<AttrType> strippedAttribs;
@@ -1728,7 +1619,7 @@
         // EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
         // EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
         // but some drivers don't like the DEFAULT value and generate an error.
-        for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+        for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
             if (attr[0] == EGL_GL_COLORSPACE_KHR &&
                 dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
                 if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
@@ -1742,14 +1633,15 @@
         strippedAttribs.push_back(EGL_NONE);
     }
 
-    ContextRef _c(dp.get(), ctx);
+    ContextRef _c(dp, ctx);
     egl_context_t* const c = _c.get();
 
     EGLImageKHR result = EGL_NO_IMAGE_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && eglCreateImageFunc) {
         result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
-                                    needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list);
+                                    needsAndroidPEglMitigation() ? strippedAttribs.data()
+                                                                 : attrib_list);
     }
     return result;
 }
@@ -1788,7 +1680,7 @@
 
 EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
                                PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1825,7 +1717,7 @@
 template <typename AttrType, typename FuncType>
 EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
                              FuncType eglCreateSyncFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_SYNC_KHR;
 
     egl_connection_t* const cnx = &gEGLImpl;
@@ -1864,7 +1756,7 @@
 
 EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
                               PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1893,7 +1785,7 @@
 }
 
 EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1906,7 +1798,7 @@
 
 EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
                              PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLint result = EGL_FALSE;
@@ -1938,7 +1830,7 @@
 template <typename AttrType, typename FuncType>
 EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
                                 FuncType eglGetSyncAttribFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1983,106 +1875,93 @@
                                                                             .eglGetSyncAttribKHR);
 }
 
-EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint* attrib_list) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_STREAM_KHR;
 
     EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
-        result = cnx->egl.eglCreateStreamKHR(
-                dp->disp.dpy, attrib_list);
+        result = cnx->egl.eglCreateStreamKHR(dp->disp.dpy, attrib_list);
     }
     return result;
 }
 
-EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
-        result = cnx->egl.eglDestroyStreamKHR(
-                dp->disp.dpy, stream);
+        result = cnx->egl.eglDestroyStreamKHR(dp->disp.dpy, stream);
     }
     return result;
 }
 
-EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                  EGLint value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
-        result = cnx->egl.eglStreamAttribKHR(
-                dp->disp.dpy, stream, attribute, value);
+        result = cnx->egl.eglStreamAttribKHR(dp->disp.dpy, stream, attribute, value);
     }
     return result;
 }
 
-EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint *value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                 EGLint* value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
-        result = cnx->egl.eglQueryStreamKHR(
-                dp->disp.dpy, stream, attribute, value);
+        result = cnx->egl.eglQueryStreamKHR(dp->disp.dpy, stream, attribute, value);
     }
     return result;
 }
 
-EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLuint64KHR *value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                    EGLuint64KHR* value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
-        result = cnx->egl.eglQueryStreamu64KHR(
-                dp->disp.dpy, stream, attribute, value);
+        result = cnx->egl.eglQueryStreamu64KHR(dp->disp.dpy, stream, attribute, value);
     }
     return result;
 }
 
-EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLTimeKHR *value)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                     EGLTimeKHR* value) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
-        result = cnx->egl.eglQueryStreamTimeKHR(
-                dp->disp.dpy, stream, attribute, value);
+        result = cnx->egl.eglQueryStreamTimeKHR(dp->disp.dpy, stream, attribute, value);
     }
     return result;
 }
 
 EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
-        EGLStreamKHR stream, const EGLint *attrib_list)
-{
-    egl_display_ptr dp = validate_display(dpy);
+                                                 EGLStreamKHR stream, const EGLint* attrib_list) {
+    egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_SURFACE;
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
-        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
-                dp->disp.dpy, config, stream, attrib_list);
+        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(dp->disp.dpy, config,
+                                                                        stream, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+            egl_surface_t* s = new egl_surface_t(dp, config, nullptr, surface,
                                                  EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
             return s;
         }
@@ -2090,77 +1969,63 @@
     return EGL_NO_SURFACE;
 }
 
-EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
-        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
-                dp->disp.dpy, stream);
+        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(dp->disp.dpy, stream);
     }
     return result;
 }
 
-EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
-        result = cnx->egl.eglStreamConsumerAcquireKHR(
-                dp->disp.dpy, stream);
+        result = cnx->egl.eglStreamConsumerAcquireKHR(dp->disp.dpy, stream);
     }
     return result;
 }
 
-EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
-        result = cnx->egl.eglStreamConsumerReleaseKHR(
-                dp->disp.dpy, stream);
+        result = cnx->egl.eglStreamConsumerReleaseKHR(dp->disp.dpy, stream);
     }
     return result;
 }
 
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
-        EGLDisplay dpy, EGLStreamKHR stream)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
 
     EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
-        result = cnx->egl.eglGetStreamFileDescriptorKHR(
-                dp->disp.dpy, stream);
+        result = cnx->egl.eglGetStreamFileDescriptorKHR(dpy, stream);
     }
     return result;
 }
 
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
-        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(EGLDisplay dpy,
+                                                      EGLNativeFileDescriptorKHR file_descriptor) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_STREAM_KHR;
 
     EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
-        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
-                dp->disp.dpy, file_descriptor);
+        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(dp->disp.dpy, file_descriptor);
     }
     return result;
 }
@@ -2173,7 +2038,7 @@
 template <typename ReturnType, typename FuncType>
 ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
                            FuncType eglWaitSyncFunc) {
-    const egl_display_ptr dp = validate_display(dpy);
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
     ReturnType result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
@@ -2210,9 +2075,8 @@
 // ANDROID extensions
 // ----------------------------------------------------------------------------
 
-EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
 
     EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
@@ -2224,35 +2088,33 @@
 }
 
 EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
-        EGLnsecsANDROID time)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+                                          EGLnsecsANDROID time) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return EGL_FALSE;
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
         return EGL_FALSE;
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     native_window_set_buffers_timestamp(s->getNativeWindow(), time);
 
     return EGL_TRUE;
 }
 
-EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) {
     if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+    return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
 }
 
 // ----------------------------------------------------------------------------
 // NVIDIA extensions
 // ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl() {
     EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
 
@@ -2263,8 +2125,7 @@
     return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
-EGLuint64NV eglGetSystemTimeNVImpl()
-{
+EGLuint64NV eglGetSystemTimeNVImpl() {
     EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
 
@@ -2278,43 +2139,40 @@
 // ----------------------------------------------------------------------------
 // Partial update extension
 // ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
-        EGLint *rects, EGLint n_rects)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+                                     EGLint n_rects) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         setError(EGL_BAD_DISPLAY, EGL_FALSE);
         return EGL_FALSE;
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
         return EGL_FALSE;
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
     if (s->cnx->egl.eglSetDamageRegionKHR) {
-        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
+        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, rects, n_rects);
     }
 
     return EGL_FALSE;
 }
 
-EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
-            EGLuint64KHR *frameId) {
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
 
     if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2335,19 +2193,19 @@
 }
 
 EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
-        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+                                             EGLint numTimestamps, const EGLint* names,
+                                             EGLnsecsANDROID* values) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
 
     if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2373,36 +2231,35 @@
         }
     }
 
-    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
-            compositeDeadline, compositeInterval, compositeToPresentLatency);
+    int ret = native_window_get_compositor_timing(s->getNativeWindow(), compositeDeadline,
+                                                  compositeInterval, compositeToPresentLatency);
 
     switch (ret) {
-      case 0:
-        return EGL_TRUE;
-      case -ENOSYS:
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-      default:
-        // This should not happen. Return an error that is not in the spec
-        // so it's obvious something is very wrong.
-        ALOGE("eglGetCompositorTiming: Unexpected error.");
-        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+        case 0:
+            return EGL_TRUE;
+        case -ENOSYS:
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        default:
+            // This should not happen. Return an error that is not in the spec
+            // so it's obvious something is very wrong.
+            ALOGE("eglGetCompositorTiming: Unexpected error.");
+            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
 }
 
-EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
-        EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+                                                      EGLint name) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
 
     ANativeWindow* window = s->getNativeWindow();
     if (!window) {
@@ -2420,20 +2277,19 @@
 }
 
 EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
-        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
-        EGLnsecsANDROID *values)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+                                            EGLuint64KHR frameId, EGLint numTimestamps,
+                                            const EGLint* timestamps, EGLnsecsANDROID* values) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
 
     if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2483,10 +2339,11 @@
         }
     }
 
-    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
-            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
-            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
-            dequeueReadyTime, releaseTime);
+    int ret =
+            native_window_get_frame_timestamps(s->getNativeWindow(), frameId, requestedPresentTime,
+                                               acquireTime, latchTime, firstRefreshStartTime,
+                                               lastRefreshStartTime, gpuCompositionDoneTime,
+                                               displayPresentTime, dequeueReadyTime, releaseTime);
 
     switch (ret) {
         case 0:
@@ -2505,20 +2362,19 @@
     }
 }
 
-EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
-        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
-    const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+                                                    EGLint timestamp) {
+    const egl_display_t* dp = validate_display(dpy);
     if (!dp) {
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
-    SurfaceRef _s(dp.get(), surface);
+    SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t const* const s = get_surface(surface);
 
     ANativeWindow* window = s->getNativeWindow();
     if (!window) {
@@ -2540,8 +2396,7 @@
             return EGL_TRUE;
         case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
             int value = 0;
-            window->query(window,
-                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+            window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
             return value == 0 ? EGL_FALSE : EGL_TRUE;
         }
         default:
@@ -2549,25 +2404,25 @@
     }
 }
 
-const GLubyte * glGetStringImpl(GLenum name) {
-    const GLubyte * ret = egl_get_string_for_current_context(name);
+const GLubyte* glGetStringImpl(GLenum name) {
+    const GLubyte* ret = egl_get_string_for_current_context(name);
     if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetString(name);
+        gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+        if (_c) ret = _c->glGetString(name);
     }
     return ret;
 }
 
-const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
-    const GLubyte * ret = egl_get_string_for_current_context(name, index);
+const GLubyte* glGetStringiImpl(GLenum name, GLuint index) {
+    const GLubyte* ret = egl_get_string_for_current_context(name, index);
     if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetStringi(name, index);
+        gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+        if (_c) ret = _c->glGetStringi(name, index);
     }
     return ret;
 }
 
-void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+void glGetBooleanvImpl(GLenum pname, GLboolean* data) {
     if (pname == GL_NUM_EXTENSIONS) {
         int num_exts = egl_get_num_extensions_for_current_context();
         if (num_exts >= 0) {
@@ -2576,11 +2431,11 @@
         }
     }
 
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetBooleanv(pname, data);
 }
 
-void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+void glGetFloatvImpl(GLenum pname, GLfloat* data) {
     if (pname == GL_NUM_EXTENSIONS) {
         int num_exts = egl_get_num_extensions_for_current_context();
         if (num_exts >= 0) {
@@ -2589,11 +2444,11 @@
         }
     }
 
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetFloatv(pname, data);
 }
 
-void glGetIntegervImpl(GLenum pname, GLint * data) {
+void glGetIntegervImpl(GLenum pname, GLint* data) {
     if (pname == GL_NUM_EXTENSIONS) {
         int num_exts = egl_get_num_extensions_for_current_context();
         if (num_exts >= 0) {
@@ -2602,11 +2457,11 @@
         }
     }
 
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetIntegerv(pname, data);
 }
 
-void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+void glGetInteger64vImpl(GLenum pname, GLint64* data) {
     if (pname == GL_NUM_EXTENSIONS) {
         int num_exts = egl_get_num_extensions_for_current_context();
         if (num_exts >= 0) {
@@ -2615,7 +2470,7 @@
         }
     }
 
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetInteger64v(pname, data);
 }
 
@@ -2624,8 +2479,8 @@
     EGLFuncPointer address;
 };
 
+// clang-format off
 static const implementation_map_t sPlatformImplMap[] = {
-        // clang-format off
     { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
     { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
     { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
@@ -2712,11 +2567,10 @@
     { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
     { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
     { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
-        // clang-format on
 };
+// clang-format on
 
-EGLFuncPointer FindPlatformImplAddr(const char* name)
-{
+EGLFuncPointer FindPlatformImplAddr(const char* name) {
     static const bool DEBUG = false;
 
     if (name == nullptr) {
@@ -2730,7 +2584,8 @@
             return nullptr;
         }
         if (!strcmp(name, sPlatformImplMap[i].name)) {
-            ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+            ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)",
+                  (unsigned long long)sPlatformImplMap[i].address, i, name);
             return sPlatformImplMap[i].address;
         }
     }
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8d118e0..dd1dcc2 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -16,10 +16,10 @@
 
 #include "egl_tls.h"
 
-#include <stdlib.h>
-
 #include <android-base/properties.h>
 #include <log/log.h>
+#include <stdlib.h>
+
 #include "CallStack.h"
 #include "egl_platform_entries.h"
 
@@ -28,33 +28,46 @@
 pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED;
 pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
 
-egl_tls_t::egl_tls_t()
-    : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
-}
+egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {}
 
-const char *egl_tls_t::egl_strerror(EGLint err) {
+const char* egl_tls_t::egl_strerror(EGLint err) {
     switch (err) {
-        case EGL_SUCCESS:               return "EGL_SUCCESS";
-        case EGL_NOT_INITIALIZED:       return "EGL_NOT_INITIALIZED";
-        case EGL_BAD_ACCESS:            return "EGL_BAD_ACCESS";
-        case EGL_BAD_ALLOC:             return "EGL_BAD_ALLOC";
-        case EGL_BAD_ATTRIBUTE:         return "EGL_BAD_ATTRIBUTE";
-        case EGL_BAD_CONFIG:            return "EGL_BAD_CONFIG";
-        case EGL_BAD_CONTEXT:           return "EGL_BAD_CONTEXT";
-        case EGL_BAD_CURRENT_SURFACE:   return "EGL_BAD_CURRENT_SURFACE";
-        case EGL_BAD_DISPLAY:           return "EGL_BAD_DISPLAY";
-        case EGL_BAD_MATCH:             return "EGL_BAD_MATCH";
-        case EGL_BAD_NATIVE_PIXMAP:     return "EGL_BAD_NATIVE_PIXMAP";
-        case EGL_BAD_NATIVE_WINDOW:     return "EGL_BAD_NATIVE_WINDOW";
-        case EGL_BAD_PARAMETER:         return "EGL_BAD_PARAMETER";
-        case EGL_BAD_SURFACE:           return "EGL_BAD_SURFACE";
-        case EGL_CONTEXT_LOST:          return "EGL_CONTEXT_LOST";
-        default: return "UNKNOWN";
+        case EGL_SUCCESS:
+            return "EGL_SUCCESS";
+        case EGL_NOT_INITIALIZED:
+            return "EGL_NOT_INITIALIZED";
+        case EGL_BAD_ACCESS:
+            return "EGL_BAD_ACCESS";
+        case EGL_BAD_ALLOC:
+            return "EGL_BAD_ALLOC";
+        case EGL_BAD_ATTRIBUTE:
+            return "EGL_BAD_ATTRIBUTE";
+        case EGL_BAD_CONFIG:
+            return "EGL_BAD_CONFIG";
+        case EGL_BAD_CONTEXT:
+            return "EGL_BAD_CONTEXT";
+        case EGL_BAD_CURRENT_SURFACE:
+            return "EGL_BAD_CURRENT_SURFACE";
+        case EGL_BAD_DISPLAY:
+            return "EGL_BAD_DISPLAY";
+        case EGL_BAD_MATCH:
+            return "EGL_BAD_MATCH";
+        case EGL_BAD_NATIVE_PIXMAP:
+            return "EGL_BAD_NATIVE_PIXMAP";
+        case EGL_BAD_NATIVE_WINDOW:
+            return "EGL_BAD_NATIVE_WINDOW";
+        case EGL_BAD_PARAMETER:
+            return "EGL_BAD_PARAMETER";
+        case EGL_BAD_SURFACE:
+            return "EGL_BAD_SURFACE";
+        case EGL_CONTEXT_LOST:
+            return "EGL_CONTEXT_LOST";
+        default:
+            return "UNKNOWN";
     }
 }
 
-void egl_tls_t::validateTLSKey()
-{
+void egl_tls_t::validateTLSKey() {
     struct TlsKeyInitializer {
         static void create() { pthread_key_create(&sKey, destructTLSData); }
     };
@@ -88,14 +101,12 @@
              "EGL TLS data still exists after eglReleaseThread");
 }
 
-void egl_tls_t::setErrorEtcImpl(
-        const char* caller, int line, EGLint error, bool quiet) {
+void egl_tls_t::setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet) {
     validateTLSKey();
     egl_tls_t* tls = getTLS();
     if (tls->error != error) {
         if (!quiet) {
-            ALOGE("%s:%d error %x (%s)",
-                    caller, line, error, egl_strerror(error));
+            ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
             if (base::GetBoolProperty("debug.egl.callstack", false)) {
                 CallStack::log(LOG_TAG);
             }
@@ -111,7 +122,6 @@
         return true;
     }
     return false;
-
 }
 
 egl_tls_t* egl_tls_t::getTLS() {
@@ -161,10 +171,9 @@
     if (sKey == TLS_KEY_NOT_INITIALIZED) {
         return EGL_NO_CONTEXT;
     }
-    egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey);
+    egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
     if (!tls) return EGL_NO_CONTEXT;
     return tls->ctx;
 }
 
-
 } // namespace android
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 86a375c..b5fcc1a 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -18,12 +18,9 @@
 #define ANDROID_EGL_TLS_H
 
 #include <EGL/egl.h>
-
 #include <pthread.h>
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 class DbgContext;
 
@@ -32,15 +29,14 @@
     static pthread_key_t sKey;
     static pthread_once_t sOnceKey;
 
-    EGLint      error;
-    EGLContext  ctx;
-    bool        logCallWithNoContext;
+    EGLint error;
+    EGLContext ctx;
+    bool logCallWithNoContext;
 
     egl_tls_t();
     static void validateTLSKey();
     static void destructTLSData(void* data);
-    static void setErrorEtcImpl(
-            const char* caller, int line, EGLint error, bool quiet);
+    static void setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet);
 
 public:
     static egl_tls_t* getTLS();
@@ -50,24 +46,20 @@
     static void setContext(EGLContext ctx);
     static EGLContext getContext();
     static bool logNoContextCall();
-    static const char *egl_strerror(EGLint err);
+    static const char* egl_strerror(EGLint err);
 
-    template<typename T>
-    static T setErrorEtc(const char* caller,
-            int line, EGLint error, T returnValue, bool quiet = false) {
+    template <typename T>
+    static T setErrorEtc(const char* caller, int line, EGLint error, T returnValue,
+                         bool quiet = false) {
         setErrorEtcImpl(caller, line, error, quiet);
         return returnValue;
     }
 };
 
-#define setError(_e, _r)        \
-    egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
 
-#define setErrorQuiet(_e, _r)   \
-    egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
+#define setErrorQuiet(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
index 7664de2..ffdf676 100644
--- a/opengl/libs/EGL/egl_trace.h
+++ b/opengl/libs/EGL/egl_trace.h
@@ -18,16 +18,14 @@
 
 #if defined(__ANDROID__)
 
-#include <stdint.h>
-
 #include <cutils/trace.h>
+#include <stdint.h>
 
 // See <cutils/trace.h> for more ATRACE_* macros.
 
 // ATRACE_NAME traces from its location until the end of its enclosing scope.
-#define _PASTE(x, y) x ## y
-#define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+#define PASTE(x, y) x##y
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
 
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -36,13 +34,9 @@
 
 class EglScopedTrace {
 public:
-    inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
-        atrace_begin(mTag, name);
-    }
+    inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
 
-    inline ~EglScopedTrace() {
-        atrace_end(mTag);
-    }
+    inline ~EglScopedTrace() { atrace_end(mTag); }
 
 private:
     uint64_t mTag;
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 5fbffbd..fcc11f1 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -17,40 +17,32 @@
 #ifndef ANDROID_EGLDEFS_H
 #define ANDROID_EGLDEFS_H
 
+#include <log/log.h>
+
 #include "../hooks.h"
 #include "egl_platform_entries.h"
 
-#include <log/log.h>
-
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 4
 #define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
-//  EGLDisplay are global, not attached to a given thread
+// EGLDisplay are global, not attached to a given thread
 const unsigned int NUM_DISPLAYS = 1;
 
-// ----------------------------------------------------------------------------
+extern const char* const platform_names[];
 
-extern char const * const platform_names[];
-
-// clang-format off
 struct egl_connection_t {
-    enum {
-        GLESv1_INDEX = 0,
-        GLESv2_INDEX = 1
-    };
+    enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 };
 
-    inline egl_connection_t() : dso(nullptr),
-                                libEgl(nullptr),
-                                libGles1(nullptr),
-                                libGles2(nullptr),
-                                systemDriverUnloaded(false) {
-
-        char const* const* entries = platform_names;
+    inline egl_connection_t()
+          : dso(nullptr),
+            libEgl(nullptr),
+            libGles1(nullptr),
+            libGles2(nullptr),
+            systemDriverUnloaded(false) {
+        const char* const* entries = platform_names;
         EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
         while (*entries) {
             const char* name = *entries;
@@ -66,41 +58,34 @@
         }
     }
 
-    void *              dso;
-    gl_hooks_t *        hooks[2];
-    EGLint              major;
-    EGLint              minor;
-    EGLint              driverVersion;
-    egl_t               egl;
+    void* dso;
+    gl_hooks_t* hooks[2];
+    EGLint major;
+    EGLint minor;
+    EGLint driverVersion;
+    egl_t egl;
 
     // Functions implemented or redirected by platform libraries
-    platform_impl_t     platform;
+    platform_impl_t platform;
 
-    void*               libEgl;
-    void*               libGles1;
-    void*               libGles2;
+    void* libEgl;
+    void* libGles1;
+    void* libGles2;
 
-    bool                systemDriverUnloaded;
-    bool                useAngle;       // Was ANGLE successfully loaded
+    bool systemDriverUnloaded;
+    bool useAngle; // Was ANGLE successfully loaded
 };
-// clang-format on
-
-// ----------------------------------------------------------------------------
 
 extern gl_hooks_t gHooks[2];
 extern gl_hooks_t gHooksNoContext;
 extern pthread_key_t gGLWrapperKey;
 extern "C" void gl_unimplemented();
 extern "C" void gl_noop();
-
-extern char const * const gl_names[];
-extern char const * const gl_names_1[];
-extern char const * const egl_names[];
-
+extern const char* const gl_names[];
+extern const char* const gl_names_1[];
+extern const char* const egl_names[];
 extern egl_connection_t gEGLImpl;
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
 
 #endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index fedc789..b3d6f74 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -16,15 +16,12 @@
 
 #include <ctype.h>
 #include <errno.h>
-#include <stdlib.h>
-
 #include <log/log.h>
+#include <stdlib.h>
 
 #include "egldefs.h"
 
-// ----------------------------------------------------------------------------
 namespace android {
-// ----------------------------------------------------------------------------
 
 #undef API_ENTRY
 #undef CALL_GL_EXTENSION_API
@@ -34,6 +31,7 @@
 #undef GL_EXTENSION_LIST
 #undef GET_TLS
 
+// clang-format off
 #if defined(__arm__)
 
     #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
@@ -239,13 +237,13 @@
     name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255)
 
 
-GL_EXTENSION_LIST( GL_EXTENSION )
+GL_EXTENSION_LIST(GL_EXTENSION)
 
-#define GL_EXTENSION_ARRAY(_n)  GL_EXTENSION_NAME(_n),
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+// clang-format on
 
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
-        GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
- };
+extern const __eglMustCastToProperFunctionPointerType
+        gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {GL_EXTENSION_LIST(GL_EXTENSION_ARRAY)};
 
 #undef GET_TLS
 #undef GL_EXTENSION_LIST
@@ -255,7 +253,4 @@
 #undef API_ENTRY
 #undef CALL_GL_EXTENSION_API
 
-// ----------------------------------------------------------------------------
 }; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 510226d..bbd786d 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -138,7 +138,7 @@
     };
     EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -148,7 +148,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -258,7 +258,7 @@
     EXPECT_EQ(components[2], 8);
     EXPECT_EQ(components[3], 8);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -268,7 +268,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
     EGLint winAttrs[] = {
@@ -306,7 +306,7 @@
 
     get8BitConfig(config);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -316,7 +316,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
     EGLint winAttrs[] = {
@@ -398,7 +398,7 @@
     EXPECT_EQ(components[2], 10);
     EXPECT_EQ(components[3], 2);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -408,7 +408,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
     EGLint winAttrs[] = {
@@ -570,7 +570,7 @@
 
     ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -580,7 +580,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -622,7 +622,7 @@
 
     ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -632,7 +632,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
     std::vector<EGLint> winAttrs;
@@ -705,7 +705,7 @@
     EXPECT_GE(components[2], 16);
     EXPECT_GE(components[3], 16);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -714,7 +714,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -734,7 +734,7 @@
 
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_KHR_no_config_context"));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -809,7 +809,7 @@
     EXPECT_EQ(components[2], 10);
     EXPECT_EQ(components[3], 2);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -819,7 +819,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -835,7 +835,7 @@
 
     ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -845,7 +845,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -882,7 +882,7 @@
     ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(1, numConfigs);
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -892,7 +892,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -913,7 +913,7 @@
 
     ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -923,7 +923,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
@@ -953,7 +953,7 @@
 
     ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
 
-    struct DummyConsumer : public BnConsumerListener {
+    struct MockConsumer : public BnConsumerListener {
         void onFrameAvailable(const BufferItem& /* item */) override {}
         void onBuffersReleased() override {}
         void onSidebandStreamChanged() override {}
@@ -963,7 +963,7 @@
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
-    consumer->consumerConnect(new DummyConsumer, false);
+    consumer->consumerConnect(new MockConsumer, false);
     sp<Surface> mSTC = new Surface(producer);
     sp<ANativeWindow> mANW = mSTC;
 
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 9efd38f..7fd9c3a 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -17,7 +17,7 @@
 
 mkdir out
 
-# Create dummy Java files for Android APIs that are used by the code we generate.
+# Create stub Java files for Android APIs that are used by the code we generate.
 # This allows us to test the generated code without building the rest of Android.
 
 mkdir -p out/javax/microedition/khronos/opengles
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9c80212..59ef3c8 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -15,6 +15,7 @@
  */
 
 import java.io.PrintStream;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -201,6 +202,33 @@
         out.println(iii + ");");
     }
 
+    // Function to automatically generate properly formatted function calls that
+    // comply with clang format rules
+    public static String formatFunctionCall(String indent, String functionCall) {
+        final int MAXLEN = 100;
+        String tokens[] = functionCall.split("\\(|\\)", 2);
+        String params[] = tokens[1].split(",\\s*");
+        String formatted = indent + tokens[0] + "(";
+        char[] chars = new char[indent.length() + tokens[0].length() + 1];
+        Arrays.fill(chars, ' ');
+        String multiIndent = new String(chars);
+        ArrayList<String> lines = new ArrayList<String>();
+        for(int i = 0; i < params.length; i++) {
+            String terminator = ((i == params.length - 1) ? "" : ",");
+            if(indent.length() + formatted.length() + params[i].length() > MAXLEN) {
+                lines.add(formatted);
+                if (!indent.equals(multiIndent)) {
+                    indent = multiIndent;
+                }
+                formatted = indent + params[i] + terminator;
+            } else {
+              formatted += (i == 0 ? "" : " ") + params[i] + terminator;
+            }
+        }
+        lines.add(formatted);
+        return String.join("\n", lines);
+    }
+
     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
             String iii) {
         printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
@@ -1538,14 +1566,19 @@
                                         "_exception ? JNI_ABORT : 0" : "0")) +
                                 ");");
                         } else {
-                            out.println(indent + indent +
+                            String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
+                                "_" + cfunc.getArgName(cIndex) + "BufferOffset";
+                            String typeCast = "(char *)" + cfunc.getArgName(cIndex);
+                            String withOffset = "(void *)(" + typeCast + " - " + bufferOffset + ")";
+                            String releasePointerCall = (
                                 "releasePointer(_env, " + array + ", " +
-                                cfunc.getArgName(cIndex) +
+                                withOffset +
                                 ", " +
                                 (cfunc.getArgType(cIndex).isConst() ?
                                     "JNI_FALSE" : (emitExceptionCheck ?
                                         "_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
                                 ");");
+                            out.println(formatFunctionCall(indent + indent, releasePointerCall));
                         }
                         out.println(indent + "}");
                     }
diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index 93203fd..b2ea041 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -20,7 +20,7 @@
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
index 34cb3e1..6dffac5 100644
--- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -20,7 +20,7 @@
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
index b3b0690..be8b3e3 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -20,7 +20,7 @@
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index e763b4e..d84a693 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -1,5 +1,5 @@
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 #include <assert.h>
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index c12efc3..9cab1d6 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -20,7 +20,7 @@
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index 4767406..321bb83 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,7 @@
     sp<IBinder> displayToken = nullptr;
     sp<SurfaceControl> surfaceControl = nullptr;
     if (it == mDisplays.end()) {
-        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
         if (displayToken == nullptr) {
             ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
             return nullptr;
@@ -145,7 +145,7 @@
     auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     ids.resize(displayIds.size());
     for (auto i = 0; i < displayIds.size(); ++i) {
-        ids[i] = displayIds[i];
+        ids[i] = displayIds[i].value;
     }
 
     _cb(ids);
@@ -157,7 +157,7 @@
     HwDisplayConfig activeConfig;
     HwDisplayState  activeState;
 
-    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
     if (displayToken == nullptr) {
         ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
     } else {
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index ffb7b5e..5c7f344 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -2,4 +2,3 @@
     class hal
     user graphics
     group automotive_evs
-    disabled  # will not automatically start with its class; must be explicitly started.
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 04fe1e6..9a9bca1 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -22,6 +22,7 @@
         "libcutils",
         "libgfxstats",
         "libgpumem",
+        "libgpumemtracer",
         "libgraphicsenv",
         "liblog",
         "libutils",
@@ -43,11 +44,11 @@
     defaults: ["libgpuservice_defaults"],
     cflags: [
         "-fvisibility=hidden",
-        "-fwhole-program-vtables", // requires ThinLTO
     ],
     lto: {
         thin: true,
     },
+    whole_program_vtables: true, // Requires ThinLTO
 }
 
 filegroup {
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index bf0d6a9..52d5d4f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -27,10 +27,13 @@
 #include <gpumem/GpuMem.h>
 #include <gpustats/GpuStats.h>
 #include <private/android_filesystem_config.h>
+#include <tracing/GpuMemTracer.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 #include <vkjson.h>
 
+#include <thread>
+
 namespace android {
 
 using base::StringAppendF;
@@ -46,8 +49,14 @@
 const char* const GpuService::SERVICE_NAME = "gpu";
 
 GpuService::GpuService()
-      : mGpuMem(std::make_unique<GpuMem>()), mGpuStats(std::make_unique<GpuStats>()) {
-    mGpuMem->initialize();
+      : mGpuMem(std::make_shared<GpuMem>()),
+        mGpuStats(std::make_unique<GpuStats>()),
+        mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
+    std::thread asyncInitThread([this]() {
+        mGpuMem->initialize();
+        mGpuMemTracer->initialize(mGpuMem);
+    });
+    asyncInitThread.detach();
 };
 
 void GpuService::setGpuStats(const std::string& driverPackageName,
@@ -65,6 +74,26 @@
     mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
 }
 
+void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+
+    // only system_server is allowed to set updatable driver path
+    if (uid != AID_SYSTEM) {
+        ALOGE("Permission Denial: can't set updatable driver path from pid=%d, uid=%d\n", pid, uid);
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mLock);
+    mDeveloperDriverPath = driverPath;
+}
+
+std::string GpuService::getUpdatableDriverPath() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mDeveloperDriverPath;
+}
+
 status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
     ATRACE_CALL();
 
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index f4b8597..409084b 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -30,6 +30,7 @@
 
 class GpuMem;
 class GpuStats;
+class GpuMemTracer;
 
 class GpuService : public BnGpuService, public PriorityDumper {
 public:
@@ -51,6 +52,8 @@
                      int64_t driverLoadingTime) override;
     void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
                         const GpuStatsInfo::Stats stats, const uint64_t value) override;
+    void setUpdatableDriverPath(const std::string& driverPath) override;
+    std::string getUpdatableDriverPath() override;
 
     /*
      * IBinder interface
@@ -73,8 +76,11 @@
     /*
      * Attributes
      */
-    std::unique_ptr<GpuMem> mGpuMem;
+    std::shared_ptr<GpuMem> mGpuMem;
     std::unique_ptr<GpuStats> mGpuStats;
+    std::unique_ptr<GpuMemTracer> mGpuMemTracer;
+    std::mutex mLock;
+    std::string mDeveloperDriverPath;
 };
 
 } // namespace android
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 1d4b524..3aa862f 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -24,6 +24,8 @@
 #include <libbpf.h>
 #include <libbpf_android.h>
 #include <log/log.h>
+#include <unistd.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <unordered_map>
@@ -41,26 +43,38 @@
     // Make sure bpf programs are loaded
     bpf::waitForProgsLoaded();
 
-    int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+    errno = 0;
+    int fd = bpf::retrieveProgram(kGpuMemTotalProgPath);
     if (fd < 0) {
-        ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+        ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kGpuMemTotalProgPath, errno,
+              strerror(errno));
         return;
     }
 
     // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
-    if (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
-        ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
-              kGpuMemTotalTracepoint);
-        return;
+    errno = 0;
+    int count = 0;
+    while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+        if (++count > kGpuWaitTimeout) {
+            ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kGpuMemTraceGroup,
+                  kGpuMemTotalTracepoint, errno, strerror(errno));
+            return;
+        }
+        // Retry until GPU driver loaded or timeout.
+        sleep(1);
     }
 
     // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+    errno = 0;
     auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
     if (!map.isValid()) {
-        ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+        ALOGE("Failed to create bpf map from %s [%d(%s)]", kGpuMemTotalMapPath, errno,
+              strerror(errno));
         return;
     }
     setGpuMemTotalMap(map);
+
+    mInitialized.store(true);
 }
 
 void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
@@ -71,7 +85,7 @@
 void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
     ATRACE_CALL();
 
-    if (!mGpuMemTotalMap.isValid()) {
+    if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
         result->append("Failed to initialize GPU memory eBPF\n");
         return;
     }
@@ -120,4 +134,24 @@
     }
 }
 
+void GpuMem::traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+                                                           uint64_t size)>& callback) {
+    auto res = mGpuMemTotalMap.getFirstKey();
+    if (!res.ok()) return;
+    uint64_t key = res.value();
+    while (true) {
+        uint32_t gpu_id = key >> 32;
+        uint32_t pid = key;
+
+        res = mGpuMemTotalMap.readValue(key);
+        if (!res.ok()) break;
+        uint64_t size = res.value();
+
+        callback(systemTime(), gpu_id, pid, size);
+        res = mGpuMemTotalMap.getNextKey(key);
+        if (!res.ok()) break;
+        key = res.value();
+    }
+}
+
 } // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index 6d0322a..de691e2 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -20,6 +20,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <functional>
+
 namespace android {
 
 class GpuMem {
@@ -31,6 +33,11 @@
     void initialize();
     // dumpsys interface
     void dump(const Vector<String16>& args, std::string* result);
+    bool isInitialized() { return mInitialized.load(); }
+
+    // Traverse the gpu memory total map to feed the callback function.
+    void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+                                                       uint64_t size)>& callback);
 
 private:
     // Friend class for testing.
@@ -39,6 +46,8 @@
     // set gpu memory total map
     void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
 
+    // indicate whether ebpf has been initialized
+    std::atomic<bool> mInitialized = false;
     // bpf map for GPU memory total data
     android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
 
@@ -51,6 +60,8 @@
             "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
     // pinned gpu memory total bpf map path in bpf sysfs
     static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+    // 30 seconds timeout for trying to attach bpf program to tracepoint
+    static constexpr int kGpuWaitTimeout = 30;
 };
 
 } // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 231d068..220952d 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -291,24 +291,27 @@
 
     if (data) {
         for (const auto& ele : mAppStats) {
-            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
-            AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
-            AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
-            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+            std::string glDriverBytes = int64VectorToProtoByteString(
+                ele.second.glDriverLoadingTime);
+            std::string vkDriverBytes = int64VectorToProtoByteString(
+                ele.second.vkDriverLoadingTime);
+            std::string angleDriverBytes = int64VectorToProtoByteString(
+                ele.second.angleDriverLoadingTime);
 
-            std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
-            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
-            bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
-            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
-            bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
-            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
-            AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
-            AStatsEvent_writeBool(event, ele.second.falsePrerotation);
-            AStatsEvent_writeBool(event, ele.second.gles1InUse);
-            AStatsEvent_build(event);
+            android::util::addAStatsEvent(
+                    data,
+                    android::util::GPU_STATS_APP_INFO,
+                    ele.second.appPackageName.c_str(),
+                    ele.second.driverVersionCode,
+                    android::util::BytesField(glDriverBytes.c_str(),
+                                              glDriverBytes.length()),
+                    android::util::BytesField(vkDriverBytes.c_str(),
+                                              vkDriverBytes.length()),
+                    android::util::BytesField(angleDriverBytes.c_str(),
+                                              angleDriverBytes.length()),
+                    ele.second.cpuVulkanInUse,
+                    ele.second.falsePrerotation,
+                    ele.second.gles1InUse);
         }
     }
 
@@ -326,22 +329,22 @@
 
     if (data) {
         for (const auto& ele : mGlobalStats) {
-            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
-            AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
-            AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
-            AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
-            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
-            AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
-            AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
-            AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
-            AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
-            AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
-            AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
-            AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
-            AStatsEvent_writeInt32(event, ele.second.glesVersion);
-            AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
-            AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
-            AStatsEvent_build(event);
+          android::util::addAStatsEvent(
+                  data,
+                  android::util::GPU_STATS_GLOBAL_INFO,
+                  ele.second.driverPackageName.c_str(),
+                  ele.second.driverVersionName.c_str(),
+                  ele.second.driverVersionCode,
+                  ele.second.driverBuildTime,
+                  ele.second.glLoadingCount,
+                  ele.second.glLoadingFailureCount,
+                  ele.second.vkLoadingCount,
+                  ele.second.vkLoadingFailureCount,
+                  ele.second.vulkanVersion,
+                  ele.second.cpuVulkanVersion,
+                  ele.second.glesVersion,
+                  ele.second.angleLoadingCount,
+                  ele.second.angleLoadingFailureCount);
         }
     }
 
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index db81f5d..940a26b 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -38,4 +38,5 @@
     static_libs: [
         "libgmock",
     ],
+    require_root: true,
 }
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
index 66f51c7..72976a8 100644
--- a/services/gpuservice/tests/unittests/AndroidTest.xml
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -18,6 +18,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index aad79ae..c5f8859 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -41,6 +41,8 @@
 constexpr uint64_t TEST_PROC_VAL_1 = 234;
 constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
 constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_KEY_MASK = 0x1 | 0x2 | 0x4;
+constexpr uint32_t TEST_KEY_COUNT = 3;
 
 class GpuMemTest : public testing::Test {
 public:
@@ -57,11 +59,17 @@
     }
 
     void SetUp() override {
+        SKIP_IF_BPF_NOT_SUPPORTED;
+        bpf::setrlimitForTest();
+
         mGpuMem = std::make_unique<GpuMem>();
         mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+        mTestableGpuMem.setInitialized();
+        errno = 0;
         mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
                                                    BPF_F_NO_PREALLOC);
 
+        EXPECT_EQ(0, errno);
         EXPECT_LE(0, mTestMap.getMap().get());
         EXPECT_TRUE(mTestMap.isValid());
     }
@@ -79,6 +87,8 @@
 };
 
 TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
+
     EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
     EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
     EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
@@ -87,16 +97,20 @@
 }
 
 TEST_F(GpuMemTest, bpfInitializationFailed) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
+
     EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
 }
 
 TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
     EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
 }
 
 TEST_F(GpuMemTest, globalMemTotal) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
@@ -104,6 +118,7 @@
 }
 
 TEST_F(GpuMemTest, missingGlobalMemTotal) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
@@ -111,6 +126,7 @@
 }
 
 TEST_F(GpuMemTest, procMemTotal) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
@@ -129,5 +145,37 @@
                                        TEST_PROC_VAL_2)));
 }
 
+TEST_F(GpuMemTest, traverseGpuMemTotals) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+    mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+    static uint32_t sMask = 0;
+    static uint32_t sCount = 0;
+    mGpuMem->traverseGpuMemTotals([](int64_t, uint32_t gpuId, uint32_t pid, uint64_t size) {
+        const uint64_t key = ((uint64_t)gpuId << 32) | pid;
+        switch (key) {
+            case TEST_GLOBAL_KEY:
+                EXPECT_EQ(size, TEST_GLOBAL_VAL);
+                sMask |= 0x1;
+                break;
+            case TEST_PROC_KEY_1:
+                EXPECT_EQ(size, TEST_PROC_VAL_1);
+                sMask |= 0x2;
+                break;
+            case TEST_PROC_KEY_2:
+                EXPECT_EQ(size, TEST_PROC_VAL_2);
+                sMask |= 0x4;
+                break;
+        }
+        sCount++;
+    });
+
+    EXPECT_EQ(sMask, TEST_KEY_MASK);
+    EXPECT_EQ(sCount, TEST_KEY_COUNT);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
index 0e4b01c..6c8becb 100644
--- a/services/gpuservice/tests/unittests/TestableGpuMem.h
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -26,6 +26,8 @@
     TestableGpuMem() = default;
     explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {}
 
+    void setInitialized() { mGpuMem->mInitialized.store(true); }
+
     void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
         mGpuMem->setGpuMemTotalMap(map);
     }
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
new file mode 100644
index 0000000..919fed3
--- /dev/null
+++ b/services/gpuservice/tracing/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libgpumemtracer",
+    srcs: [
+        "GpuMemTracer.cpp",
+    ],
+    shared_libs: [
+        "libgpumem",
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "libperfetto_client_experimental",
+    ],
+    export_include_dirs: ["include"],
+    export_static_lib_headers: [
+        "libperfetto_client_experimental",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
new file mode 100644
index 0000000..000cf27
--- /dev/null
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMemTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "tracing/GpuMemTracer.h"
+
+#include <gpumem/GpuMem.h>
+#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
+#include <unistd.h>
+
+#include <thread>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
+
+namespace android {
+
+std::mutex GpuMemTracer::sTraceMutex;
+std::condition_variable GpuMemTracer::sCondition;
+bool GpuMemTracer::sTraceStarted;
+
+void GpuMemTracer::initialize(std::shared_ptr<GpuMem> gpuMem) {
+    if (!gpuMem->isInitialized()) {
+        ALOGE("Cannot initialize GpuMemTracer before GpuMem");
+        return;
+    }
+    mGpuMem = gpuMem;
+    perfetto::TracingInitArgs args;
+    args.backends = perfetto::kSystemBackend;
+    perfetto::Tracing::Initialize(args);
+    registerDataSource();
+    std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+    pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
+    tracerThread.detach();
+}
+
+void GpuMemTracer::registerDataSource() {
+    perfetto::DataSourceDescriptor dsd;
+    dsd.set_name(kGpuMemDataSource);
+    GpuMemDataSource::Register(dsd);
+}
+
+void GpuMemTracer::threadLoop() {
+    while (true) {
+        {
+            std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+            while (!sTraceStarted) {
+                sCondition.wait(lock);
+            }
+        }
+        traceInitialCounters();
+        {
+            std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
+            sTraceStarted = false;
+        }
+    }
+}
+
+void GpuMemTracer::traceInitialCounters() {
+    if (!mGpuMem->isInitialized()) {
+        // This should never happen.
+        ALOGE("Cannot trace without GpuMem initialization");
+        return;
+    }
+    mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
+        GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
+            auto packet = ctx.NewTracePacket();
+            packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+            packet->set_timestamp(ts);
+            auto* event = packet->set_gpu_mem_total_event();
+            event->set_gpu_id(gpuId);
+            event->set_pid(pid);
+            event->set_size(size);
+        });
+    });
+    // Flush the TraceContext. The last packet in the above loop will go
+    // missing without this flush.
+    GpuMemDataSource::Trace([](GpuMemDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+} // namespace android
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
new file mode 100644
index 0000000..40deb4c
--- /dev/null
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <perfetto/tracing.h>
+
+#include <mutex>
+
+namespace android {
+
+class GpuMem;
+
+class GpuMemTracer {
+public:
+    class GpuMemDataSource : public perfetto::DataSource<GpuMemDataSource> {
+        virtual void OnSetup(const SetupArgs&) override{};
+        virtual void OnStart(const StartArgs&) override {
+            std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+            sTraceStarted = true;
+            sCondition.notify_all();
+        }
+        virtual void OnStop(const StopArgs&) override{};
+    };
+
+    ~GpuMemTracer() = default;
+
+    // Sets up the perfetto tracing backend and data source.
+    void initialize(std::shared_ptr<GpuMem>);
+    // Registers the data source with the perfetto backend. Called as part of initialize()
+    // and should not be called manually outside of tests. Public to allow for substituting a
+    // perfetto::kInProcessBackend in tests.
+    void registerDataSource();
+
+    static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
+    static std::condition_variable sCondition;
+    static std::mutex sTraceMutex;
+    static bool sTraceStarted;
+
+private:
+    void traceInitialCounters();
+    void threadLoop();
+
+    std::shared_ptr<GpuMem> mGpuMem;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f67c9d0..96e6207 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -21,7 +21,13 @@
         "-Werror",
         "-Wno-unused-parameter",
         "-Wthread-safety",
+        "-Wshadow",
+        "-Wshadow-field-in-constructor-modified",
+        "-Wshadow-uncaptured-local",
     ],
+    sanitize: {
+        misc_undefined: ["bounds"],
+    },
 }
 
 /////////////////////////////////////////////////
@@ -53,6 +59,9 @@
         "libutils",
         "libui",
     ],
+    static_libs: [
+        "libattestation",
+    ],
 }
 
 cc_library_shared {
@@ -99,6 +108,7 @@
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
+        "VibrationElement.cpp"
     ],
 }
 
@@ -110,6 +120,7 @@
         "libcutils",
         "libinput",
         "liblog",
+        "libui",
         "libutils",
     ],
     header_libs: [
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e68946d..3d99589 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,25 @@
 
 namespace android {
 
+static int32_t exceptionCodeFromStatusT(status_t status) {
+    switch (status) {
+        case OK:
+            return binder::Status::EX_NONE;
+        case INVALID_OPERATION:
+            return binder::Status::EX_UNSUPPORTED_OPERATION;
+        case BAD_VALUE:
+        case BAD_TYPE:
+        case NAME_NOT_FOUND:
+            return binder::Status::EX_ILLEGAL_ARGUMENT;
+        case NO_INIT:
+            return binder::Status::EX_ILLEGAL_STATE;
+        case PERMISSION_DENIED:
+            return binder::Status::EX_SECURITY;
+        default:
+            return binder::Status::EX_TRANSACTION_FAILED;
+    }
+}
+
 InputManager::InputManager(
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
@@ -93,16 +112,15 @@
 
 class BinderWindowHandle : public InputWindowHandle {
 public:
-    BinderWindowHandle(const InputWindowInfo& info) {
-        mInfo = info;
-    }
+    BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
 
     bool updateInfo() override {
         return true;
     }
 };
 
-void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos,
+binder::Status InputManager::setInputWindows(
+        const std::vector<InputWindowInfo>& infos,
         const sp<ISetInputWindowsListener>& setInputWindowsListener) {
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
 
@@ -116,26 +134,45 @@
     if (setInputWindowsListener) {
         setInputWindowsListener->onSetInputWindowsFinished();
     }
+    return binder::Status::ok();
 }
 
 // Used by tests only.
-void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int uid = ipc->getCallingUid();
     if (uid != AID_SHELL && uid != AID_ROOT) {
         ALOGE("Invalid attempt to register input channel over IPC"
                 "from non shell/root entity (PID: %d)", ipc->getCallingPid());
-        return;
+        return binder::Status::ok();
     }
-    mDispatcher->registerInputChannel(channel);
+
+    base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
+    if (!channel) {
+        return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
+                                                 channel.error().message().c_str());
+    }
+    (*channel)->copyTo(*outChannel);
+    return binder::Status::ok();
 }
 
-void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
-    mDispatcher->unregisterInputChannel(channel);
+binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+    mDispatcher->removeInputChannel(connectionToken);
+    return binder::Status::ok();
 }
 
-void InputManager::setMotionClassifierEnabled(bool enabled) {
-    mClassifier->setMotionClassifierEnabled(enabled);
+status_t InputManager::dump(int fd, const Vector<String16>& args) {
+    std::string dump;
+
+    dump += " InputFlinger dump\n";
+
+    ::write(fd, dump.c_str(), dump.size());
+    return NO_ERROR;
+}
+
+binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+    mDispatcher->setFocusedWindow(request);
+    return binder::Status::ok();
 }
 
 } // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 0158441..49bea13 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,15 +26,19 @@
 
 #include <InputDispatcherInterface.h>
 #include <InputDispatcherPolicyInterface.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/os/ISetInputWindowsListener.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 
-#include <input/IInputFlinger.h>
+#include <android/os/BnInputFlinger.h>
+#include <android/os/IInputFlinger.h>
 #include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
 
 namespace android {
 class InputChannel;
@@ -43,17 +47,19 @@
 /*
  * The input manager is the core of the system event processing.
  *
- * The input manager has two components.
+ * The input manager has three components.
  *
  * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
- *    policy, and posts messages to a queue managed by the InputDispatcherThread.
- * 2. The InputDispatcher class starts a thread that waits for new events on the
- *    queue and asynchronously dispatches them to applications.
+ *    policy, and posts messages to a queue managed by the InputClassifier.
+ * 2. The InputClassifier class starts a thread to communicate with the device-specific
+ *    classifiers. It then waits on the queue of events from InputReader, applies a classification
+ *    to them, and queues them for the InputDispatcher.
+ * 3. The InputDispatcher class starts a thread that waits for new events on the
+ *    previous queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReader class and InputDispatcher class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReader
- * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
- * InputDispatchPolicy, however.
+ * By design, none of these classes share any internal state.  Moreover, all communication is
+ * done one way from the InputReader to the InputDispatcher and never the reverse.  All
+ * classes may interact with the InputDispatchPolicy, however.
  *
  * The InputManager class never makes any calls into Java itself.  Instead, the
  * InputDispatchPolicy is responsible for performing all external interactions with the
@@ -74,33 +80,37 @@
     /* Gets the input reader. */
     virtual sp<InputReaderInterface> getReader() = 0;
 
+    /* Gets the input classifier */
+    virtual sp<InputClassifierInterface> getClassifier() = 0;
+
     /* Gets the input dispatcher. */
     virtual sp<InputDispatcherInterface> getDispatcher() = 0;
 };
 
 class InputManager : public InputManagerInterface, public BnInputFlinger {
 protected:
-    virtual ~InputManager();
+    ~InputManager() override;
 
 public:
     InputManager(
             const sp<InputReaderPolicyInterface>& readerPolicy,
             const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
 
-    virtual status_t start();
-    virtual status_t stop();
+    status_t start() override;
+    status_t stop() override;
 
-    virtual sp<InputReaderInterface> getReader();
-    virtual sp<InputClassifierInterface> getClassifier();
-    virtual sp<InputDispatcherInterface> getDispatcher();
+    sp<InputReaderInterface> getReader() override;
+    sp<InputClassifierInterface> getClassifier() override;
+    sp<InputDispatcherInterface> getDispatcher() override;
 
-    virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener);
+    status_t dump(int fd, const Vector<String16>& args) override;
+    binder::Status setInputWindows(
+            const std::vector<InputWindowInfo>& handles,
+            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
 
-    virtual void registerInputChannel(const sp<InputChannel>& channel);
-    virtual void unregisterInputChannel(const sp<InputChannel>& channel);
-
-    void setMotionClassifierEnabled(bool enabled);
+    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status setFocusedWindow(const FocusRequest&) override;
 
 private:
     sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index b2dadf8..9cc777d 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,6 +19,9 @@
 //#define LOG_NDEBUG 0
 
 #include "InputReaderBase.h"
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
+#include "input/NamedEnum.h"
 
 #include <android/log.h>
 #include <android-base/stringprintf.h>
@@ -99,17 +102,19 @@
     size_t count = 0;
     std::optional<DisplayViewport> result = std::nullopt;
     for (const DisplayViewport& currentViewport : mDisplays) {
-        // Return the first match
+        // Return the first match, or the default display if we're looking for the internal viewport
         if (currentViewport.type == type) {
-            if (!result) {
+            if (!result ||
+                (type == ViewportType::INTERNAL &&
+                 currentViewport.displayId == ADISPLAY_ID_DEFAULT)) {
                 result = std::make_optional(currentViewport);
             }
             count++;
         }
     }
     if (count > 1) {
-        ALOGE("Found %zu viewports with type %s, but expected 1 at most",
-                count, viewportTypeToString(type));
+        ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
+              NamedEnum::string(type).c_str());
     }
     return result;
 }
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
new file mode 100644
index 0000000..8073a93
--- /dev/null
+++ b/services/inputflinger/TEST_MAPPING
@@ -0,0 +1,43 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsWindowManagerDeviceTestCases",
+      "options": [
+        {
+          "include-filter": "android.server.wm.WindowInputTests"
+        }
+      ]
+    },
+    {
+      "name": "libinput_tests"
+    },
+    {
+      "name": "inputflinger_tests"
+    },
+    {
+      "name": "InputTests"
+    },
+    {
+      "name": "libinputservice_test"
+    },
+    {
+      "name": "CtsInputTestCases"
+    },
+    {
+      "name": "CtsViewTestCases",
+      "options": [
+        {
+          "include-filter": "android.view.cts.MotionEventTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsSecurityTestCases",
+      "options": [
+        {
+          "include-filter": "android.security.cts.MotionEventTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
new file mode 100644
index 0000000..aaf5834
--- /dev/null
+++ b/services/inputflinger/VibrationElement.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VibrationElement.h"
+
+#include <android-base/stringprintf.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+const std::string VibrationElement::toString() const {
+    std::string dump;
+    dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
+
+    for (auto it = channels.begin(); it != channels.end(); ++it) {
+        dump += std::to_string(*it);
+        if (std::next(it) != channels.end()) {
+            dump += ", ";
+        }
+    }
+
+    dump += "]]";
+    return dump;
+}
+
+uint16_t VibrationElement::getMagnitude(size_t channelIdx) const {
+    if (channelIdx >= channels.size()) {
+        return 0;
+    }
+    // convert range [0,255] to [0,65535] (android framework to linux ff ranges)
+    return static_cast<uint16_t>(channels[channelIdx]) << 8;
+}
+
+bool VibrationElement::isOn() const {
+    return std::any_of(channels.begin(), channels.end(),
+                       [](uint16_t channel) { return channel != 0; });
+}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 066a816..9abf8b1 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -18,6 +18,7 @@
         "libutils",
     ],
     static_libs: [
+        "libattestation",
         "libinputdispatcher",
     ],
 }
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5a14133..c2d165e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -19,6 +19,9 @@
 #include <binder/Binder.h>
 #include "../dispatcher/InputDispatcher.h"
 
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+
 namespace android::inputdispatcher {
 
 // An arbitrary device id.
@@ -45,49 +48,47 @@
     virtual ~FakeInputDispatcherPolicy() {}
 
 private:
-    virtual void notifyConfigurationChanged(nsecs_t) override {}
+    void notifyConfigurationChanged(nsecs_t) override {}
 
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
-                              const std::string& name) override {
+    std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>&,
+                                       const sp<IBinder>&, const std::string& name) override {
         ALOGE("The window is not responding : %s", name.c_str());
-        return 0;
+        return 0s;
     }
 
-    virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+    void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
-    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+    void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
-    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+    void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+
+    void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+    bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
         return true;
     }
 
-    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+    void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
 
-    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+    void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
 
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
-                                                  uint32_t) override {
+    nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
         return 0;
     }
 
-    virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
-                                      KeyEvent*) override {
+    bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
         return false;
     }
 
-    virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+    void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
 
-    virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+    void pokeUserActivity(nsecs_t, int32_t) override {}
 
-    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
-        return false;
-    }
+    bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
 
-    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+    void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
 
     InputDispatcherConfiguration mConfig;
 };
@@ -98,7 +99,8 @@
     virtual ~FakeApplicationHandle() {}
 
     virtual bool updateInfo() {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.dispatchingTimeoutMillis =
+                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
         return true;
     }
 };
@@ -106,7 +108,7 @@
 class FakeInputReceiver {
 public:
     void consumeEvent() {
-        uint32_t consumeSeq;
+        uint32_t consumeSeq = 0;
         InputEvent* event;
 
         std::chrono::time_point start = std::chrono::steady_clock::now();
@@ -132,14 +134,14 @@
 protected:
     explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
           : mDispatcher(dispatcher) {
-        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+        mClientChannel = *mDispatcher->createInputChannel(name);
         mConsumer = std::make_unique<InputConsumer>(mClientChannel);
     }
 
     virtual ~FakeInputReceiver() {}
 
     sp<InputDispatcher> mDispatcher;
-    sp<InputChannel> mServerChannel, mClientChannel;
+    std::shared_ptr<InputChannel> mClientChannel;
     std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
 };
@@ -149,21 +151,18 @@
     static const int32_t WIDTH = 200;
     static const int32_t HEIGHT = 200;
 
-    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+    FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
                      const sp<InputDispatcher>& dispatcher, const std::string name)
           : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
-        mDispatcher->registerInputChannel(mServerChannel);
-
         inputApplicationHandle->updateInfo();
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
 
     virtual bool updateInfo() override {
-        mInfo.token = mServerChannel->getConnectionToken();
+        mInfo.token = mClientChannel->getConnectionToken();
         mInfo.name = "FakeWindowHandle";
-        mInfo.layoutParamsFlags = 0;
-        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.type = InputWindowInfo::Type::APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.frameLeft = mFrame.left;
         mInfo.frameTop = mFrame.top;
         mInfo.frameRight = mFrame.right;
@@ -172,13 +171,11 @@
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(mFrame);
         mInfo.visible = true;
-        mInfo.canReceiveKeys = true;
-        mInfo.hasFocus = true;
+        mInfo.focusable = true;
         mInfo.hasWallpaper = false;
         mInfo.paused = false;
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
-        mInfo.inputFeatures = 0;
         mInfo.displayId = ADISPLAY_ID_DEFAULT;
 
         return true;
@@ -202,13 +199,13 @@
 
     const nsecs_t currentTime = now();
 
+    ui::Transform identityTransform;
     MotionEvent event;
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
                      ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
                      /* actionButton */ 0, /* flags */ 0,
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
-                     1 /* xScale */, 1 /* yScale */,
-                     /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+                     identityTransform, /* xPrecision */ 0,
                      /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -249,7 +246,7 @@
     dispatcher->start();
 
     // Create a window that will receive motion events
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
 
     dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -285,7 +282,7 @@
     dispatcher->start();
 
     // Create a window that will receive motion events
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
 
     dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -294,13 +291,13 @@
         MotionEvent event = generateMotionEvent();
         // Send ACTION_DOWN
         dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
                                      POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 
         // Send ACTION_UP
         event.setAction(AMOTION_EVENT_ACTION_UP);
         dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
                                      POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 
         window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index a98f4b4..ff9aac9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -22,6 +22,7 @@
 filegroup {
     name: "libinputdispatcher_sources",
     srcs: [
+        "AnrTracker.cpp",
         "Connection.cpp",
         "Entry.cpp",
         "InjectionState.cpp",
@@ -47,6 +48,9 @@
         "libui",
         "libutils",
     ],
+    static_libs: [
+        "libattestation",
+    ],
     header_libs: [
         "libinputdispatcher_headers",
     ],
@@ -67,4 +71,5 @@
     export_header_lib_headers: [
         "libinputdispatcher_headers",
     ],
+    logtags: ["EventLogTags.logtags"],
 }
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
new file mode 100644
index 0000000..c3f611e
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnrTracker.h"
+
+namespace android::inputdispatcher {
+
+template <typename T>
+static T max(const T& a, const T& b) {
+    return a < b ? b : a;
+}
+
+void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {
+    mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
+}
+
+/**
+ * Erase a single entry only. If there are multiple duplicate entries
+ * (same time, same connection), then only remove one of them.
+ */
+void AnrTracker::erase(nsecs_t timeoutTime, const sp<IBinder>& token) {
+    auto pair = std::make_pair(timeoutTime, token);
+    auto it = mAnrTimeouts.find(pair);
+    if (it != mAnrTimeouts.end()) {
+        mAnrTimeouts.erase(it);
+    }
+}
+
+void AnrTracker::eraseToken(const sp<IBinder>& token) {
+    for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) {
+        if (it->second == token) {
+            it = mAnrTimeouts.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+bool AnrTracker::empty() const {
+    return mAnrTimeouts.empty();
+}
+
+// If empty() is false, return the time at which the next connection should cause an ANR
+// If empty() is true, return LONG_LONG_MAX
+nsecs_t AnrTracker::firstTimeout() const {
+    if (mAnrTimeouts.empty()) {
+        return std::numeric_limits<nsecs_t>::max();
+    }
+    return mAnrTimeouts.begin()->first;
+}
+
+const sp<IBinder>& AnrTracker::firstToken() const {
+    return mAnrTimeouts.begin()->second;
+}
+
+void AnrTracker::clear() {
+    mAnrTimeouts.clear();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
new file mode 100644
index 0000000..097dba5
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+
+#include <binder/IBinder.h>
+#include <utils/Timers.h>
+#include <set>
+
+namespace android::inputdispatcher {
+
+/**
+ * Keeps track of the times when each connection is going to ANR.
+ * Provides the ability to quickly find the connection that is going to cause ANR next.
+ */
+class AnrTracker {
+public:
+    void insert(nsecs_t timeoutTime, sp<IBinder> token);
+    void erase(nsecs_t timeoutTime, const sp<IBinder>& token);
+    void eraseToken(const sp<IBinder>& token);
+    void clear();
+
+    bool empty() const;
+    // If empty() is false, return the time at which the next connection should cause an ANR
+    // If empty() is true, return LONG_LONG_MAX
+    nsecs_t firstTimeout() const;
+    // Return the token of the next connection that should cause an ANR.
+    // Do not call this unless empty() is false, you will encounter undefined behaviour.
+    const sp<IBinder>& firstToken() const;
+
+private:
+    // Optimization: use a multiset to keep track of the event timeouts. When an event is sent
+    // to the InputConsumer, we add an entry to this structure. We look at the smallest value to
+    // determine if any of the connections is unresponsive, and to determine when we should wake
+    // next for the future ANR check.
+    // Using a multiset helps quickly look up the next timeout due.
+    //
+    // We must use a multi-set, because it is plausible (although highly unlikely) to have entries
+    // from the same connection and same timestamp, but different sequence numbers.
+    // We are not tracking sequence numbers, and just allow duplicates to exist.
+    std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index 188212b..cee9c39 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -20,14 +20,13 @@
 
 namespace android::inputdispatcher {
 
-Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
+Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
                        const IdGenerator& idGenerator)
       : status(STATUS_NORMAL),
         inputChannel(inputChannel),
         monitor(monitor),
         inputPublisher(inputChannel),
-        inputState(idGenerator),
-        inputPublisherBlocked(false) {}
+        inputState(idGenerator) {}
 
 Connection::~Connection() {}
 
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index bb3f2fe..c4262ad 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -42,14 +42,15 @@
     };
 
     Status status;
-    sp<InputChannel> inputChannel; // never null
+    std::shared_ptr<InputChannel> inputChannel; // never null
     bool monitor;
     InputPublisher inputPublisher;
     InputState inputState;
 
-    // True if the socket is full and no further events can be published until
-    // the application consumes some of the input.
-    bool inputPublisherBlocked;
+    // True if this connection is responsive.
+    // If this connection is not responsive, avoid publishing more events to it until the
+    // application consumes some of the input.
+    bool responsive = true;
 
     // Queue of events that need to be published to the connection.
     std::deque<DispatchEntry*> outboundQueue;
@@ -58,7 +59,8 @@
     // yet received a "finished" response from the application.
     std::deque<DispatchEntry*> waitQueue;
 
-    Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator);
+    Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
+               const IdGenerator& idGenerator);
 
     inline const std::string getInputChannelName() const { return inputChannel->getName(); }
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 21c8ae1..29df00b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,38 +28,6 @@
 
 namespace android::inputdispatcher {
 
-static std::string motionActionToString(int32_t action) {
-    // Convert MotionEvent action to string
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AMOTION_EVENT_ACTION_MOVE:
-            return "MOVE";
-        case AMOTION_EVENT_ACTION_UP:
-            return "UP";
-        case AMOTION_EVENT_ACTION_CANCEL:
-            return "CANCEL";
-        case AMOTION_EVENT_ACTION_POINTER_DOWN:
-            return "POINTER_DOWN";
-        case AMOTION_EVENT_ACTION_POINTER_UP:
-            return "POINTER_UP";
-    }
-    return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
-    // Convert KeyEvent action to string
-    switch (action) {
-        case AKEY_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AKEY_EVENT_ACTION_UP:
-            return "UP";
-        case AKEY_EVENT_ACTION_MULTIPLE:
-            return "MULTIPLE";
-    }
-    return StringPrintf("%" PRId32, action);
-}
-
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
     return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
              entry.displayId},
@@ -91,7 +59,6 @@
 
 EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags)
       : id(id),
-        refCount(1),
         type(type),
         eventTime(eventTime),
         policyFlags(policyFlags),
@@ -102,21 +69,6 @@
     releaseInjectionState();
 }
 
-std::string EventEntry::getDescription() const {
-    std::string result;
-    appendDescription(result);
-    return result;
-}
-
-void EventEntry::release() {
-    refCount -= 1;
-    if (refCount == 0) {
-        delete this;
-    } else {
-        ALOG_ASSERT(refCount > 0);
-    }
-}
-
 void EventEntry::releaseInjectionState() {
     if (injectionState) {
         injectionState->release();
@@ -131,8 +83,8 @@
 
 ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
 
-void ConfigurationChangedEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+std::string ConfigurationChangedEntry::getDescription() const {
+    return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
 }
 
 // --- DeviceResetEntry ---
@@ -142,22 +94,24 @@
 
 DeviceResetEntry::~DeviceResetEntry() {}
 
-void DeviceResetEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
+std::string DeviceResetEntry::getDescription() const {
+    return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
 }
 
 // --- FocusEntry ---
 
 // Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
-FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus)
+FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+                       std::string_view reason)
       : EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
         connectionToken(connectionToken),
-        hasFocus(hasFocus) {}
+        hasFocus(hasFocus),
+        reason(reason) {}
 
 FocusEntry::~FocusEntry() {}
 
-void FocusEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+std::string FocusEntry::getDescription() const {
+    return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
 }
 
 // --- KeyEntry ---
@@ -183,16 +137,16 @@
 
 KeyEntry::~KeyEntry() {}
 
-void KeyEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("KeyEvent");
+std::string KeyEntry::getDescription() const {
     if (!GetBoolProperty("ro.debuggable", false)) {
-        return;
+        return "KeyEvent";
     }
-    msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+    return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
+                        ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
                         "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
                         "repeatCount=%d), policyFlags=0x%08x",
-                        deviceId, source, displayId, keyActionToString(action).c_str(), flags,
-                        keyCode, scanCode, metaState, repeatCount, policyFlags);
+                        deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
+                        flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
 }
 
 void KeyEntry::recycle() {
@@ -215,7 +169,6 @@
                          uint32_t pointerCount, const PointerProperties* pointerProperties,
                          const PointerCoords* pointerCoords, float xOffset, float yOffset)
       : EventEntry(id, Type::MOTION, eventTime, policyFlags),
-        eventTime(eventTime),
         deviceId(deviceId),
         source(source),
         displayId(displayId),
@@ -243,20 +196,21 @@
 
 MotionEntry::~MotionEntry() {}
 
-void MotionEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("MotionEvent");
+std::string MotionEntry::getDescription() const {
     if (!GetBoolProperty("ro.debuggable", false)) {
-        return;
+        return "MotionEvent";
     }
-    msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32
+    std::string msg;
+    msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
+                        ", source=0x%08x, displayId=%" PRId32
                         ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
                         "buttonState=0x%08x, "
                         "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
                         "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
-                        deviceId, source, displayId, motionActionToString(action).c_str(),
-                        actionButton, flags, metaState, buttonState,
-                        motionClassificationToString(classification), edgeFlags, xPrecision,
-                        yPrecision, xCursorPosition, yCursorPosition);
+                        deviceId, eventTime, source, displayId,
+                        MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
+                        buttonState, motionClassificationToString(classification), edgeFlags,
+                        xPrecision, yPrecision, xCursorPosition, yCursorPosition);
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         if (i) {
@@ -266,32 +220,23 @@
                             pointerCoords[i].getY());
     }
     msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
+    return msg;
 }
 
 // --- DispatchEntry ---
 
 volatile int32_t DispatchEntry::sNextSeqAtomic;
 
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
-                             float yOffset, float globalScaleFactor, float windowXScale,
-                             float windowYScale)
+DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+                             ui::Transform transform, float globalScaleFactor)
       : seq(nextSeq()),
-        eventEntry(eventEntry),
+        eventEntry(std::move(eventEntry)),
         targetFlags(targetFlags),
-        xOffset(xOffset),
-        yOffset(yOffset),
+        transform(transform),
         globalScaleFactor(globalScaleFactor),
-        windowXScale(windowXScale),
-        windowYScale(windowYScale),
         deliveryTime(0),
         resolvedAction(0),
-        resolvedFlags(0) {
-    eventEntry->refCount += 1;
-}
-
-DispatchEntry::~DispatchEntry() {
-    eventEntry->release();
-}
+        resolvedFlags(0) {}
 
 uint32_t DispatchEntry::nextSeq() {
     // Sequence number 0 is reserved and will never be returned.
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index a135409..0661709 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -54,7 +54,6 @@
     }
 
     int32_t id;
-    mutable int32_t refCount;
     Type type;
     nsecs_t eventTime;
     uint32_t policyFlags;
@@ -79,23 +78,19 @@
         return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER;
     }
 
-    void release();
+    virtual std::string getDescription() const = 0;
 
-    virtual void appendDescription(std::string& msg) const = 0;
-
-    std::string getDescription() const;
-
-protected:
     EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
     virtual ~EventEntry();
+
+protected:
     void releaseInjectionState();
 };
 
 struct ConfigurationChangedEntry : EventEntry {
     explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
-    virtual void appendDescription(std::string& msg) const;
+    std::string getDescription() const override;
 
-protected:
     virtual ~ConfigurationChangedEntry();
 };
 
@@ -103,20 +98,20 @@
     int32_t deviceId;
 
     DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
-    virtual void appendDescription(std::string& msg) const;
+    std::string getDescription() const override;
 
-protected:
     virtual ~DeviceResetEntry();
 };
 
 struct FocusEntry : EventEntry {
     sp<IBinder> connectionToken;
     bool hasFocus;
+    std::string_view reason;
 
-    FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
-    virtual void appendDescription(std::string& msg) const;
+    FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+               std::string_view reason);
+    std::string getDescription() const override;
 
-protected:
     virtual ~FocusEntry();
 };
 
@@ -146,15 +141,13 @@
     KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
              uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
              int32_t metaState, int32_t repeatCount, nsecs_t downTime);
-    virtual void appendDescription(std::string& msg) const;
+    std::string getDescription() const override;
     void recycle();
 
-protected:
     virtual ~KeyEntry();
 };
 
 struct MotionEntry : EventEntry {
-    nsecs_t eventTime;
     int32_t deviceId;
     uint32_t source;
     int32_t displayId;
@@ -181,9 +174,8 @@
                 float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                 float xOffset, float yOffset);
-    virtual void appendDescription(std::string& msg) const;
+    std::string getDescription() const override;
 
-protected:
     virtual ~MotionEntry();
 };
 
@@ -191,23 +183,23 @@
 struct DispatchEntry {
     const uint32_t seq; // unique sequence number, never 0
 
-    EventEntry* eventEntry; // the event to dispatch
+    std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
     int32_t targetFlags;
-    float xOffset;
-    float yOffset;
+    ui::Transform transform;
     float globalScaleFactor;
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
+    // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
+    // and will be undefined before that.
     nsecs_t deliveryTime; // time when the event was actually delivered
+    // An ANR will be triggered if a response for this entry is not received by timeoutTime
+    nsecs_t timeoutTime;
 
     // Set to the resolved ID, action and flags when the event is enqueued.
     int32_t resolvedEventId;
     int32_t resolvedAction;
     int32_t resolvedFlags;
 
-    DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
-                  float globalScaleFactor, float windowXScale, float windowYScale);
-    ~DispatchEntry();
+    DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+                  ui::Transform transform, float globalScaleFactor);
 
     inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
 
@@ -252,15 +244,16 @@
     // parameters for the command (usage varies by command)
     sp<Connection> connection;
     nsecs_t eventTime;
-    KeyEntry* keyEntry;
-    sp<InputApplicationHandle> inputApplicationHandle;
+    std::shared_ptr<KeyEntry> keyEntry;
+    std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
     std::string reason;
     int32_t userActivityEventType;
     uint32_t seq;
     bool handled;
-    sp<InputChannel> inputChannel;
+    sp<IBinder> connectionToken;
     sp<IBinder> oldToken;
     sp<IBinder> newToken;
+    std::string obscuringPackage;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
new file mode 100644
index 0000000..2836467
--- /dev/null
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -0,0 +1,42 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# See system/core/logcat/event.logtags for the master copy of the tags.
+
+# 62000 - 62199 reserved for inputflinger
+
+62000 input_interaction (windows|4)
+62001 input_focus (window|3),(reason|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index b2d0a26..c8024a6 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -24,7 +24,7 @@
       : refCount(1),
         injectorPid(injectorPid),
         injectorUid(injectorUid),
-        injectionResult(INPUT_EVENT_INJECTION_PENDING),
+        injectionResult(android::os::InputEventInjectionResult::PENDING),
         injectionIsAsync(false),
         pendingForegroundDispatches(0) {}
 
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 311a0f1..0bfafb1 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -17,34 +17,19 @@
 #ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
 #define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
 
+#include <stdint.h>
 #include "InputDispatcherInterface.h"
 
-#include <stdint.h>
+namespace android {
 
-namespace android::inputdispatcher {
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
-    /* Injection is asynchronous and is assumed always to be successful. */
-    INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
-    /* Waits for previous events to be dispatched so that the input dispatcher can determine
-     * whether input event injection willbe permitted based on the current input focus.
-     * Does not wait for the input event to finish processing. */
-    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
-    /* Waits for the input event to be completely processed. */
-    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
+namespace inputdispatcher {
 
 struct InjectionState {
     mutable int32_t refCount;
 
     int32_t injectorPid;
     int32_t injectorUid;
-    int32_t injectionResult;             // initially INPUT_EVENT_INJECTION_PENDING
+    android::os::InputEventInjectionResult injectionResult; // initially PENDING
     bool injectionIsAsync;               // set to true if injection is not waiting for the result
     int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
@@ -55,6 +40,7 @@
     ~InjectionState();
 };
 
-} // namespace android::inputdispatcher
+} // namespace inputdispatcher
+} // namespace android
 
 #endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 056a725..e5d208a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -28,8 +28,8 @@
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 0
 
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
+// Log debug messages about channel creation
+#define DEBUG_CHANNEL_CREATION 0
 
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
@@ -37,6 +37,10 @@
 // Log debug messages about input focus tracking.
 static constexpr bool DEBUG_FOCUS = false;
 
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
 // Log debug messages about the app switch latency optimization.
 #define DEBUG_APP_SWITCH 0
 
@@ -45,27 +49,28 @@
 
 #include "InputDispatcher.h"
 
-#include "Connection.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
+#include <binder/Binder.h>
+#include <input/InputDevice.h>
+#include <input/InputWindow.h>
+#include <log/log.h>
+#include <log/log_event_list.h>
+#include <powermanager/PowerManager.h>
 #include <statslog.h>
-#include <stddef.h>
-#include <time.h>
 #include <unistd.h>
+#include <utils/Trace.h>
+
+#include <cerrno>
+#include <cinttypes>
+#include <climits>
+#include <cstddef>
+#include <ctime>
 #include <queue>
 #include <sstream>
 
-#include <android-base/chrono_utils.h>
-#include <android-base/stringprintf.h>
-#include <binder/Binder.h>
-#include <input/InputDevice.h>
-#include <log/log.h>
-#include <openssl/hmac.h>
-#include <openssl/rand.h>
-#include <powermanager/PowerManager.h>
-#include <utils/Trace.h>
+#include "Connection.h"
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -73,12 +78,16 @@
 #define INDENT4 "        "
 
 using android::base::StringPrintf;
+using android::os::BlockUntrustedTouchesMode;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
 
 namespace android::inputdispatcher {
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
-constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
+constexpr std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT =
+        std::chrono::milliseconds(android::os::IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
 
 // Amount of time to allow for all pending events to be processed when an app switch
 // key is on the way.  This is used to preempt input dispatch and drop input events
@@ -89,21 +98,24 @@
 // before considering it stale and dropping it.
 constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
 
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished.  This value extends the ANR timeout by the specified
-// amount.  For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
 // Log a warning when an event takes longer than this to process, even if an ANR does not occur.
 constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
 
 // Log a warning when an interception call takes longer than this to process.
 constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms;
 
+// Additional key latency in case a connection is still processing some motion events.
+// This will help with the case when a user touched a button that opens a new window,
+// and gives us the chance to dispatch the key to this new window.
+constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms;
+
 // Number of recent events to keep for debugging purposes.
 constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
 
+// Event log tags. See EventLogTags.logtags for reference
+constexpr int LOGTAG_INPUT_INTERACTION = 62000;
+constexpr int LOGTAG_INPUT_FOCUS = 62001;
+
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
@@ -160,6 +172,10 @@
     }
 }
 
+static int64_t millis(std::chrono::nanoseconds t) {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
+}
+
 static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
                                 const PointerProperties* pointerProperties) {
     if (!isValidMotionAction(action, actionButton, pointerCount)) {
@@ -188,12 +204,12 @@
     return true;
 }
 
-static void dumpRegion(std::string& dump, const Region& region) {
+static std::string dumpRegion(const Region& region) {
     if (region.isEmpty()) {
-        dump += "<empty>";
-        return;
+        return "<empty>";
     }
 
+    std::string dump;
     bool first = true;
     Region::const_iterator cur = region.begin();
     Region::const_iterator const tail = region.end();
@@ -206,6 +222,37 @@
         dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom);
         cur++;
     }
+    return dump;
+}
+
+static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+    constexpr size_t maxEntries = 50; // max events to print
+    constexpr size_t skipBegin = maxEntries / 2;
+    const size_t skipEnd = queue.size() - maxEntries / 2;
+    // skip from maxEntries / 2 ... size() - maxEntries/2
+    // only print from 0 .. skipBegin and then from skipEnd .. size()
+
+    std::string dump;
+    for (size_t i = 0; i < queue.size(); i++) {
+        const DispatchEntry& entry = *queue[i];
+        if (i >= skipBegin && i < skipEnd) {
+            dump += StringPrintf(INDENT4 "<skipped %zu entries>\n", skipEnd - skipBegin);
+            i = skipEnd - 1; // it will be incremented to "skipEnd" by 'continue'
+            continue;
+        }
+        dump.append(INDENT4);
+        dump += entry.eventEntry->getDescription();
+        dump += StringPrintf(", seq=%" PRIu32
+                             ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms",
+                             entry.seq, entry.targetFlags, entry.resolvedAction,
+                             ns2ms(currentTime - entry.eventEntry->eventTime));
+        if (entry.deliveryTime != 0) {
+            // This entry was delivered, so add information on how long we've been waiting
+            dump += StringPrintf(", wait=%" PRId64 "ms", ns2ms(currentTime - entry.deliveryTime));
+        }
+        dump.append("\n");
+    }
+    return dump;
 }
 
 /**
@@ -243,6 +290,15 @@
     return removed;
 }
 
+/**
+ * Find the entry in std::unordered_map by key and return the value as an optional.
+ */
+template <typename K, typename V>
+static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) {
+    auto it = map.find(key);
+    return it != map.end() ? std::optional<V>{it->second} : std::nullopt;
+}
+
 static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
     if (first == second) {
         return true;
@@ -260,58 +316,54 @@
 }
 
 static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
-                                                          EventEntry* eventEntry,
+                                                          std::shared_ptr<EventEntry> eventEntry,
                                                           int32_t inputTargetFlags) {
-    if (inputTarget.useDefaultPointerInfo()) {
-        const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
-        return std::make_unique<DispatchEntry>(eventEntry, // increments ref
-                                               inputTargetFlags, pointerInfo.xOffset,
-                                               pointerInfo.yOffset, inputTarget.globalScaleFactor,
-                                               pointerInfo.windowXScale, pointerInfo.windowYScale);
+    if (inputTarget.useDefaultPointerTransform()) {
+        const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
+        return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
+                                               inputTarget.globalScaleFactor);
     }
 
     ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
     const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
 
-    PointerCoords pointerCoords[motionEntry.pointerCount];
+    std::vector<PointerCoords> pointerCoords;
+    pointerCoords.resize(motionEntry.pointerCount);
 
     // Use the first pointer information to normalize all other pointers. This could be any pointer
     // as long as all other pointers are normalized to the same value and the final DispatchEntry
-    // uses the offset and scale for the normalized pointer.
-    const PointerInfo& firstPointerInfo =
-            inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+    // uses the transform for the normalized pointer.
+    const ui::Transform& firstPointerTransform =
+            inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()];
+    ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
 
     // Iterate through all pointers in the event to normalize against the first.
     for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
         const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
-        const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
-
-        // The scale factor is the ratio of the current pointers scale to the normalized scale.
-        float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
-        float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+        const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
 
         pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
-        // First apply the current pointers offset to set the window at 0,0
-        pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
-        // Next scale the coordinates.
-        pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
-        // Lastly, offset the coordinates so they're in the normalized pointer's frame.
-        pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
-                                                -firstPointerInfo.yOffset);
+        // First, apply the current pointer's transform to update the coordinates into
+        // window space.
+        pointerCoords[pointerIndex].transform(currTransform);
+        // Next, apply the inverse transform of the normalized coordinates so the
+        // current coordinates are transformed into the normalized coordinate space.
+        pointerCoords[pointerIndex].transform(inverseFirstTransform);
     }
 
-    MotionEntry* combinedMotionEntry =
-            new MotionEntry(motionEntry.id, motionEntry.eventTime, motionEntry.deviceId,
-                            motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
-                            motionEntry.action, motionEntry.actionButton, motionEntry.flags,
-                            motionEntry.metaState, motionEntry.buttonState,
-                            motionEntry.classification, motionEntry.edgeFlags,
-                            motionEntry.xPrecision, motionEntry.yPrecision,
-                            motionEntry.xCursorPosition, motionEntry.yCursorPosition,
-                            motionEntry.downTime, motionEntry.pointerCount,
-                            motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
-                            0 /* yOffset */);
+    std::unique_ptr<MotionEntry> combinedMotionEntry =
+            std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime,
+                                          motionEntry.deviceId, motionEntry.source,
+                                          motionEntry.displayId, motionEntry.policyFlags,
+                                          motionEntry.action, motionEntry.actionButton,
+                                          motionEntry.flags, motionEntry.metaState,
+                                          motionEntry.buttonState, motionEntry.classification,
+                                          motionEntry.edgeFlags, motionEntry.xPrecision,
+                                          motionEntry.yPrecision, motionEntry.xCursorPosition,
+                                          motionEntry.yCursorPosition, motionEntry.downTime,
+                                          motionEntry.pointerCount, motionEntry.pointerProperties,
+                                          pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */);
 
     if (motionEntry.injectionState) {
         combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -319,12 +371,8 @@
     }
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
-            std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
-                                            inputTargetFlags, firstPointerInfo.xOffset,
-                                            firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
-                                            firstPointerInfo.windowXScale,
-                                            firstPointerInfo.windowYScale);
-    combinedMotionEntry->release();
+            std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
+                                            firstPointerTransform, inputTarget.globalScaleFactor);
     return dispatchEntry;
 }
 
@@ -340,51 +388,38 @@
     }
 }
 
-static std::array<uint8_t, 128> getRandomKey() {
-    std::array<uint8_t, 128> key;
-    if (RAND_bytes(key.data(), key.size()) != 1) {
-        LOG_ALWAYS_FATAL("Can't generate HMAC key");
-    }
-    return key;
+static status_t openInputChannelPair(const std::string& name,
+                                     std::shared_ptr<InputChannel>& serverChannel,
+                                     std::unique_ptr<InputChannel>& clientChannel) {
+    std::unique_ptr<InputChannel> uniqueServerChannel;
+    status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
+
+    serverChannel = std::move(uniqueServerChannel);
+    return result;
 }
 
-// --- HmacKeyManager ---
-
-HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
-
-std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
-    size_t size;
-    switch (event.type) {
-        case VerifiedInputEvent::Type::KEY: {
-            size = sizeof(VerifiedKeyEvent);
-            break;
-        }
-        case VerifiedInputEvent::Type::MOTION: {
-            size = sizeof(VerifiedMotionEvent);
-            break;
-        }
+const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
+    switch (result) {
+        case InputDispatcher::FocusResult::OK:
+            return "Ok";
+        case InputDispatcher::FocusResult::NO_WINDOW:
+            return "Window not found";
+        case InputDispatcher::FocusResult::NOT_FOCUSABLE:
+            return "Window not focusable";
+        case InputDispatcher::FocusResult::NOT_VISIBLE:
+            return "Window not visible";
     }
-    const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
-    return sign(start, size);
 }
 
-std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
-    // SHA256 always generates 32-bytes result
-    std::array<uint8_t, 32> hash;
-    unsigned int hashLen = 0;
-    uint8_t* result =
-            HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
-    if (result == nullptr) {
-        ALOGE("Could not sign the data using HMAC");
-        return INVALID_HMAC;
+template <typename T>
+static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+    if (lhs == nullptr && rhs == nullptr) {
+        return true;
     }
-
-    if (hashLen != hash.size()) {
-        ALOGE("HMAC-SHA256 has unexpected length");
-        return INVALID_HMAC;
+    if (lhs == nullptr || rhs == nullptr) {
+        return false;
     }
-
-    return hash;
+    return *lhs == *rhs;
 }
 
 // --- InputDispatcher ---
@@ -404,8 +439,8 @@
         // To avoid leaking stack in case that call never comes, and for tests,
         // initialize it here anyways.
         mInTouchMode(true),
-        mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
-        mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+        mMaximumObscuringOpacityForTouch(1.0f),
+        mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
 
@@ -425,7 +460,7 @@
 
     while (!mConnectionsByFd.empty()) {
         sp<Connection> connection = mConnectionsByFd.begin()->second;
-        unregisterInputChannel(connection->inputChannel);
+        removeInputChannel(connection->inputChannel->getConnectionToken());
     }
 }
 
@@ -465,6 +500,11 @@
             nextWakeupTime = LONG_LONG_MIN;
         }
 
+        // If we are still waiting for ack on some events,
+        // we might have to wake up earlier to check if an app is anr'ing.
+        const nsecs_t nextAnrCheck = processAnrsLocked();
+        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+
         // We are about to enter an infinitely long sleep, because we have no commands or
         // pending or queued events
         if (nextWakeupTime == LONG_LONG_MAX) {
@@ -478,6 +518,81 @@
     mLooper->pollOnce(timeoutMillis);
 }
 
+/**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+    // Check if the application that we are waiting for is still focused.
+    std::shared_ptr<InputApplicationHandle> focusedApplication =
+            getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+    if (focusedApplication == nullptr ||
+        focusedApplication->getApplicationToken() !=
+                mAwaitedFocusedApplication->getApplicationToken()) {
+        // Unexpected because we should have reset the ANR timer when focused application changed
+        ALOGE("Waited for a focused window, but focused application has already changed to %s",
+              focusedApplication->getName().c_str());
+        return; // The focused application has changed.
+    }
+
+    const sp<InputWindowHandle>& focusedWindowHandle =
+            getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+    if (focusedWindowHandle != nullptr) {
+        return; // We now have a focused window. No need for ANR.
+    }
+    onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
+ * Check if any of the connections' wait queues have events that are too old.
+ * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
+ * Return the time at which we should wake up next.
+ */
+nsecs_t InputDispatcher::processAnrsLocked() {
+    const nsecs_t currentTime = now();
+    nsecs_t nextAnrCheck = LONG_LONG_MAX;
+    // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
+    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
+        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
+            processNoFocusedWindowAnrLocked();
+            mAwaitedFocusedApplication.reset();
+            mNoFocusedWindowTimeoutTime = std::nullopt;
+            return LONG_LONG_MIN;
+        } else {
+            // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
+            nextAnrCheck = *mNoFocusedWindowTimeoutTime;
+        }
+    }
+
+    // Check if any connection ANRs are due
+    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
+    if (currentTime < nextAnrCheck) { // most likely scenario
+        return nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck
+    }
+
+    // If we reached here, we have an unresponsive connection.
+    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+    if (connection == nullptr) {
+        ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+        return nextAnrCheck;
+    }
+    connection->responsive = false;
+    // Stop waking up for this unresponsive connection
+    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+    onAnrLocked(*connection);
+    return LONG_LONG_MIN;
+}
+
+std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+    sp<InputWindowHandle> window = getWindowHandleLocked(token);
+    if (window != nullptr) {
+        return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+    }
+    return DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+}
+
 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
     nsecs_t currentTime = now();
 
@@ -541,9 +656,6 @@
         if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             pokeUserActivityLocked(*mPendingEvent);
         }
-
-        // Get ready to dispatch the event.
-        resetAnrTimeoutsLocked();
     }
 
     // Now we have an event to dispatch.
@@ -563,22 +675,24 @@
 
     switch (mPendingEvent->type) {
         case EventEntry::Type::CONFIGURATION_CHANGED: {
-            ConfigurationChangedEntry* typedEntry =
-                    static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+            const ConfigurationChangedEntry& typedEntry =
+                    static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
             done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
             dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
             break;
         }
 
         case EventEntry::Type::DEVICE_RESET: {
-            DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
+            const DeviceResetEntry& typedEntry =
+                    static_cast<const DeviceResetEntry&>(*mPendingEvent);
             done = dispatchDeviceResetLocked(currentTime, typedEntry);
             dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
             break;
         }
 
         case EventEntry::Type::FOCUS: {
-            FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+            std::shared_ptr<FocusEntry> typedEntry =
+                    std::static_pointer_cast<FocusEntry>(mPendingEvent);
             dispatchFocusLocked(currentTime, typedEntry);
             done = true;
             dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
@@ -586,37 +700,38 @@
         }
 
         case EventEntry::Type::KEY: {
-            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+            std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
             if (isAppSwitchDue) {
-                if (isAppSwitchKeyEvent(*typedEntry)) {
+                if (isAppSwitchKeyEvent(*keyEntry)) {
                     resetPendingAppSwitchLocked(true);
                     isAppSwitchDue = false;
                 } else if (dropReason == DropReason::NOT_DROPPED) {
                     dropReason = DropReason::APP_SWITCH;
                 }
             }
-            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
                 dropReason = DropReason::STALE;
             }
             if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                 dropReason = DropReason::BLOCKED;
             }
-            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+            done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
             break;
         }
 
         case EventEntry::Type::MOTION: {
-            MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+            std::shared_ptr<MotionEntry> motionEntry =
+                    std::static_pointer_cast<MotionEntry>(mPendingEvent);
             if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
                 dropReason = DropReason::APP_SWITCH;
             }
-            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
                 dropReason = DropReason::STALE;
             }
             if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                 dropReason = DropReason::BLOCKED;
             }
-            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
             break;
         }
     }
@@ -637,11 +752,14 @@
  * Return false otherwise (the default behaviour)
  */
 bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
-    bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+    const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
             (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
-    if (isPointerDownEvent &&
-        mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
-        mInputTargetWaitApplicationToken != nullptr) {
+
+    // Optimize case where the current application is unresponsive and the user
+    // decides to touch a window in a different application.
+    // If the application takes too long to catch up then we drop all events preceding
+    // the touch into the other window.
+    if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
         int32_t displayId = motionEntry.displayId;
         int32_t x = static_cast<int32_t>(
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
@@ -650,27 +768,57 @@
         sp<InputWindowHandle> touchedWindowHandle =
                 findTouchedWindowAtLocked(displayId, x, y, nullptr);
         if (touchedWindowHandle != nullptr &&
-            touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) {
+            touchedWindowHandle->getApplicationToken() !=
+                    mAwaitedFocusedApplication->getApplicationToken()) {
             // User touched a different application than the one we are waiting on.
-            // Flag the event, and start pruning the input queue.
-            ALOGI("Pruning input queue because user touched a different application");
+            ALOGI("Pruning input queue because user touched a different application while waiting "
+                  "for %s",
+                  mAwaitedFocusedApplication->getName().c_str());
             return true;
         }
+
+        // Alternatively, maybe there's a gesture monitor that could handle this event
+        std::vector<TouchedMonitor> gestureMonitors =
+                findTouchedGestureMonitorsLocked(displayId, {});
+        for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+            sp<Connection> connection =
+                    getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+            if (connection != nullptr && connection->responsive) {
+                // This monitor could take more input. Drop all events preceding this
+                // event, so that gesture monitor could get a chance to receive the stream
+                ALOGW("Pruning the input queue because %s is unresponsive, but we have a "
+                      "responsive gesture monitor that may handle the event",
+                      mAwaitedFocusedApplication->getName().c_str());
+                return true;
+            }
+        }
+    }
+
+    // Prevent getting stuck: if we have a pending key event, and some motion events that have not
+    // yet been processed by some connections, the dispatcher will wait for these motion
+    // events to be processed before dispatching the key event. This is because these motion events
+    // may cause a new window to be launched, which the user might expect to receive focus.
+    // To prevent waiting forever for such events, just send the key to the currently focused window
+    if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+        ALOGD("Received a new pointer down event, stop waiting for events to process and "
+              "just send the pending key event to the focused window.");
+        mKeyIsWaitingForEventsTimeout = now();
     }
     return false;
 }
 
-bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
     bool needWake = mInboundQueue.empty();
-    mInboundQueue.push_back(entry);
+    mInboundQueue.push_back(std::move(newEntry));
+    EventEntry& entry = *(mInboundQueue.back());
     traceInboundQueueLengthLocked();
 
-    switch (entry->type) {
+    switch (entry.type) {
         case EventEntry::Type::KEY: {
             // Optimize app switch latency.
             // If the application takes too long to catch up then we drop all events preceding
             // the app switch key.
-            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
             if (isAppSwitchKeyEvent(keyEntry)) {
                 if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
                     mAppSwitchSawKeyDown = true;
@@ -689,12 +837,8 @@
         }
 
         case EventEntry::Type::MOTION: {
-            // Optimize case where the current application is unresponsive and the user
-            // decides to touch a window in a different application.
-            // If the application takes too long to catch up then we drop all events preceding
-            // the touch into the other window.
-            if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
-                mNextUnblockedEvent = entry;
+            if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
+                mNextUnblockedEvent = mInboundQueue.back();
                 needWake = true;
             }
             break;
@@ -713,11 +857,9 @@
     return needWake;
 }
 
-void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
-    entry->refCount += 1;
+void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
     mRecentQueue.push_back(entry);
     if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
-        mRecentQueue.front()->release();
         mRecentQueue.pop_front();
     }
 }
@@ -731,17 +873,16 @@
                 "Must provide a valid touch state if adding portal windows or outside targets");
     }
     // Traverse windows from front to back to find touched window.
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
-            int32_t flags = windowInfo->layoutParamsFlags;
+            auto flags = windowInfo->flags;
 
             if (windowInfo->visible) {
-                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                    bool isTouchModal = (flags &
-                                         (InputWindowInfo::FLAG_NOT_FOCUSABLE |
-                                          InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+                    bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) &&
+                            !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                         int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                         if (portalToDisplayId != ADISPLAY_ID_NONE &&
@@ -758,7 +899,7 @@
                     }
                 }
 
-                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
                     touchState->addOrUpdateWindow(windowHandle,
                                                   InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                                   BitSet32(0));
@@ -898,7 +1039,7 @@
 
 void InputDispatcher::drainInboundQueueLocked() {
     while (!mInboundQueue.empty()) {
-        EventEntry* entry = mInboundQueue.front();
+        std::shared_ptr<EventEntry> entry = mInboundQueue.front();
         mInboundQueue.pop_front();
         releaseInboundEventLocked(entry);
     }
@@ -907,72 +1048,53 @@
 
 void InputDispatcher::releasePendingEventLocked() {
     if (mPendingEvent) {
-        resetAnrTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
         mPendingEvent = nullptr;
     }
 }
 
-void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
+void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
     InjectionState* injectionState = entry->injectionState;
-    if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("Injected inbound event was dropped.");
 #endif
-        setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(*entry, InputEventInjectionResult::FAILED);
     }
     if (entry == mNextUnblockedEvent) {
         mNextUnblockedEvent = nullptr;
     }
     addRecentEventLocked(entry);
-    entry->release();
 }
 
 void InputDispatcher::resetKeyRepeatLocked() {
     if (mKeyRepeatState.lastKeyEntry) {
-        mKeyRepeatState.lastKeyEntry->release();
         mKeyRepeatState.lastKeyEntry = nullptr;
     }
 }
 
-KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
-    KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
+    std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
 
-    // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags &
             (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
-    if (entry->refCount == 1) {
-        entry->recycle();
-        entry->id = mIdGenerator.nextId();
-        entry->eventTime = currentTime;
-        entry->policyFlags = policyFlags;
-        entry->repeatCount += 1;
-    } else {
-        KeyEntry* newEntry =
-                new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
-                             entry->displayId, policyFlags, entry->action, entry->flags,
-                             entry->keyCode, entry->scanCode, entry->metaState,
-                             entry->repeatCount + 1, entry->downTime);
 
-        mKeyRepeatState.lastKeyEntry = newEntry;
-        entry->release();
+    std::shared_ptr<KeyEntry> newEntry =
+            std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
+                                       entry->source, entry->displayId, policyFlags, entry->action,
+                                       entry->flags, entry->keyCode, entry->scanCode,
+                                       entry->metaState, entry->repeatCount + 1, entry->downTime);
 
-        entry = newEntry;
-    }
-    entry->syntheticRepeat = true;
-
-    // Increment reference count since we keep a reference to the event in
-    // mKeyRepeatState.lastKeyEntry in addition to the one we return.
-    entry->refCount += 1;
-
+    newEntry->syntheticRepeat = true;
+    mKeyRepeatState.lastKeyEntry = newEntry;
     mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
-    return entry;
+    return newEntry;
 }
 
 bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
-                                                         ConfigurationChangedEntry* entry) {
+                                                         const ConfigurationChangedEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
+    ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
 #endif
 
     // Reset key repeating in case a keyboard device was added or removed or something.
@@ -981,24 +1103,26 @@
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
-    commandEntry->eventTime = entry->eventTime;
+    commandEntry->eventTime = entry.eventTime;
     postCommandLocked(std::move(commandEntry));
     return true;
 }
 
-bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
+bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
+                                                const DeviceResetEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
-          entry->deviceId);
+    ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+          entry.deviceId);
 #endif
 
     CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
-    options.deviceId = entry->deviceId;
+    options.deviceId = entry.deviceId;
     synthesizeCancelationEventsForAllConnectionsLocked(options);
     return true;
 }
 
-void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
+                                              std::string_view reason) {
     if (mPendingEvent != nullptr) {
         // Move the pending event to the front of the queue. This will give the chance
         // for the pending event to get dispatched to the newly focused window
@@ -1006,21 +1130,24 @@
         mPendingEvent = nullptr;
     }
 
-    FocusEntry* focusEntry =
-            new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
+    std::unique_ptr<FocusEntry> focusEntry =
+            std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus,
+                                         reason);
 
     // This event should go to the front of the queue, but behind all other focus events
     // Find the last focus event, and insert right after it
-    std::deque<EventEntry*>::reverse_iterator it =
+    std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
             std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
-                         [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+                         [](const std::shared_ptr<EventEntry>& event) {
+                             return event->type == EventEntry::Type::FOCUS;
+                         });
 
     // Maintain the order of focus events. Insert the entry after all other focus events.
-    mInboundQueue.insert(it.base(), focusEntry);
+    mInboundQueue.insert(it.base(), std::move(focusEntry));
 }
 
-void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
-    sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
+    std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
     if (channel == nullptr) {
         return; // Window has gone away
     }
@@ -1028,11 +1155,14 @@
     target.inputChannel = channel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
     entry->dispatchInProgress = true;
-
+    std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
+            channel->getName();
+    std::string reason = std::string("reason=").append(entry->reason);
+    android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
     dispatchEventLocked(currentTime, entry, {target});
 }
 
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
     if (!entry->dispatchInProgress) {
@@ -1040,11 +1170,17 @@
             (entry->policyFlags & POLICY_FLAG_TRUSTED) &&
             (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
             if (mKeyRepeatState.lastKeyEntry &&
-                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
                 // We have seen two identical key downs in a row which indicates that the device
                 // driver is automatically generating key repeats itself.  We take note of the
                 // repeat here, but we disable our own next key repeat timer since it is clear that
                 // we will not need to synthesize key repeats ourselves.
+                mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
+                // Make sure we don't get key down from a different device. If a different
+                // device Id has same key pressed down, the new device Id will replace the
+                // current one to hold the key repeat with repeat count reset.
+                // In the future when got a KEY_UP on the device id, drop it and do not
+                // stop the key repeat on current device.
                 entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                 resetKeyRepeatLocked();
                 mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
@@ -1054,7 +1190,12 @@
                 mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
             }
             mKeyRepeatState.lastKeyEntry = entry;
-            entry->refCount += 1;
+        } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
+                   mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
+            // The key on device 'deviceId' is still down, do not stop key repeat
+#if DEBUG_INBOUND_EVENT_DETAILS
+            ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+#endif
         } else if (!entry->syntheticRepeat) {
             resetKeyRepeatLocked();
         }
@@ -1087,14 +1228,11 @@
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                     &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
-            sp<InputWindowHandle> focusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
-            if (focusedWindowHandle != nullptr) {
-                commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
-            }
+            sp<IBinder> focusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+            commandEntry->connectionToken = focusedWindowToken;
             commandEntry->keyEntry = entry;
             postCommandLocked(std::move(commandEntry));
-            entry->refCount += 1;
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1107,23 +1245,23 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DropReason::NOT_DROPPED) {
-        setInjectionResult(entry,
-                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
-                                                             : INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(*entry,
+                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+                                                             : InputEventInjectionResult::FAILED);
         mReporter->reportDroppedKey(entry->id);
         return true;
     }
 
     // Identify targets.
     std::vector<InputTarget> inputTargets;
-    int32_t injectionResult =
+    InputEventInjectionResult injectionResult =
             findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
-    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    if (injectionResult == InputEventInjectionResult::PENDING) {
         return false;
     }
 
-    setInjectionResult(entry, injectionResult);
-    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+    setInjectionResult(*entry, injectionResult);
+    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
         return true;
     }
 
@@ -1146,7 +1284,7 @@
 #endif
 }
 
-bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                            DropReason* dropReason, nsecs_t* nextWakeupTime) {
     ATRACE_CALL();
     // Preprocessing.
@@ -1158,9 +1296,9 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DropReason::NOT_DROPPED) {
-        setInjectionResult(entry,
-                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
-                                                             : INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(*entry,
+                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+                                                             : InputEventInjectionResult::FAILED);
         return true;
     }
 
@@ -1170,7 +1308,7 @@
     std::vector<InputTarget> inputTargets;
 
     bool conflictingPointerActions = false;
-    int32_t injectionResult;
+    InputEventInjectionResult injectionResult;
     if (isPointerEvent) {
         // Pointer event.  (eg. touchscreen)
         injectionResult =
@@ -1181,16 +1319,16 @@
         injectionResult =
                 findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
     }
-    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    if (injectionResult == InputEventInjectionResult::PENDING) {
         return false;
     }
 
-    setInjectionResult(entry, injectionResult);
-    if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+    setInjectionResult(*entry, injectionResult);
+    if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
         ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
         return true;
     }
-    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
         CancelationOptions::Mode mode(isPointerEvent
                                               ? CancelationOptions::CANCEL_POINTER_EVENTS
                                               : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
@@ -1259,13 +1397,16 @@
 #endif
 }
 
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
+                                          std::shared_ptr<EventEntry> eventEntry,
                                           const std::vector<InputTarget>& inputTargets) {
     ATRACE_CALL();
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("dispatchEventToCurrentInputTargets");
 #endif
 
+    updateInteractionTokensLocked(*eventEntry, inputTargets);
+
     ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
     pokeUserActivityLocked(*eventEntry);
@@ -1285,109 +1426,29 @@
     }
 }
 
-int32_t InputDispatcher::handleTargetsNotReadyLocked(
-        nsecs_t currentTime, const EventEntry& entry,
-        const sp<InputApplicationHandle>& applicationHandle,
-        const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
-    if (applicationHandle == nullptr && windowHandle == nullptr) {
-        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Waiting for system to become ready for input.  Reason: %s", reason);
-            }
-            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
-            mInputTargetWaitStartTime = currentTime;
-            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
-            mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationToken.clear();
-        }
-    } else {
-        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-            ALOGI("Waiting for application to become ready for input: %s.  Reason: %s",
-                  getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
-            std::chrono::nanoseconds timeout;
-            if (windowHandle != nullptr) {
-                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
-            } else if (applicationHandle != nullptr) {
-                timeout =
-                        applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
-            } else {
-                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
-            }
-
-            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
-            mInputTargetWaitStartTime = currentTime;
-            mInputTargetWaitTimeoutTime = currentTime + timeout.count();
-            mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationToken.clear();
-
-            if (windowHandle != nullptr) {
-                mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
-            }
-            if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
-                mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
-            }
-        }
-    }
-
-    if (mInputTargetWaitTimeoutExpired) {
-        return INPUT_EVENT_INJECTION_TIMED_OUT;
-    }
-
-    if (currentTime >= mInputTargetWaitTimeoutTime) {
-        onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
-                    mInputTargetWaitStartTime, reason);
-
-        // Force poll loop to wake up immediately on next iteration once we get the
-        // ANR response back from the policy.
-        *nextWakeupTime = LONG_LONG_MIN;
-        return INPUT_EVENT_INJECTION_PENDING;
-    } else {
-        // Force poll loop to wake up when timeout is due.
-        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
-            *nextWakeupTime = mInputTargetWaitTimeoutTime;
-        }
-        return INPUT_EVENT_INJECTION_PENDING;
+void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+    // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
+    // If the policy decides to close the app, we will get a channel removal event via
+    // unregisterInputChannel, and will clean up the connection that way. We are already not
+    // sending new pointers to the connection when it blocked, but focused events will continue to
+    // pile up.
+    ALOGW("Canceling events for %s because it is unresponsive",
+          connection->inputChannel->getName().c_str());
+    if (connection->status == Connection::STATUS_NORMAL) {
+        CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+                                   "application not responding");
+        synthesizeCancelationEventsForConnectionLocked(connection, options);
     }
 }
 
-void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
-    for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
-        TouchState& state = pair.second;
-        state.removeWindowByToken(token);
-    }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
-        nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) {
-    if (timeoutExtension > 0) {
-        // Extend the timeout.
-        mInputTargetWaitTimeoutTime = now() + timeoutExtension;
-    } else {
-        // Give up.
-        mInputTargetWaitTimeoutExpired = true;
-
-        // Input state will not be realistic.  Mark it out of sync.
-        sp<Connection> connection = getConnectionLocked(inputConnectionToken);
-        if (connection != nullptr) {
-            removeWindowByTokenLocked(inputConnectionToken);
-
-            if (connection->status == Connection::STATUS_NORMAL) {
-                CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
-                                           "application not responding");
-                synthesizeCancelationEventsForConnectionLocked(connection, options);
-            }
-        }
-    }
-}
-
-void InputDispatcher::resetAnrTimeoutsLocked() {
+void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
     if (DEBUG_FOCUS) {
         ALOGD("Resetting ANR timeouts.");
     }
 
     // Reset input target wait timeout.
-    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
-    mInputTargetWaitApplicationToken.clear();
+    mNoFocusedWindowTimeoutTime = std::nullopt;
+    mAwaitedFocusedApplication.reset();
 }
 
 /**
@@ -1418,45 +1479,115 @@
     return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
 }
 
-int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const EventEntry& entry,
-                                                        std::vector<InputTarget>& inputTargets,
-                                                        nsecs_t* nextWakeupTime) {
+bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
+                                                const char* focusedWindowName) {
+    if (mAnrTracker.empty()) {
+        // already processed all events that we waited for
+        mKeyIsWaitingForEventsTimeout = std::nullopt;
+        return false;
+    }
+
+    if (!mKeyIsWaitingForEventsTimeout.has_value()) {
+        // Start the timer
+        ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
+              "focus to change",
+              focusedWindowName);
+        mKeyIsWaitingForEventsTimeout = currentTime +
+                std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT)
+                        .count();
+        return true;
+    }
+
+    // We still have pending events, and already started the timer
+    if (currentTime < *mKeyIsWaitingForEventsTimeout) {
+        return true; // Still waiting
+    }
+
+    // Waited too long, and some connection still hasn't processed all motions
+    // Just send the key to the focused window
+    ALOGW("Dispatching key to %s even though there are other unprocessed events",
+          focusedWindowName);
+    mKeyIsWaitingForEventsTimeout = std::nullopt;
+    return false;
+}
+
+InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
+        nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
+        nsecs_t* nextWakeupTime) {
     std::string reason;
 
     int32_t displayId = getTargetDisplayId(entry);
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-    sp<InputApplicationHandle> focusedApplicationHandle =
+    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+    std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
             getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
 
     // If there is no currently focused window and no focused application
     // then drop the event.
-    if (focusedWindowHandle == nullptr) {
-        if (focusedApplicationHandle != nullptr) {
-            return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
-                                               nullptr, nextWakeupTime,
-                                               "Waiting because no window has focus but there is "
-                                               "a focused application that may eventually add a "
-                                               "window when it finishes starting up.");
-        }
-
-        ALOGI("Dropping event because there is no focused window or focused application in display "
-              "%" PRId32 ".",
-              displayId);
-        return INPUT_EVENT_INJECTION_FAILED;
+    if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
+        ALOGI("Dropping %s event because there is no focused window or focused application in "
+              "display %" PRId32 ".",
+              EventEntry::typeToString(entry.type), displayId);
+        return InputEventInjectionResult::FAILED;
     }
 
+    // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
+    // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
+    // start interacting with another application via touch (app switch). This code can be removed
+    // if the "no focused window ANR" is moved to the policy. Input doesn't know whether
+    // an app is expected to have a focused window.
+    if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
+        if (!mNoFocusedWindowTimeoutTime.has_value()) {
+            // We just discovered that there's no focused window. Start the ANR timer
+            std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
+                    DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+            mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
+            mAwaitedFocusedApplication = focusedApplicationHandle;
+            mAwaitedApplicationDisplayId = displayId;
+            ALOGW("Waiting because no window has focus but %s may eventually add a "
+                  "window when it finishes starting up. Will wait for %" PRId64 "ms",
+                  mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
+            *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+            return InputEventInjectionResult::PENDING;
+        } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
+            // Already raised ANR. Drop the event
+            ALOGE("Dropping %s event because there is no focused window",
+                  EventEntry::typeToString(entry.type));
+            return InputEventInjectionResult::FAILED;
+        } else {
+            // Still waiting for the focused window
+            return InputEventInjectionResult::PENDING;
+        }
+    }
+
+    // we have a valid, non-null focused window
+    resetNoFocusedWindowTimeoutLocked();
+
     // Check permissions.
     if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
-        return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        return InputEventInjectionResult::PERMISSION_DENIED;
     }
 
-    // Check whether the window is ready for more input.
-    reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
-    if (!reason.empty()) {
-        return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
-                                           focusedWindowHandle, nextWakeupTime, reason.c_str());
+    if (focusedWindowHandle->getInfo()->paused) {
+        ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+        return InputEventInjectionResult::PENDING;
+    }
+
+    // If the event is a key event, then we must wait for all previous events to
+    // complete before delivering it because previous events may have the
+    // side-effect of transferring focus to a different window and we want to
+    // ensure that the following keys are sent to the new window.
+    //
+    // Suppose the user touches a button in a window then immediately presses "A".
+    // If the button causes a pop-up window to appear then we want to ensure that
+    // the "A" key is delivered to the new pop-up window.  This is because users
+    // often anticipate pending UI changes when typing on a keyboard.
+    // To obtain this behavior, we must serialize key events with respect to all
+    // prior input events.
+    if (entry.type == EventEntry::Type::KEY) {
+        if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
+            *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+            return InputEventInjectionResult::PENDING;
+        }
     }
 
     // Success!  Output targets.
@@ -1465,14 +1596,38 @@
                           BitSet32(0), inputTargets);
 
     // Done.
-    return INPUT_EVENT_INJECTION_SUCCEEDED;
+    return InputEventInjectionResult::SUCCEEDED;
 }
 
-int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const MotionEntry& entry,
-                                                        std::vector<InputTarget>& inputTargets,
-                                                        nsecs_t* nextWakeupTime,
-                                                        bool* outConflictingPointerActions) {
+/**
+ * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
+ * that are currently unresponsive.
+ */
+std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
+        const std::vector<TouchedMonitor>& monitors) const {
+    std::vector<TouchedMonitor> responsiveMonitors;
+    std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
+                 [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
+                     sp<Connection> connection = getConnectionLocked(
+                             monitor.monitor.inputChannel->getConnectionToken());
+                     if (connection == nullptr) {
+                         ALOGE("Could not find connection for monitor %s",
+                               monitor.monitor.inputChannel->getName().c_str());
+                         return false;
+                     }
+                     if (!connection->responsive) {
+                         ALOGW("Unresponsive monitor %s will not get the new gesture",
+                               connection->inputChannel->getName().c_str());
+                         return false;
+                     }
+                     return true;
+                 });
+    return responsiveMonitors;
+}
+
+InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
+        nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
+        nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
     ATRACE_CALL();
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
@@ -1487,9 +1642,10 @@
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
-    int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
     InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-    sp<InputWindowHandle> newHoverWindowHandle;
+    sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+    sp<InputWindowHandle> newTouchedWindowHandle;
 
     // Copy current touch state into tempTouchState.
     // This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -1521,7 +1677,7 @@
                   "in display %" PRId32,
                   displayId);
             // TODO: test multiple simultaneous input streams.
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionResult = InputEventInjectionResult::FAILED;
             switchedDevice = false;
             wrongDevice = true;
             goto Failed;
@@ -1537,7 +1693,7 @@
               "in display %" PRId32,
               displayId);
         // TODO: test multiple simultaneous input streams.
-        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
         switchedDevice = false;
         wrongDevice = true;
         goto Failed;
@@ -1558,7 +1714,7 @@
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        sp<InputWindowHandle> newTouchedWindowHandle =
+        newTouchedWindowHandle =
                 findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                           isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
 
@@ -1583,11 +1739,51 @@
             newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
         }
 
+        if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
+            ALOGI("Not sending touch event to %s because it is paused",
+                  newTouchedWindowHandle->getName().c_str());
+            newTouchedWindowHandle = nullptr;
+        }
+
+        // Ensure the window has a connection and the connection is responsive
+        if (newTouchedWindowHandle != nullptr) {
+            const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);
+            if (!isResponsive) {
+                ALOGW("%s will not receive the new gesture at %" PRIu64,
+                      newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+                newTouchedWindowHandle = nullptr;
+            }
+        }
+
+        // Drop events that can't be trusted due to occlusion
+        if (newTouchedWindowHandle != nullptr &&
+            mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+            TouchOcclusionInfo occlusionInfo =
+                    computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
+            if (!isTouchTrustedLocked(occlusionInfo)) {
+                if (DEBUG_TOUCH_OCCLUSION) {
+                    ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+                    for (const auto& log : occlusionInfo.debugInfo) {
+                        ALOGD("%s", log.c_str());
+                    }
+                }
+                onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+                if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+                    ALOGW("Dropping untrusted touch event due to %s/%d",
+                          occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+                    newTouchedWindowHandle = nullptr;
+                }
+            }
+        }
+
+        // Also don't send the new touch event to unresponsive gesture monitors
+        newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+
         if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
             ALOGI("Dropping event because there is no touchable window or gesture monitor at "
                   "(%d, %d) in display %" PRId32 ".",
                   x, y, displayId);
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
         }
 
@@ -1604,10 +1800,10 @@
             }
 
             // Update hover state.
-            if (isHoverAction) {
+            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+                newHoverWindowHandle = nullptr;
+            } else if (isHoverAction) {
                 newHoverWindowHandle = newTouchedWindowHandle;
-            } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
-                newHoverWindowHandle = mLastHoverWindowHandle;
             }
 
             // Update the temporary touch state.
@@ -1630,7 +1826,7 @@
                       "dropped the pointer down event in display %" PRId32,
                       displayId);
             }
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
         }
 
@@ -1642,8 +1838,7 @@
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
-            sp<InputWindowHandle> newTouchedWindowHandle =
-                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+            newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                 oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
                 if (DEBUG_FOCUS) {
@@ -1680,8 +1875,11 @@
     }
 
     if (newHoverWindowHandle != mLastHoverWindowHandle) {
-        // Let the previous window know that the hover sequence is over.
-        if (mLastHoverWindowHandle != nullptr) {
+        // Let the previous window know that the hover sequence is over, unless we already did it
+        // when dispatching it as is to newTouchedWindowHandle.
+        if (mLastHoverWindowHandle != nullptr &&
+            (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
+             mLastHoverWindowHandle != newTouchedWindowHandle)) {
 #if DEBUG_HOVER
             ALOGD("Sending hover exit event to window %s.",
                   mLastHoverWindowHandle->getName().c_str());
@@ -1690,8 +1888,11 @@
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
         }
 
-        // Let the new window know that the hover sequence is starting.
-        if (newHoverWindowHandle != nullptr) {
+        // Let the new window know that the hover sequence is starting, unless we already did it
+        // when dispatching it as is to newTouchedWindowHandle.
+        if (newHoverWindowHandle != nullptr &&
+            (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
+             newHoverWindowHandle != newTouchedWindowHandle)) {
 #if DEBUG_HOVER
             ALOGD("Sending hover enter event to window %s.",
                   newHoverWindowHandle->getName().c_str());
@@ -1710,7 +1911,7 @@
             if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
                 haveForegroundWindow = true;
                 if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
-                    injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                    injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
                     injectionPermission = INJECTION_PERMISSION_DENIED;
                     goto Failed;
                 }
@@ -1721,7 +1922,7 @@
             ALOGI("Dropping event because there is no touched foreground window in display "
                   "%" PRId32 " or gesture monitor to receive it.",
                   displayId);
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
         }
 
@@ -1749,21 +1950,6 @@
         }
     }
 
-    // Ensure all touched foreground windows are ready for new input.
-    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
-        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            // Check whether the window is ready for more input.
-            std::string reason =
-                    checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
-                                                       entry, "touched");
-            if (!reason.empty()) {
-                return handleTargetsNotReadyLocked(currentTime, entry, nullptr,
-                                                   touchedWindow.windowHandle, nextWakeupTime,
-                                                   reason.c_str());
-            }
-        }
-    }
-
     // If this is the first pointer going down and the touched window has a wallpaper
     // then also add the touched wallpaper windows so they are locked in for the duration
     // of the touch gesture.
@@ -1774,12 +1960,12 @@
         sp<InputWindowHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
-            const std::vector<sp<InputWindowHandle>> windowHandles =
+            const std::vector<sp<InputWindowHandle>>& windowHandles =
                     getWindowHandlesLocked(displayId);
             for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
                 const InputWindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId &&
-                    windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
+                    windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
                     tempTouchState
                             .addOrUpdateWindow(windowHandle,
                                                InputTarget::FLAG_WINDOW_IS_OBSCURED |
@@ -1793,7 +1979,7 @@
     }
 
     // Success!  Output targets.
-    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+    injectionResult = InputEventInjectionResult::SUCCEEDED;
 
     for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
         addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
@@ -1911,7 +2097,8 @@
 
     if (it == inputTargets.end()) {
         InputTarget inputTarget;
-        sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+        std::shared_ptr<InputChannel> inputChannel =
+                getInputChannelLocked(windowHandle->getToken());
         if (inputChannel == nullptr) {
             ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
             return;
@@ -1926,8 +2113,7 @@
     ALOG_ASSERT(it->flags == targetFlags);
     ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
 
-    it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
-                    windowInfo->windowXScale, windowInfo->windowYScale);
+    it->addPointers(pointerIds, windowInfo->transform);
 }
 
 void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1950,7 +2136,9 @@
     InputTarget target;
     target.inputChannel = monitor.inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-    target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
+    ui::Transform t;
+    t.set(xOffset, yOffset);
+    target.setDefaultPointerTransform(t);
     inputTargets.push_back(target);
 }
 
@@ -1989,14 +2177,18 @@
     auto otherInfo = otherHandle->getInfo();
     if (!otherInfo->visible) {
         return false;
-    } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) {
-      // In general, if ownerPid is the same we don't want to generate occlusion
-      // events. This line is now necessary since we are including all Surfaces
-      // in occlusion calculation, so if we didn't check PID like this SurfaceView
-      // would occlude their parents. On the other hand before we started including
-      // all surfaces in occlusion calculation and had this line, we would count
-      // windows with an input channel from the same PID as occluding, and so we
-      // preserve this behavior with the getToken() == null check.
+    } else if (otherInfo->alpha == 0 &&
+               otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+        // Those act as if they were invisible, so we don't need to flag them.
+        // We do want to potentially flag touchable windows even if they have 0
+        // opacity, since they can consume touches and alter the effects of the
+        // user interaction (eg. apps that rely on
+        // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
+        // windows), hence we also check for FLAG_NOT_TOUCHABLE.
+        return false;
+    } else if (info->ownerUid == otherInfo->ownerUid) {
+        // If ownerUid is the same we don't generate occlusion events as there
+        // is no security boundary within an uid.
         return false;
     } else if (otherInfo->trustedOverlay) {
         return false;
@@ -2006,10 +2198,113 @@
     return true;
 }
 
+/**
+ * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is
+ * untrusted, one should check:
+ *
+ * 1. If result.hasBlockingOcclusion is true.
+ *    If it's, it means the touch should be blocked due to a window with occlusion mode of
+ *    BLOCK_UNTRUSTED.
+ *
+ * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch.
+ *    If it is (and 1 is false), then the touch should be blocked because a stack of windows
+ *    (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed
+ *    obscuring opacity above the threshold. Note that if there was no window of occlusion mode
+ *    USE_OPACITY, result.obscuringOpacity would've been 0 and since
+ *    mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true.
+ *
+ * If neither of those is true, then it means the touch can be allowed.
+ */
+InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+        const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    int32_t displayId = windowInfo->displayId;
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    TouchOcclusionInfo info;
+    info.hasBlockingOcclusion = false;
+    info.obscuringOpacity = 0;
+    info.obscuringUid = -1;
+    std::map<int32_t, float> opacityByUid;
+    for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
+        }
+        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
+            windowInfo->ownerUid != otherInfo->ownerUid && otherInfo->frameContainsPoint(x, y)) {
+            if (DEBUG_TOUCH_OCCLUSION) {
+                info.debugInfo.push_back(
+                        dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+            }
+            // canBeObscuredBy() has returned true above, which means this window is untrusted, so
+            // we perform the checks below to see if the touch can be propagated or not based on the
+            // window's touch occlusion mode
+            if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
+                info.hasBlockingOcclusion = true;
+                info.obscuringUid = otherInfo->ownerUid;
+                info.obscuringPackage = otherInfo->packageName;
+                break;
+            }
+            if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
+                uint32_t uid = otherInfo->ownerUid;
+                float opacity =
+                        (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
+                // Given windows A and B:
+                // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
+                opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
+                opacityByUid[uid] = opacity;
+                if (opacity > info.obscuringOpacity) {
+                    info.obscuringOpacity = opacity;
+                    info.obscuringUid = uid;
+                    info.obscuringPackage = otherInfo->packageName;
+                }
+            }
+        }
+    }
+    if (DEBUG_TOUCH_OCCLUSION) {
+        info.debugInfo.push_back(
+                dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+    }
+    return info;
+}
+
+std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
+                                                         bool isTouchedWindow) const {
+    return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32
+                                ", mode=%s, alpha=%.2f, "
+                                "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+                                "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
+                                "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
+                        (isTouchedWindow) ? "[TOUCHED] " : "",
+                        NamedEnum::string(info->type, "%" PRId32).c_str(),
+                        info->packageName.c_str(), info->ownerUid, info->id,
+                        toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+                        info->frameTop, info->frameRight, info->frameBottom,
+                        dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+                        info->applicationInfo.name.c_str(), info->flags.string().c_str(),
+                        info->inputFeatures.string().c_str(), toString(info->token != nullptr));
+}
+
+bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+    if (occlusionInfo.hasBlockingOcclusion) {
+        ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
+              occlusionInfo.obscuringUid);
+        return false;
+    }
+    if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
+        ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+              "%.2f, maximum allowed = %.2f)",
+              occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+              occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
+        return false;
+    }
+    return true;
+}
+
 bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
                                                     int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
@@ -2025,7 +2320,7 @@
 
 bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
@@ -2040,94 +2335,8 @@
     return false;
 }
 
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
-        nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
-        const EventEntry& eventEntry, const char* targetType) {
-    // If the window is paused then keep waiting.
-    if (windowHandle->getInfo()->paused) {
-        return StringPrintf("Waiting because the %s window is paused.", targetType);
-    }
-
-    // If the window's connection is not registered then keep waiting.
-    sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
-    if (connection == nullptr) {
-        return StringPrintf("Waiting because the %s window's input channel is not "
-                            "registered with the input dispatcher.  The window may be in the "
-                            "process of being removed.",
-                            targetType);
-    }
-
-    // If the connection is dead then keep waiting.
-    if (connection->status != Connection::STATUS_NORMAL) {
-        return StringPrintf("Waiting because the %s window's input connection is %s."
-                            "The window may be in the process of being removed.",
-                            targetType, connection->getStatusLabel());
-    }
-
-    // If the connection is backed up then keep waiting.
-    if (connection->inputPublisherBlocked) {
-        return StringPrintf("Waiting because the %s window's input channel is full.  "
-                            "Outbound queue length: %zu.  Wait queue length: %zu.",
-                            targetType, connection->outboundQueue.size(),
-                            connection->waitQueue.size());
-    }
-
-    // Ensure that the dispatch queues aren't too far backed up for this event.
-    if (eventEntry.type == EventEntry::Type::KEY) {
-        // If the event is a key event, then we must wait for all previous events to
-        // complete before delivering it because previous events may have the
-        // side-effect of transferring focus to a different window and we want to
-        // ensure that the following keys are sent to the new window.
-        //
-        // Suppose the user touches a button in a window then immediately presses "A".
-        // If the button causes a pop-up window to appear then we want to ensure that
-        // the "A" key is delivered to the new pop-up window.  This is because users
-        // often anticipate pending UI changes when typing on a keyboard.
-        // To obtain this behavior, we must serialize key events with respect to all
-        // prior input events.
-        if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) {
-            return StringPrintf("Waiting to send key event because the %s window has not "
-                                "finished processing all of the input events that were previously "
-                                "delivered to it.  Outbound queue length: %zu.  Wait queue length: "
-                                "%zu.",
-                                targetType, connection->outboundQueue.size(),
-                                connection->waitQueue.size());
-        }
-    } else {
-        // Touch events can always be sent to a window immediately because the user intended
-        // to touch whatever was visible at the time.  Even if focus changes or a new
-        // window appears moments later, the touch event was meant to be delivered to
-        // whatever window happened to be on screen at the time.
-        //
-        // Generic motion events, such as trackball or joystick events are a little trickier.
-        // Like key events, generic motion events are delivered to the focused window.
-        // Unlike key events, generic motion events don't tend to transfer focus to other
-        // windows and it is not important for them to be serialized.  So we prefer to deliver
-        // generic motion events as soon as possible to improve efficiency and reduce lag
-        // through batching.
-        //
-        // The one case where we pause input event delivery is when the wait queue is piling
-        // up with lots of events because the application is not responding.
-        // This condition ensures that ANRs are detected reliably.
-        if (!connection->waitQueue.empty() &&
-            currentTime >=
-                    connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
-            return StringPrintf("Waiting to send non-key event because the %s window has not "
-                                "finished processing certain input events that were delivered to "
-                                "it over "
-                                "%0.1fms ago.  Wait queue length: %zu.  Wait queue head age: "
-                                "%0.1fms.",
-                                targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
-                                connection->waitQueue.size(),
-                                (currentTime - connection->waitQueue.front()->deliveryTime) *
-                                        0.000001f);
-        }
-    }
-    return "";
-}
-
 std::string InputDispatcher::getApplicationWindowLabel(
-        const sp<InputApplicationHandle>& applicationHandle,
+        const std::shared_ptr<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != nullptr) {
         if (windowHandle != nullptr) {
@@ -2148,11 +2357,10 @@
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     if (focusedWindowHandle != nullptr) {
         const InputWindowInfo* info = focusedWindowHandle->getInfo();
-        if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
+        if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
 #if DEBUG_DISPATCH_CYCLE
             ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
 #endif
@@ -2199,7 +2407,7 @@
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                  const sp<Connection>& connection,
-                                                 EventEntry* eventEntry,
+                                                 std::shared_ptr<EventEntry> eventEntry,
                                                  const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
@@ -2233,7 +2441,7 @@
 
         const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
         if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
-            MotionEntry* splitMotionEntry =
+            std::unique_ptr<MotionEntry> splitMotionEntry =
                     splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
             if (!splitMotionEntry) {
                 return; // split event was dropped
@@ -2243,8 +2451,8 @@
                       connection->getInputChannelName().c_str());
                 logOutboundMotionDetails("  ", *splitMotionEntry);
             }
-            enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
-            splitMotionEntry->release();
+            enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),
+                                         inputTarget);
             return;
         }
     }
@@ -2255,7 +2463,7 @@
 
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                    const sp<Connection>& connection,
-                                                   EventEntry* eventEntry,
+                                                   std::shared_ptr<EventEntry> eventEntry,
                                                    const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
@@ -2287,7 +2495,7 @@
 }
 
 void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
-                                                 EventEntry* eventEntry,
+                                                 std::shared_ptr<EventEntry> eventEntry,
                                                  const InputTarget& inputTarget,
                                                  int32_t dispatchMode) {
     if (ATRACE_ENABLED()) {
@@ -2309,11 +2517,11 @@
 
     // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
     // different EventEntry than what was passed in.
-    EventEntry* newEntry = dispatchEntry->eventEntry;
+    EventEntry& newEntry = *(dispatchEntry->eventEntry);
     // Apply target flags and update the connection's input state.
-    switch (newEntry->type) {
+    switch (newEntry.type) {
         case EventEntry::Type::KEY: {
-            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
             dispatchEntry->resolvedEventId = keyEntry.id;
             dispatchEntry->resolvedAction = keyEntry.action;
             dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2330,7 +2538,7 @@
         }
 
         case EventEntry::Type::MOTION: {
-            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
             // Assign a default value to dispatchEntry that will never be generated by InputReader,
             // and assign a InputDispatcher value if it doesn't change in the if-else chain below.
             constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
@@ -2401,7 +2609,7 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
             LOG_ALWAYS_FATAL("%s events should not go to apps",
-                             EventEntry::typeToString(newEntry->type));
+                             EventEntry::typeToString(newEntry.type));
             break;
         }
     }
@@ -2416,31 +2624,92 @@
     traceOutboundQueueLength(connection);
 }
 
+/**
+ * This function is purely for debugging. It helps us understand where the user interaction
+ * was taking place. For example, if user is touching launcher, we will see a log that user
+ * started interacting with launcher. In that example, the event would go to the wallpaper as well.
+ * We will see both launcher and wallpaper in that list.
+ * Once the interaction with a particular set of connections starts, no new logs will be printed
+ * until the set of interacted connections changes.
+ *
+ * The following items are skipped, to reduce the logspam:
+ * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged
+ * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions).
+ * This includes situations like the soft BACK button key. When the user releases (lifts up the
+ * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP.
+ * Both of those ACTION_UP events would not be logged
+ * Monitors (both gesture and global): any gesture monitors or global monitors receiving events
+ * will not be logged. This is omitted to reduce the amount of data printed.
+ * If you see <none>, it's likely that one of the gesture monitors pilfered the event, and therefore
+ * gesture monitor is the only connection receiving the remainder of the gesture.
+ */
+void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
+                                                    const std::vector<InputTarget>& targets) {
+    // Skip ACTION_UP events, and all events other than keys and motions
+    if (entry.type == EventEntry::Type::KEY) {
+        const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+        if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+            return;
+        }
+    } else if (entry.type == EventEntry::Type::MOTION) {
+        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+        if (motionEntry.action == AMOTION_EVENT_ACTION_UP ||
+            motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
+            return;
+        }
+    } else {
+        return; // Not a key or a motion
+    }
+
+    std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+    std::vector<sp<Connection>> newConnections;
+    for (const InputTarget& target : targets) {
+        if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
+            InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+            continue; // Skip windows that receive ACTION_OUTSIDE
+        }
+
+        sp<IBinder> token = target.inputChannel->getConnectionToken();
+        sp<Connection> connection = getConnectionLocked(token);
+        if (connection == nullptr || connection->monitor) {
+            continue; // We only need to keep track of the non-monitor connections.
+        }
+        newConnectionTokens.insert(std::move(token));
+        newConnections.emplace_back(connection);
+    }
+    if (newConnectionTokens == mInteractionConnectionTokens) {
+        return; // no change
+    }
+    mInteractionConnectionTokens = newConnectionTokens;
+
+    std::string windowList;
+    for (const sp<Connection>& connection : newConnections) {
+        windowList += connection->getWindowName() + ", ";
+    }
+    std::string message = "Interaction with windows: " + windowList;
+    if (windowList.empty()) {
+        message += "<none>";
+    }
+    android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS;
+}
+
 void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
-                                                      const sp<IBinder>& newToken) {
+                                                      const sp<IBinder>& token) {
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
     uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
     if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
         return;
     }
 
-    sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
-    if (inputWindowHandle == nullptr) {
-        return;
-    }
-
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-
-    bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
-
-    if (!hasFocusChanged) {
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+    if (focusedToken == token) {
+        // ignore since token is focused
         return;
     }
 
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
-    commandEntry->newToken = newToken;
+    commandEntry->newToken = token;
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -2458,48 +2727,44 @@
     while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
         DispatchEntry* dispatchEntry = connection->outboundQueue.front();
         dispatchEntry->deliveryTime = currentTime;
+        const std::chrono::nanoseconds timeout =
+                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
+        dispatchEntry->timeoutTime = currentTime + timeout.count();
 
         // Publish the event.
         status_t status;
-        EventEntry* eventEntry = dispatchEntry->eventEntry;
-        switch (eventEntry->type) {
+        const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
+        switch (eventEntry.type) {
             case EventEntry::Type::KEY: {
-                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
+                const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+                std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
 
                 // Publish the key event.
-                status =
-                        connection->inputPublisher
-                                .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
-                                                 keyEntry->deviceId, keyEntry->source,
-                                                 keyEntry->displayId, std::move(hmac),
-                                                 dispatchEntry->resolvedAction,
-                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
-                                                 keyEntry->scanCode, keyEntry->metaState,
-                                                 keyEntry->repeatCount, keyEntry->downTime,
-                                                 keyEntry->eventTime);
+                status = connection->inputPublisher
+                                 .publishKeyEvent(dispatchEntry->seq,
+                                                  dispatchEntry->resolvedEventId, keyEntry.deviceId,
+                                                  keyEntry.source, keyEntry.displayId,
+                                                  std::move(hmac), dispatchEntry->resolvedAction,
+                                                  dispatchEntry->resolvedFlags, keyEntry.keyCode,
+                                                  keyEntry.scanCode, keyEntry.metaState,
+                                                  keyEntry.repeatCount, keyEntry.downTime,
+                                                  keyEntry.eventTime);
                 break;
             }
 
             case EventEntry::Type::MOTION: {
-                MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+                const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
 
                 PointerCoords scaledCoords[MAX_POINTERS];
-                const PointerCoords* usingCoords = motionEntry->pointerCoords;
+                const PointerCoords* usingCoords = motionEntry.pointerCoords;
 
                 // Set the X and Y offset and X and Y scale depending on the input source.
-                float xOffset = 0.0f, yOffset = 0.0f;
-                float xScale = 1.0f, yScale = 1.0f;
-                if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+                if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
                     !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                     float globalScaleFactor = dispatchEntry->globalScaleFactor;
-                    xScale = dispatchEntry->windowXScale;
-                    yScale = dispatchEntry->windowYScale;
-                    xOffset = dispatchEntry->xOffset * xScale;
-                    yOffset = dispatchEntry->yOffset * yScale;
                     if (globalScaleFactor != 1.0f) {
-                        for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
-                            scaledCoords[i] = motionEntry->pointerCoords[i];
+                        for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+                            scaledCoords[i] = motionEntry.pointerCoords[i];
                             // Don't apply window scale here since we don't want scale to affect raw
                             // coordinates. The scale will be sent back to the client and applied
                             // later when requesting relative coordinates.
@@ -2511,42 +2776,42 @@
                 } else {
                     // We don't want the dispatch target to know.
                     if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
-                        for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+                        for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
                             scaledCoords[i].clear();
                         }
                         usingCoords = scaledCoords;
                     }
                 }
 
-                std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
+                std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
 
                 // Publish the motion event.
                 status = connection->inputPublisher
                                  .publishMotionEvent(dispatchEntry->seq,
                                                      dispatchEntry->resolvedEventId,
-                                                     motionEntry->deviceId, motionEntry->source,
-                                                     motionEntry->displayId, std::move(hmac),
+                                                     motionEntry.deviceId, motionEntry.source,
+                                                     motionEntry.displayId, std::move(hmac),
                                                      dispatchEntry->resolvedAction,
-                                                     motionEntry->actionButton,
+                                                     motionEntry.actionButton,
                                                      dispatchEntry->resolvedFlags,
-                                                     motionEntry->edgeFlags, motionEntry->metaState,
-                                                     motionEntry->buttonState,
-                                                     motionEntry->classification, xScale, yScale,
-                                                     xOffset, yOffset, motionEntry->xPrecision,
-                                                     motionEntry->yPrecision,
-                                                     motionEntry->xCursorPosition,
-                                                     motionEntry->yCursorPosition,
-                                                     motionEntry->downTime, motionEntry->eventTime,
-                                                     motionEntry->pointerCount,
-                                                     motionEntry->pointerProperties, usingCoords);
-                reportTouchEventForStatistics(*motionEntry);
+                                                     motionEntry.edgeFlags, motionEntry.metaState,
+                                                     motionEntry.buttonState,
+                                                     motionEntry.classification,
+                                                     dispatchEntry->transform,
+                                                     motionEntry.xPrecision, motionEntry.yPrecision,
+                                                     motionEntry.xCursorPosition,
+                                                     motionEntry.yCursorPosition,
+                                                     motionEntry.downTime, motionEntry.eventTime,
+                                                     motionEntry.pointerCount,
+                                                     motionEntry.pointerProperties, usingCoords);
+                reportTouchEventForStatistics(motionEntry);
                 break;
             }
             case EventEntry::Type::FOCUS: {
-                FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+                const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
                 status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
-                                                                      focusEntry->id,
-                                                                      focusEntry->hasFocus,
+                                                                      focusEntry.id,
+                                                                      focusEntry.hasFocus,
                                                                       mInTouchMode);
                 break;
             }
@@ -2554,7 +2819,7 @@
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET: {
                 LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
-                                 EventEntry::typeToString(eventEntry->type));
+                                 EventEntry::typeToString(eventEntry.type));
                 return;
             }
         }
@@ -2577,7 +2842,6 @@
                           "waiting for the application to catch up",
                           connection->getInputChannelName().c_str());
 #endif
-                    connection->inputPublisherBlocked = true;
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -2594,10 +2858,30 @@
                                                     dispatchEntry));
         traceOutboundQueueLength(connection);
         connection->waitQueue.push_back(dispatchEntry);
+        if (connection->responsive) {
+            mAnrTracker.insert(dispatchEntry->timeoutTime,
+                               connection->inputChannel->getConnectionToken());
+        }
         traceWaitQueueLength(connection);
     }
 }
 
+std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) const {
+    size_t size;
+    switch (event.type) {
+        case VerifiedInputEvent::Type::KEY: {
+            size = sizeof(VerifiedKeyEvent);
+            break;
+        }
+        case VerifiedInputEvent::Type::MOTION: {
+            size = sizeof(VerifiedMotionEvent);
+            break;
+        }
+    }
+    const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+    return mHmacKeyManager.sign(start, size);
+}
+
 const std::array<uint8_t, 32> InputDispatcher::getSignature(
         const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
     int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
@@ -2607,7 +2891,7 @@
         VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
         verifiedEvent.actionMasked = actionMasked;
         verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
-        return mHmacKeyManager.sign(verifiedEvent);
+        return sign(verifiedEvent);
     }
     return INVALID_HMAC;
 }
@@ -2617,7 +2901,7 @@
     VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
     verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
     verifiedEvent.action = dispatchEntry.resolvedAction;
-    return mHmacKeyManager.sign(verifiedEvent);
+    return sign(verifiedEvent);
 }
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
@@ -2628,8 +2912,6 @@
           connection->getInputChannelName().c_str(), seq, toString(handled));
 #endif
 
-    connection->inputPublisherBlocked = false;
-
     if (connection->status == Connection::STATUS_BROKEN ||
         connection->status == Connection::STATUS_ZOMBIE) {
         return;
@@ -2675,7 +2957,7 @@
 
 void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
     if (dispatchEntry->hasForegroundTarget()) {
-        decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
+        decrementPendingForegroundDispatches(*(dispatchEntry->eventEntry));
     }
     delete dispatchEntry;
 }
@@ -2743,8 +3025,8 @@
             }
         }
 
-        // Unregister the channel.
-        d->unregisterInputChannelLocked(connection->inputChannel, notify);
+        // Remove the channel.
+        d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
         return 0; // remove the callback
     }             // release lock
 }
@@ -2774,7 +3056,7 @@
 }
 
 void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
-        const sp<InputChannel>& channel, const CancelationOptions& options) {
+        const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
     sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
     if (connection == nullptr) {
         return;
@@ -2791,7 +3073,7 @@
 
     nsecs_t currentTime = now();
 
-    std::vector<EventEntry*> cancelationEvents =
+    std::vector<std::unique_ptr<EventEntry>> cancelationEvents =
             connection->inputState.synthesizeCancelationEvents(currentTime, options);
 
     if (cancelationEvents.empty()) {
@@ -2809,15 +3091,14 @@
             getWindowHandleLocked(connection->inputChannel->getConnectionToken());
     if (windowHandle != nullptr) {
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
-        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
-                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.setDefaultPointerTransform(windowInfo->transform);
         target.globalScaleFactor = windowInfo->globalScaleFactor;
     }
     target.inputChannel = connection->inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
     for (size_t i = 0; i < cancelationEvents.size(); i++) {
-        EventEntry* cancelationEventEntry = cancelationEvents[i];
+        std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
         switch (cancelationEventEntry->type) {
             case EventEntry::Type::KEY: {
                 logOutboundKeyDetails("cancel - ",
@@ -2841,10 +3122,8 @@
             }
         }
 
-        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
-
-        cancelationEventEntry->release();
+        enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+                                   InputTarget::FLAG_DISPATCH_AS_IS);
     }
 
     startDispatchCycleLocked(currentTime, connection);
@@ -2858,7 +3137,7 @@
 
     nsecs_t currentTime = now();
 
-    std::vector<EventEntry*> downEvents =
+    std::vector<std::unique_ptr<EventEntry>> downEvents =
             connection->inputState.synthesizePointerDownEvents(currentTime);
 
     if (downEvents.empty()) {
@@ -2875,14 +3154,13 @@
             getWindowHandleLocked(connection->inputChannel->getConnectionToken());
     if (windowHandle != nullptr) {
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
-        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
-                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.setDefaultPointerTransform(windowInfo->transform);
         target.globalScaleFactor = windowInfo->globalScaleFactor;
     }
     target.inputChannel = connection->inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
-    for (EventEntry* downEventEntry : downEvents) {
+    for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
         switch (downEventEntry->type) {
             case EventEntry::Type::MOTION: {
                 logOutboundMotionDetails("down - ",
@@ -2900,17 +3178,15 @@
             }
         }
 
-        enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
-                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
-
-        downEventEntry->release();
+        enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+                                   InputTarget::FLAG_DISPATCH_AS_IS);
     }
 
     startDispatchCycleLocked(currentTime, connection);
 }
 
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
-                                               BitSet32 pointerIds) {
+std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
+        const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
     ALOG_ASSERT(pointerIds.value != 0);
 
     uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -2983,17 +3259,22 @@
                                            originalMotionEntry.id, newId);
         ATRACE_NAME(message.c_str());
     }
-    MotionEntry* splitMotionEntry =
-            new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId,
-                            originalMotionEntry.source, originalMotionEntry.displayId,
-                            originalMotionEntry.policyFlags, action,
-                            originalMotionEntry.actionButton, originalMotionEntry.flags,
-                            originalMotionEntry.metaState, originalMotionEntry.buttonState,
-                            originalMotionEntry.classification, originalMotionEntry.edgeFlags,
-                            originalMotionEntry.xPrecision, originalMotionEntry.yPrecision,
-                            originalMotionEntry.xCursorPosition,
-                            originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
-                            splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
+    std::unique_ptr<MotionEntry> splitMotionEntry =
+            std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+                                          originalMotionEntry.deviceId, originalMotionEntry.source,
+                                          originalMotionEntry.displayId,
+                                          originalMotionEntry.policyFlags, action,
+                                          originalMotionEntry.actionButton,
+                                          originalMotionEntry.flags, originalMotionEntry.metaState,
+                                          originalMotionEntry.buttonState,
+                                          originalMotionEntry.classification,
+                                          originalMotionEntry.edgeFlags,
+                                          originalMotionEntry.xPrecision,
+                                          originalMotionEntry.yPrecision,
+                                          originalMotionEntry.xCursorPosition,
+                                          originalMotionEntry.yCursorPosition,
+                                          originalMotionEntry.downTime, splitPointerCount,
+                                          splitPointerProperties, splitPointerCoords, 0, 0);
 
     if (originalMotionEntry.injectionState) {
         splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -3012,9 +3293,9 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        ConfigurationChangedEntry* newEntry =
-                new ConfigurationChangedEntry(args->id, args->eventTime);
-        needWake = enqueueInboundEventLocked(newEntry);
+        std::unique_ptr<ConfigurationChangedEntry> newEntry =
+                std::make_unique<ConfigurationChangedEntry>(args->id, args->eventTime);
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
     } // release lock
 
     if (needWake) {
@@ -3118,12 +3399,13 @@
             mLock.lock();
         }
 
-        KeyEntry* newEntry =
-                new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
-                             args->displayId, policyFlags, args->action, flags, keyCode,
-                             args->scanCode, metaState, repeatCount, args->downTime);
+        std::unique_ptr<KeyEntry> newEntry =
+                std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
+                                           args->displayId, policyFlags, args->action, flags,
+                                           keyCode, args->scanCode, metaState, repeatCount,
+                                           args->downTime);
 
-        needWake = enqueueInboundEventLocked(newEntry);
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
     } // release lock
 
@@ -3187,13 +3469,13 @@
             mLock.unlock();
 
             MotionEvent event;
+            ui::Transform transform;
             event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                              args->action, args->actionButton, args->flags, args->edgeFlags,
-                             args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
-                             1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
-                             args->yPrecision, args->xCursorPosition, args->yCursorPosition,
-                             args->downTime, args->eventTime, args->pointerCount,
-                             args->pointerProperties, args->pointerCoords);
+                             args->metaState, args->buttonState, args->classification, transform,
+                             args->xPrecision, args->yPrecision, args->xCursorPosition,
+                             args->yCursorPosition, args->downTime, args->eventTime,
+                             args->pointerCount, args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3204,16 +3486,18 @@
         }
 
         // Just enqueue a new motion event.
-        MotionEntry* newEntry =
-                new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
-                                args->displayId, policyFlags, args->action, args->actionButton,
-                                args->flags, args->metaState, args->buttonState,
-                                args->classification, args->edgeFlags, args->xPrecision,
-                                args->yPrecision, args->xCursorPosition, args->yCursorPosition,
-                                args->downTime, args->pointerCount, args->pointerProperties,
-                                args->pointerCoords, 0, 0);
+        std::unique_ptr<MotionEntry> newEntry =
+                std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
+                                              args->source, args->displayId, policyFlags,
+                                              args->action, args->actionButton, args->flags,
+                                              args->metaState, args->buttonState,
+                                              args->classification, args->edgeFlags,
+                                              args->xPrecision, args->yPrecision,
+                                              args->xCursorPosition, args->yCursorPosition,
+                                              args->downTime, args->pointerCount,
+                                              args->pointerProperties, args->pointerCoords, 0, 0);
 
-        needWake = enqueueInboundEventLocked(newEntry);
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
     } // release lock
 
@@ -3248,9 +3532,9 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        DeviceResetEntry* newEntry =
-                new DeviceResetEntry(args->id, args->eventTime, args->deviceId);
-        needWake = enqueueInboundEventLocked(newEntry);
+        std::unique_ptr<DeviceResetEntry> newEntry =
+                std::make_unique<DeviceResetEntry>(args->id, args->eventTime, args->deviceId);
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
     } // release lock
 
     if (needWake) {
@@ -3258,15 +3542,14 @@
     }
 }
 
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
-                                          int32_t injectorUid, int32_t syncMode,
-                                          std::chrono::milliseconds timeout, uint32_t policyFlags) {
+InputEventInjectionResult InputDispatcher::injectInputEvent(
+        const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+        InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
           "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
           event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
 #endif
-
     nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
     policyFlags |= POLICY_FLAG_INJECTED;
@@ -3274,13 +3557,13 @@
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
 
-    std::queue<EventEntry*> injectedEntries;
+    std::queue<std::unique_ptr<EventEntry>> injectedEntries;
     switch (event->getType()) {
         case AINPUT_EVENT_TYPE_KEY: {
             const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
             int32_t action = incomingKey.getAction();
             if (!validateKeyEvent(action)) {
-                return INPUT_EVENT_INJECTION_FAILED;
+                return InputEventInjectionResult::FAILED;
             }
 
             int32_t flags = incomingKey.getFlags();
@@ -3308,13 +3591,14 @@
             }
 
             mLock.lock();
-            KeyEntry* injectedEntry =
-                    new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(),
-                                 VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
-                                 incomingKey.getDisplayId(), policyFlags, action, flags, keyCode,
-                                 incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
-                                 incomingKey.getDownTime());
-            injectedEntries.push(injectedEntry);
+            std::unique_ptr<KeyEntry> injectedEntry =
+                    std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
+                                               VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                               incomingKey.getDisplayId(), policyFlags, action,
+                                               flags, keyCode, incomingKey.getScanCode(), metaState,
+                                               incomingKey.getRepeatCount(),
+                                               incomingKey.getDownTime());
+            injectedEntries.push(std::move(injectedEntry));
             break;
         }
 
@@ -3326,7 +3610,7 @@
             int32_t actionButton = motionEvent->getActionButton();
             int32_t displayId = motionEvent->getDisplayId();
             if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
-                return INPUT_EVENT_INJECTION_FAILED;
+                return InputEventInjectionResult::FAILED;
             }
 
             if (!(policyFlags & POLICY_FLAG_FILTERED)) {
@@ -3342,48 +3626,57 @@
             mLock.lock();
             const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
             const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-            MotionEntry* injectedEntry =
-                    new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID,
-                                    motionEvent->getSource(), motionEvent->getDisplayId(),
-                                    policyFlags, action, actionButton, motionEvent->getFlags(),
-                                    motionEvent->getMetaState(), motionEvent->getButtonState(),
-                                    motionEvent->getClassification(), motionEvent->getEdgeFlags(),
-                                    motionEvent->getXPrecision(), motionEvent->getYPrecision(),
-                                    motionEvent->getRawXCursorPosition(),
-                                    motionEvent->getRawYCursorPosition(),
-                                    motionEvent->getDownTime(), uint32_t(pointerCount),
-                                    pointerProperties, samplePointerCoords,
-                                    motionEvent->getXOffset(), motionEvent->getYOffset());
-            injectedEntries.push(injectedEntry);
+            std::unique_ptr<MotionEntry> injectedEntry =
+                    std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+                                                  VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+                                                  motionEvent->getDisplayId(), policyFlags, action,
+                                                  actionButton, motionEvent->getFlags(),
+                                                  motionEvent->getMetaState(),
+                                                  motionEvent->getButtonState(),
+                                                  motionEvent->getClassification(),
+                                                  motionEvent->getEdgeFlags(),
+                                                  motionEvent->getXPrecision(),
+                                                  motionEvent->getYPrecision(),
+                                                  motionEvent->getRawXCursorPosition(),
+                                                  motionEvent->getRawYCursorPosition(),
+                                                  motionEvent->getDownTime(),
+                                                  uint32_t(pointerCount), pointerProperties,
+                                                  samplePointerCoords, motionEvent->getXOffset(),
+                                                  motionEvent->getYOffset());
+            injectedEntries.push(std::move(injectedEntry));
             for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
                 samplePointerCoords += pointerCount;
-                MotionEntry* nextInjectedEntry =
-                        new MotionEntry(motionEvent->getId(), *sampleEventTimes,
-                                        VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
-                                        motionEvent->getDisplayId(), policyFlags, action,
-                                        actionButton, motionEvent->getFlags(),
-                                        motionEvent->getMetaState(), motionEvent->getButtonState(),
-                                        motionEvent->getClassification(),
-                                        motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                        motionEvent->getYPrecision(),
-                                        motionEvent->getRawXCursorPosition(),
-                                        motionEvent->getRawYCursorPosition(),
-                                        motionEvent->getDownTime(), uint32_t(pointerCount),
-                                        pointerProperties, samplePointerCoords,
-                                        motionEvent->getXOffset(), motionEvent->getYOffset());
-                injectedEntries.push(nextInjectedEntry);
+                std::unique_ptr<MotionEntry> nextInjectedEntry =
+                        std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+                                                      VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+                                                      motionEvent->getDisplayId(), policyFlags,
+                                                      action, actionButton, motionEvent->getFlags(),
+                                                      motionEvent->getMetaState(),
+                                                      motionEvent->getButtonState(),
+                                                      motionEvent->getClassification(),
+                                                      motionEvent->getEdgeFlags(),
+                                                      motionEvent->getXPrecision(),
+                                                      motionEvent->getYPrecision(),
+                                                      motionEvent->getRawXCursorPosition(),
+                                                      motionEvent->getRawYCursorPosition(),
+                                                      motionEvent->getDownTime(),
+                                                      uint32_t(pointerCount), pointerProperties,
+                                                      samplePointerCoords,
+                                                      motionEvent->getXOffset(),
+                                                      motionEvent->getYOffset());
+                injectedEntries.push(std::move(nextInjectedEntry));
             }
             break;
         }
 
         default:
             ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
-            return INPUT_EVENT_INJECTION_FAILED;
+            return InputEventInjectionResult::FAILED;
     }
 
     InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
-    if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+    if (syncMode == InputEventInjectionSync::NONE) {
         injectionState->injectionIsAsync = true;
     }
 
@@ -3392,7 +3685,7 @@
 
     bool needWake = false;
     while (!injectedEntries.empty()) {
-        needWake |= enqueueInboundEventLocked(injectedEntries.front());
+        needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
         injectedEntries.pop();
     }
 
@@ -3402,16 +3695,16 @@
         mLooper->wake();
     }
 
-    int32_t injectionResult;
+    InputEventInjectionResult injectionResult;
     { // acquire lock
         std::unique_lock _l(mLock);
 
-        if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
-            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+        if (syncMode == InputEventInjectionSync::NONE) {
+            injectionResult = InputEventInjectionResult::SUCCEEDED;
         } else {
             for (;;) {
                 injectionResult = injectionState->injectionResult;
-                if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+                if (injectionResult != InputEventInjectionResult::PENDING) {
                     break;
                 }
 
@@ -3421,15 +3714,15 @@
                     ALOGD("injectInputEvent - Timed out waiting for injection result "
                           "to become available.");
 #endif
-                    injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                    injectionResult = InputEventInjectionResult::TIMED_OUT;
                     break;
                 }
 
                 mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
             }
 
-            if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED &&
-                syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+            if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
+                syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
                 while (injectionState->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
                     ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
@@ -3441,7 +3734,7 @@
                         ALOGD("injectInputEvent - Timed out waiting for pending foreground "
                               "dispatches to finish.");
 #endif
-                        injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                        injectionResult = InputEventInjectionResult::TIMED_OUT;
                         break;
                     }
 
@@ -3469,7 +3762,7 @@
             const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
             VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
             result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
-            calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
+            calculatedHmac = sign(verifiedKeyEvent);
             break;
         }
         case AINPUT_EVENT_TYPE_MOTION: {
@@ -3477,7 +3770,7 @@
             VerifiedMotionEvent verifiedMotionEvent =
                     verifiedMotionEventFromMotionEvent(motionEvent);
             result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
-            calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+            calculatedHmac = sign(verifiedMotionEvent);
             break;
         }
         default: {
@@ -3499,8 +3792,9 @@
             mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
 }
 
-void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
-    InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::setInjectionResult(EventEntry& entry,
+                                         InputEventInjectionResult injectionResult) {
+    InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
 #if DEBUG_INJECTION
         ALOGD("Setting input event injection result to %d.  "
@@ -3508,21 +3802,24 @@
               injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
+        if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
-                case INPUT_EVENT_INJECTION_SUCCEEDED:
+                case InputEventInjectionResult::SUCCEEDED:
                     ALOGV("Asynchronous input event injection succeeded.");
                     break;
-                case INPUT_EVENT_INJECTION_FAILED:
+                case InputEventInjectionResult::FAILED:
                     ALOGW("Asynchronous input event injection failed.");
                     break;
-                case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+                case InputEventInjectionResult::PERMISSION_DENIED:
                     ALOGW("Asynchronous input event injection permission denied.");
                     break;
-                case INPUT_EVENT_INJECTION_TIMED_OUT:
+                case InputEventInjectionResult::TIMED_OUT:
                     ALOGW("Asynchronous input event injection timed out.");
                     break;
+                case InputEventInjectionResult::PENDING:
+                    ALOGE("Setting result to 'PENDING' for asynchronous injection");
+                    break;
             }
         }
 
@@ -3531,15 +3828,15 @@
     }
 }
 
-void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) {
-    InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
+    InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
         injectionState->pendingForegroundDispatches += 1;
     }
 }
 
-void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
-    InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
+    InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
         injectionState->pendingForegroundDispatches -= 1;
 
@@ -3549,15 +3846,21 @@
     }
 }
 
-std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
+const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked(
         int32_t displayId) const {
-    return getValueByKey(mWindowHandlesByDisplay, displayId);
+    static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES;
+    auto it = mWindowHandlesByDisplay.find(displayId);
+    return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
 }
 
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
         const sp<IBinder>& windowHandleToken) const {
+    if (windowHandleToken == nullptr) {
+        return nullptr;
+    }
+
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
             if (windowHandle->getToken() == windowHandleToken) {
                 return windowHandle;
@@ -3567,11 +3870,31 @@
     return nullptr;
 }
 
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                             int displayId) const {
+    if (windowHandleToken == nullptr) {
+        return nullptr;
+    }
+
+    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+        if (windowHandle->getToken() == windowHandleToken) {
+            return windowHandle;
+        }
+    }
+    return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    return getWindowHandleLocked(focusedToken, displayId);
+}
+
 bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& handle : windowHandles) {
-            if (handle->getToken() == windowHandle->getToken()) {
+            if (handle->getId() == windowHandle->getId() &&
+                handle->getToken() == windowHandle->getToken()) {
                 if (windowHandle->getInfo()->displayId != it.first) {
                     ALOGE("Found window %s in display %" PRId32
                           ", but it should belong to display %" PRId32,
@@ -3585,7 +3908,31 @@
     return false;
 }
 
-sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
+    sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
+    const bool noInputChannel =
+            windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+    if (connection != nullptr && noInputChannel) {
+        ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
+              windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
+        return false;
+    }
+
+    if (connection == nullptr) {
+        if (!noInputChannel) {
+            ALOGI("Could not find connection for %s", windowHandle.getName().c_str());
+        }
+        return false;
+    }
+    if (!connection->responsive) {
+        ALOGW("Window %s is not responsive", windowHandle.getName().c_str());
+        return false;
+    }
+    return true;
+}
+
+std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
+        const sp<IBinder>& token) const {
     size_t count = mInputChannelsByToken.count(token);
     if (count == 0) {
         return nullptr;
@@ -3620,10 +3967,9 @@
         if ((getInputChannelLocked(handle->getToken()) == nullptr &&
              info->portalToDisplayId == ADISPLAY_ID_NONE)) {
             const bool noInputChannel =
-                    info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
-            const bool canReceiveInput =
-                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
-                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+                    info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+            const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) ||
+                    !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE);
             if (canReceiveInput && !noInputChannel) {
                 ALOGV("Window handle %s has no registered input channel",
                       handle->getName().c_str());
@@ -3637,7 +3983,8 @@
             continue;
         }
 
-        if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+        if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
+                (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
             const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
             oldHandle->updateFrom(handle);
             newHandles.push_back(oldHandle);
@@ -3679,58 +4026,46 @@
         ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
     }
 
+    // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+    for (const sp<InputWindowHandle>& window : inputWindowHandles) {
+        const bool noInputWindow =
+                window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+        if (noInputWindow && window->getToken() != nullptr) {
+            ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
+                  window->getName().c_str());
+            window->releaseChannel();
+        }
+    }
+
     // Copy old handles for release if they are no longer present.
     const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
 
     updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
 
-    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
-    bool foundHoveredWindow = false;
-    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
-        // Set newFocusedWindowHandle to the top most focused window instead of the last one
-        if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
-            windowHandle->getInfo()->visible) {
-            newFocusedWindowHandle = windowHandle;
-        }
-        if (windowHandle == mLastHoverWindowHandle) {
-            foundHoveredWindow = true;
-        }
-    }
-
-    if (!foundHoveredWindow) {
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    if (mLastHoverWindowHandle &&
+        std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+                windowHandles.end()) {
         mLastHoverWindowHandle = nullptr;
     }
 
-    sp<InputWindowHandle> oldFocusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-
-    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
-        if (oldFocusedWindowHandle != nullptr) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus left window: %s in display %" PRId32,
-                      oldFocusedWindowHandle->getName().c_str(), displayId);
-            }
-            sp<InputChannel> focusedInputChannel =
-                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
-            if (focusedInputChannel != nullptr) {
-                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
-                                           "focus left window");
-                synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
-                enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
-            }
-            mFocusedWindowHandlesByDisplay.erase(displayId);
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    if (focusedToken) {
+        FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
+        if (result != FocusResult::OK) {
+            onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
         }
-        if (newFocusedWindowHandle != nullptr) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus entered window: %s in display %" PRId32,
-                      newFocusedWindowHandle->getName().c_str(), displayId);
-            }
-            mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
-            enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
-        }
+    }
 
-        if (mFocusedDisplayId == displayId) {
-            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+    std::optional<FocusRequest> focusRequest =
+            getOptionalValueByKey(mPendingFocusRequests, displayId);
+    if (focusRequest) {
+        // If the window from the pending request is now visible, provide it focus.
+        FocusResult result = handleFocusRequestLocked(*focusRequest);
+        if (result != FocusResult::NOT_VISIBLE) {
+            // Drop the request if we were able to change the focus or we cannot change
+            // it for another reason.
+            mPendingFocusRequests.erase(displayId);
         }
     }
 
@@ -3745,7 +4080,7 @@
                     ALOGD("Touched window was removed: %s in display %" PRId32,
                           touchedWindow.windowHandle->getName().c_str(), displayId);
                 }
-                sp<InputChannel> touchedInputChannel =
+                std::shared_ptr<InputChannel> touchedInputChannel =
                         getInputChannelLocked(touchedWindow.windowHandle->getToken());
                 if (touchedInputChannel != nullptr) {
                     CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -3774,7 +4109,7 @@
 }
 
 void InputDispatcher::setFocusedApplication(
-        int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
+        int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
     if (DEBUG_FOCUS) {
         ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
               inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
@@ -3782,20 +4117,23 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<InputApplicationHandle> oldFocusedApplicationHandle =
+        std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
                 getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
-        if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
-            if (oldFocusedApplicationHandle != inputApplicationHandle) {
-                if (oldFocusedApplicationHandle != nullptr) {
-                    resetAnrTimeoutsLocked();
-                }
-                mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
-            }
-        } else if (oldFocusedApplicationHandle != nullptr) {
-            resetAnrTimeoutsLocked();
-            oldFocusedApplicationHandle.clear();
+
+        if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
+            return; // This application is already focused. No need to wake up or change anything.
+        }
+
+        // Set the new application handle.
+        if (inputApplicationHandle != nullptr) {
+            mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+        } else {
             mFocusedApplicationHandlesByDisplay.erase(displayId);
         }
+
+        // No matter what the old focused application was, stop waiting on it because it is
+        // no longer focused.
+        resetNoFocusedWindowTimeoutLocked();
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3819,11 +4157,11 @@
         std::scoped_lock _l(mLock);
 
         if (mFocusedDisplayId != displayId) {
-            sp<InputWindowHandle> oldFocusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-            if (oldFocusedWindowHandle != nullptr) {
-                sp<InputChannel> inputChannel =
-                        getInputChannelLocked(oldFocusedWindowHandle->getToken());
+            sp<IBinder> oldFocusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+            if (oldFocusedWindowToken != nullptr) {
+                std::shared_ptr<InputChannel> inputChannel =
+                        getInputChannelLocked(oldFocusedWindowToken);
                 if (inputChannel != nullptr) {
                     CancelationOptions
                             options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
@@ -3834,21 +4172,16 @@
             }
             mFocusedDisplayId = displayId;
 
-            // Sanity check
-            sp<InputWindowHandle> newFocusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+            // Find new focused window and validate
+            sp<IBinder> newFocusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+            notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
-            if (newFocusedWindowHandle == nullptr) {
+            if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
-                if (!mFocusedWindowHandlesByDisplay.empty()) {
-                    ALOGE("But another display has a focused window:");
-                    for (auto& it : mFocusedWindowHandlesByDisplay) {
-                        const int32_t displayId = it.first;
-                        const sp<InputWindowHandle>& windowHandle = it.second;
-                        ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
-                              windowHandle->getName().c_str());
-                    }
+                if (!mFocusedWindowTokenByDisplay.empty()) {
+                    ALOGE("But another display has a focused window\n%s",
+                          dumpFocusedWindowsLocked().c_str());
                 }
             }
         }
@@ -3873,7 +4206,7 @@
 
         if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
             if (mDispatchFrozen && !frozen) {
-                resetAnrTimeoutsLocked();
+                resetNoFocusedWindowTimeoutLocked();
             }
 
             if (mDispatchEnabled && !enabled) {
@@ -3923,6 +4256,21 @@
     mInTouchMode = inTouchMode;
 }
 
+void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
+    if (opacity < 0 || opacity > 1) {
+        LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+        return;
+    }
+
+    std::scoped_lock lock(mLock);
+    mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) {
+    std::scoped_lock lock(mLock);
+    mBlockUntrustedTouchesMode = mode;
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
     if (fromToken == toToken) {
         if (DEBUG_FOCUS) {
@@ -4013,8 +4361,9 @@
     resetKeyRepeatLocked();
     releasePendingEventLocked();
     drainInboundQueueLocked();
-    resetAnrTimeoutsLocked();
+    resetNoFocusedWindowTimeoutLocked();
 
+    mAnrTracker.clear();
     mTouchStatesByDisplay.clear();
     mLastHoverWindowHandle.clear();
     mReplacedKeys.clear();
@@ -4032,6 +4381,46 @@
     }
 }
 
+std::string InputDispatcher::dumpFocusedWindowsLocked() {
+    if (mFocusedWindowTokenByDisplay.empty()) {
+        return INDENT "FocusedWindows: <none>\n";
+    }
+
+    std::string dump;
+    dump += INDENT "FocusedWindows:\n";
+    for (auto& it : mFocusedWindowTokenByDisplay) {
+        const int32_t displayId = it.first;
+        const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
+        if (windowHandle) {
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+                                 windowHandle->getName().c_str());
+        } else {
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32
+                                         " has focused token without a window'\n",
+                                 displayId);
+        }
+    }
+    return dump;
+}
+
+std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
+    if (mPendingFocusRequests.empty()) {
+        return INDENT "mPendingFocusRequests: <none>\n";
+    }
+
+    std::string dump;
+    dump += INDENT "mPendingFocusRequests:\n";
+    for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
+        // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
+        // try to resolve them to actual windows.
+        std::string windowName = getConnectionNameLocked(focusRequest.token);
+        std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
+        dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
+                             displayId, windowName.c_str(), focusedWindowName.c_str());
+    }
+    return dump;
+}
+
 void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
     dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
     dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4042,30 +4431,19 @@
         dump += StringPrintf(INDENT "FocusedApplications:\n");
         for (auto& it : mFocusedApplicationHandlesByDisplay) {
             const int32_t displayId = it.first;
-            const sp<InputApplicationHandle>& applicationHandle = it.second;
+            const std::shared_ptr<InputApplicationHandle>& applicationHandle = it.second;
+            const std::chrono::duration timeout =
+                    applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             dump += StringPrintf(INDENT2 "displayId=%" PRId32
                                          ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
-                                 displayId, applicationHandle->getName().c_str(),
-                                 ns2ms(applicationHandle
-                                               ->getDispatchingTimeout(
-                                                       DEFAULT_INPUT_DISPATCHING_TIMEOUT)
-                                               .count()));
+                                 displayId, applicationHandle->getName().c_str(), millis(timeout));
         }
     } else {
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
     }
 
-    if (!mFocusedWindowHandlesByDisplay.empty()) {
-        dump += StringPrintf(INDENT "FocusedWindows:\n");
-        for (auto& it : mFocusedWindowHandlesByDisplay) {
-            const int32_t displayId = it.first;
-            const sp<InputWindowHandle>& windowHandle = it.second;
-            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
-                                 windowHandle->getName().c_str());
-        }
-    } else {
-        dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
-    }
+    dump += dumpFocusedWindowsLocked();
+    dump += dumpPendingFocusRequestsLocked();
 
     if (!mTouchStatesByDisplay.empty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4109,33 +4487,35 @@
                     const sp<InputWindowHandle>& windowHandle = windowHandles[i];
                     const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
-                    dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
-                                                 "portalToDisplayId=%d, paused=%s, hasFocus=%s, "
-                                                 "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
-                                                 "flags=0x%08x, type=0x%08x, "
+                    dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
+                                                 "portalToDisplayId=%d, paused=%s, focusable=%s, "
+                                                 "hasWallpaper=%s, visible=%s, alpha=%.2f, "
+                                                 "flags=%s, type=0x%08x, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
-                                                 "windowScale=(%f,%f), touchableRegion=",
-                                         i, windowInfo->name.c_str(), windowInfo->displayId,
-                                         windowInfo->portalToDisplayId,
+                                                 "applicationInfo=%s, "
+                                                 "touchableRegion=",
+                                         i, windowInfo->name.c_str(), windowInfo->id,
+                                         windowInfo->displayId, windowInfo->portalToDisplayId,
                                          toString(windowInfo->paused),
-                                         toString(windowInfo->hasFocus),
+                                         toString(windowInfo->focusable),
                                          toString(windowInfo->hasWallpaper),
-                                         toString(windowInfo->visible),
-                                         toString(windowInfo->canReceiveKeys),
-                                         windowInfo->layoutParamsFlags,
-                                         windowInfo->layoutParamsType, windowInfo->frameLeft,
-                                         windowInfo->frameTop, windowInfo->frameRight,
-                                         windowInfo->frameBottom, windowInfo->globalScaleFactor,
-                                         windowInfo->windowXScale, windowInfo->windowYScale);
-                    dumpRegion(dump, windowInfo->touchableRegion);
-                    dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+                                         toString(windowInfo->visible), windowInfo->alpha,
+                                         windowInfo->flags.string().c_str(),
+                                         static_cast<int32_t>(windowInfo->type),
+                                         windowInfo->frameLeft, windowInfo->frameTop,
+                                         windowInfo->frameRight, windowInfo->frameBottom,
+                                         windowInfo->globalScaleFactor,
+                                         windowInfo->applicationInfo.name.c_str());
+                    dump += dumpRegion(windowInfo->touchableRegion);
+                    dump += StringPrintf(", inputFeatures=%s",
+                                         windowInfo->inputFeatures.string().c_str());
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
-                                         "ms\n",
+                                         "ms, trustedOverlay=%s, hasToken=%s\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
-                                         ns2ms(windowInfo->dispatchingTimeout));
-                    dump += StringPrintf(INDENT4 "    flags: %s\n",
-                                         inputWindowFlagsToString(windowInfo->layoutParamsFlags)
-                                                 .c_str());
+                                         millis(windowInfo->dispatchingTimeout),
+                                         toString(windowInfo->trustedOverlay),
+                                         toString(windowInfo->token != nullptr));
+                    windowInfo->transform.dump(dump, "transform", INDENT4);
                 }
             } else {
                 dump += INDENT2 "Windows: <none>\n";
@@ -4165,9 +4545,9 @@
     // Dump recently dispatched or dropped events from oldest to newest.
     if (!mRecentQueue.empty()) {
         dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
-        for (EventEntry* entry : mRecentQueue) {
+        for (std::shared_ptr<EventEntry>& entry : mRecentQueue) {
             dump += INDENT2;
-            entry->appendDescription(dump);
+            dump += entry->getDescription();
             dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
@@ -4178,7 +4558,7 @@
     if (mPendingEvent) {
         dump += INDENT "PendingEvent:\n";
         dump += INDENT2;
-        mPendingEvent->appendDescription(dump);
+        dump += mPendingEvent->getDescription();
         dump += StringPrintf(", age=%" PRId64 "ms\n",
                              ns2ms(currentTime - mPendingEvent->eventTime));
     } else {
@@ -4188,9 +4568,9 @@
     // Dump inbound events from oldest to newest.
     if (!mInboundQueue.empty()) {
         dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
-        for (EventEntry* entry : mInboundQueue) {
+        for (std::shared_ptr<EventEntry>& entry : mInboundQueue) {
             dump += INDENT2;
-            entry->appendDescription(dump);
+            dump += entry->getDescription();
             dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
@@ -4214,23 +4594,16 @@
         for (const auto& pair : mConnectionsByFd) {
             const sp<Connection>& connection = pair.second;
             dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
-                                         "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
+                                         "status=%s, monitor=%s, responsive=%s\n",
                                  pair.first, connection->getInputChannelName().c_str(),
                                  connection->getWindowName().c_str(), connection->getStatusLabel(),
-                                 toString(connection->monitor),
-                                 toString(connection->inputPublisherBlocked));
+                                 toString(connection->monitor), toString(connection->responsive));
 
             if (!connection->outboundQueue.empty()) {
                 dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
                                      connection->outboundQueue.size());
-                for (DispatchEntry* entry : connection->outboundQueue) {
-                    dump.append(INDENT4);
-                    entry->eventEntry->appendDescription(dump);
-                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
-                                         "ms\n",
-                                         entry->targetFlags, entry->resolvedAction,
-                                         ns2ms(currentTime - entry->eventEntry->eventTime));
-                }
+                dump += dumpQueue(connection->outboundQueue, currentTime);
+
             } else {
                 dump += INDENT3 "OutboundQueue: <empty>\n";
             }
@@ -4238,15 +4611,7 @@
             if (!connection->waitQueue.empty()) {
                 dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
                                      connection->waitQueue.size());
-                for (DispatchEntry* entry : connection->waitQueue) {
-                    dump += INDENT4;
-                    entry->eventEntry->appendDescription(dump);
-                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
-                                         "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
-                                         entry->targetFlags, entry->resolvedAction,
-                                         ns2ms(currentTime - entry->eventEntry->eventTime),
-                                         ns2ms(currentTime - entry->deliveryTime));
-                }
+                dump += dumpQueue(connection->waitQueue, currentTime);
             } else {
                 dump += INDENT3 "WaitQueue: <empty>\n";
             }
@@ -4272,81 +4637,82 @@
     const size_t numMonitors = monitors.size();
     for (size_t i = 0; i < numMonitors; i++) {
         const Monitor& monitor = monitors[i];
-        const sp<InputChannel>& channel = monitor.inputChannel;
+        const std::shared_ptr<InputChannel>& channel = monitor.inputChannel;
         dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
         dump += "\n";
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
+        const std::string& name) {
+#if DEBUG_CHANNEL_CREATION
+    ALOGD("channel '%s' ~ createInputChannel", name.c_str());
 #endif
 
+    std::shared_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+
+    if (result) {
+        return base::Error(result) << "Failed to open input channel pair with name " << name;
+    }
+
     { // acquire lock
         std::scoped_lock _l(mLock);
-        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
-        if (existingConnection != nullptr) {
-            ALOGW("Attempted to register already registered input channel '%s'",
-                  inputChannel->getName().c_str());
-            return BAD_VALUE;
-        }
+        sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
 
-        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
-
-        int fd = inputChannel->getFd();
+        int fd = serverChannel->getFd();
         mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     } // release lock
 
     // Wake the looper because some connections have changed.
     mLooper->wake();
-    return OK;
+    return clientChannel;
 }
 
-status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
-                                               int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
+        int32_t displayId, bool isGestureMonitor, const std::string& name) {
+    std::shared_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+    if (result) {
+        return base::Error(result) << "Failed to open input channel pair with name " << name;
+    }
+
     { // acquire lock
         std::scoped_lock _l(mLock);
 
         if (displayId < 0) {
-            ALOGW("Attempted to register input monitor without a specified display.");
-            return BAD_VALUE;
+            return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
+                                          << " without a specified display.";
         }
 
-        if (inputChannel->getConnectionToken() == nullptr) {
-            ALOGW("Attempted to register input monitor without an identifying token.");
-            return BAD_VALUE;
-        }
+        sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
 
-        sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
-
-        const int fd = inputChannel->getFd();
+        const int fd = serverChannel->getFd();
         mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
-        monitorsByDisplay[displayId].emplace_back(inputChannel);
+        monitorsByDisplay[displayId].emplace_back(serverChannel);
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     }
+
     // Wake the looper because some connections have changed.
     mLooper->wake();
-    return OK;
+    return clientChannel;
 }
 
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
-#endif
-
+status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+        status_t status = removeInputChannelLocked(connectionToken, false /*notify*/);
         if (status) {
             return status;
         }
@@ -4358,23 +4724,22 @@
     return OK;
 }
 
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
-                                                       bool notify) {
-    sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+                                                   bool notify) {
+    sp<Connection> connection = getConnectionLocked(connectionToken);
     if (connection == nullptr) {
-        ALOGW("Attempted to unregister already unregistered input channel '%s'",
-              inputChannel->getName().c_str());
+        ALOGW("Attempted to unregister already unregistered input channel");
         return BAD_VALUE;
     }
 
     removeConnectionLocked(connection);
-    mInputChannelsByToken.erase(inputChannel->getConnectionToken());
+    mInputChannelsByToken.erase(connectionToken);
 
     if (connection->monitor) {
-        removeMonitorChannelLocked(inputChannel);
+        removeMonitorChannelLocked(connectionToken);
     }
 
-    mLooper->removeFd(inputChannel->getFd());
+    mLooper->removeFd(connection->inputChannel->getFd());
 
     nsecs_t currentTime = now();
     abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -4383,19 +4748,19 @@
     return OK;
 }
 
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
-    removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
-    removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
+void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) {
+    removeMonitorChannelLocked(connectionToken, mGlobalMonitorsByDisplay);
+    removeMonitorChannelLocked(connectionToken, mGestureMonitorsByDisplay);
 }
 
 void InputDispatcher::removeMonitorChannelLocked(
-        const sp<InputChannel>& inputChannel,
+        const sp<IBinder>& connectionToken,
         std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
     for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
         std::vector<Monitor>& monitors = it->second;
         const size_t numMonitors = monitors.size();
         for (size_t i = 0; i < numMonitors; i++) {
-            if (monitors[i].inputChannel == inputChannel) {
+            if (monitors[i].inputChannel->getConnectionToken() == connectionToken) {
                 monitors.erase(monitors.begin() + i);
                 break;
             }
@@ -4446,7 +4811,8 @@
         options.deviceId = deviceId;
         options.displayId = displayId;
         for (const TouchedWindow& window : state.windows) {
-            sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+            std::shared_ptr<InputChannel> channel =
+                    getInputChannelLocked(window.windowHandle->getToken());
             if (channel != nullptr) {
                 synthesizeCancelationEventsForInputChannelLocked(channel, options);
             }
@@ -4485,7 +4851,16 @@
     return nullptr;
 }
 
+std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
+    sp<Connection> connection = getConnectionLocked(connectionToken);
+    if (connection == nullptr) {
+        return "<nullptr>";
+    }
+    return connection->getInputChannelName();
+}
+
 void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
     removeByValue(mConnectionsByFd, connection);
 }
 
@@ -4512,10 +4887,8 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-                                           const sp<InputWindowHandle>& newFocus) {
-    sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
-    sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
+                                               const sp<IBinder>& newToken) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
     commandEntry->oldToken = oldToken;
@@ -4523,17 +4896,74 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onAnrLocked(nsecs_t currentTime,
-                                  const sp<InputApplicationHandle>& applicationHandle,
-                                  const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
-                                  nsecs_t waitStartTime, const char* reason) {
-    float dispatchLatency = (currentTime - eventTime) * 0.000001f;
-    float waitDuration = (currentTime - waitStartTime) * 0.000001f;
-    ALOGI("Application is not responding: %s.  "
-          "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
-          getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
-          waitDuration, reason);
+void InputDispatcher::onAnrLocked(const Connection& connection) {
+    // Since we are allowing the policy to extend the timeout, maybe the waitQueue
+    // is already healthy again. Don't raise ANR in this situation
+    if (connection.waitQueue.empty()) {
+        ALOGI("Not raising ANR because the connection %s has recovered",
+              connection.inputChannel->getName().c_str());
+        return;
+    }
+    /**
+     * The "oldestEntry" is the entry that was first sent to the application. That entry, however,
+     * may not be the one that caused the timeout to occur. One possibility is that window timeout
+     * has changed. This could cause newer entries to time out before the already dispatched
+     * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
+     * processes the events linearly. So providing information about the oldest entry seems to be
+     * most useful.
+     */
+    DispatchEntry* oldestEntry = *connection.waitQueue.begin();
+    const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+    std::string reason =
+            android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
+                                        connection.inputChannel->getName().c_str(),
+                                        ns2ms(currentWait),
+                                        oldestEntry->eventEntry->getDescription().c_str());
+    sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
+    updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
 
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = nullptr;
+    commandEntry->connectionToken = connectionToken;
+    commandEntry->reason = std::move(reason);
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) {
+    std::string reason = android::base::StringPrintf("%s does not have a focused window",
+                                                     application->getName().c_str());
+
+    updateLastAnrStateLocked(application, reason);
+
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = application;
+    commandEntry->reason = std::move(reason);
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
+    commandEntry->obscuringPackage = obscuringPackage;
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+                                               const std::string& reason) {
+    const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
+    updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(
+        const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) {
+    const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+    updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,
+                                               const std::string& reason) {
     // Capture a record of the InputDispatcher state at the time of the ANR.
     time_t t = time(nullptr);
     struct tm tm;
@@ -4543,21 +4973,9 @@
     mLastAnrState.clear();
     mLastAnrState += INDENT "ANR:\n";
     mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
-    mLastAnrState +=
-            StringPrintf(INDENT2 "Window: %s\n",
-                         getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
-    mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
-    mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
-    mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason);
+    mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());
+    mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());
     dumpDispatchStateLocked(mLastAnrState);
-
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = applicationHandle;
-    commandEntry->inputChannel =
-            windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
-    commandEntry->reason = reason;
-    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4589,31 +5007,72 @@
 }
 
 void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> token =
-            commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
     mLock.unlock();
-
-    const nsecs_t timeoutExtension =
+    const sp<IBinder>& token = commandEntry->connectionToken;
+    const std::chrono::nanoseconds timeoutExtension =
             mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token);
+    if (timeoutExtension > 0s) {
+        extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
+    } else {
+        // stop waking up for events in this connection, it is already not responding
+        sp<Connection> connection = getConnectionLocked(token);
+        if (connection == nullptr) {
+            return;
+        }
+        cancelEventsForAnrLocked(connection);
+    }
+}
+
+void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
+
+    mLock.lock();
+}
+
+void InputDispatcher::extendAnrTimeoutsLocked(
+        const std::shared_ptr<InputApplicationHandle>& application,
+        const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
+    if (connectionToken == nullptr && application != nullptr) {
+        // The ANR happened because there's no focused window
+        mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
+        mAwaitedFocusedApplication = application;
+    }
+
+    sp<Connection> connection = getConnectionLocked(connectionToken);
+    if (connection == nullptr) {
+        // It's possible that the connection already disappeared. No action necessary.
+        return;
+    }
+
+    ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
+          connection->inputChannel->getName().c_str(), millis(timeoutExtension));
+
+    connection->responsive = true;
+    const nsecs_t newTimeout = now() + timeoutExtension.count();
+    for (DispatchEntry* entry : connection->waitQueue) {
+        if (newTimeout >= entry->timeoutTime) {
+            // Already removed old entries when connection was marked unresponsive
+            entry->timeoutTime = newTimeout;
+            mAnrTracker.insert(entry->timeoutTime, connectionToken);
+        }
+    }
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
         CommandEntry* commandEntry) {
-    KeyEntry* entry = commandEntry->keyEntry;
-
-    KeyEvent event = createKeyEvent(*entry);
+    KeyEntry& entry = *(commandEntry->keyEntry);
+    KeyEvent event = createKeyEvent(entry);
 
     mLock.unlock();
 
     android::base::Timer t;
-    sp<IBinder> token = commandEntry->inputChannel != nullptr
-            ? commandEntry->inputChannel->getConnectionToken()
-            : nullptr;
-    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
+    const sp<IBinder>& token = commandEntry->connectionToken;
+    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
               std::to_string(t.duration().count()).c_str());
@@ -4622,14 +5081,13 @@
     mLock.lock();
 
     if (delay < 0) {
-        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
     } else if (!delay) {
-        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
     } else {
-        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
-        entry->interceptKeyWakeupTime = now() + delay;
+        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+        entry.interceptKeyWakeupTime = now() + delay;
     }
-    entry->release();
 }
 
 void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
@@ -4638,6 +5096,20 @@
     mLock.lock();
 }
 
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+static bool isConnectionResponsive(const Connection& connection) {
+    const nsecs_t currentTime = now();
+    for (const DispatchEntry* entry : connection.waitQueue) {
+        if (entry->timeoutTime < currentTime) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
     const nsecs_t finishTime = commandEntry->eventTime;
@@ -4650,7 +5122,6 @@
         return;
     }
     DispatchEntry* dispatchEntry = *dispatchEntryIt;
-
     const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
     if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
         ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
@@ -4660,11 +5131,11 @@
 
     bool restartEvent;
     if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
         restartEvent =
                 afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
     } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
         restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
                                                            handled);
     } else {
@@ -4679,6 +5150,11 @@
     if (dispatchEntryIt != connection->waitQueue.end()) {
         dispatchEntry = *dispatchEntryIt;
         connection->waitQueue.erase(dispatchEntryIt);
+        mAnrTracker.erase(dispatchEntry->timeoutTime,
+                          connection->inputChannel->getConnectionToken());
+        if (!connection->responsive) {
+            connection->responsive = isConnectionResponsive(*connection);
+        }
         traceWaitQueueLength(connection);
         if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
             connection->outboundQueue.push_front(dispatchEntry);
@@ -4694,20 +5170,20 @@
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                                        DispatchEntry* dispatchEntry,
-                                                       KeyEntry* keyEntry, bool handled) {
-    if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+                                                       KeyEntry& keyEntry, bool handled) {
+    if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
-            mReporter->reportUnhandledKey(keyEntry->id);
+            mReporter->reportUnhandledKey(keyEntry.id);
         }
         return false;
     }
 
     // Get the fallback key state.
     // Clear it out after dispatching the UP.
-    int32_t originalKeyCode = keyEntry->keyCode;
+    int32_t originalKeyCode = keyEntry.keyCode;
     int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
-    if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+    if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
         connection->inputState.removeFallbackKey(originalKeyCode);
     }
 
@@ -4720,16 +5196,15 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
                   "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
-                  keyEntry->policyFlags);
+                  keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
 #endif
-            KeyEvent event = createKeyEvent(*keyEntry);
+            KeyEvent event = createKeyEvent(keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
             mLock.unlock();
 
             mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
-                                          keyEntry->policyFlags, &event);
+                                          keyEntry.policyFlags, &event);
 
             mLock.lock();
 
@@ -4748,13 +5223,13 @@
         // If the application did not handle a non-fallback key, first check
         // that we are in a good state to perform unhandled key event processing
         // Then ask the policy what to do with it.
-        bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
+        bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
         if (fallbackKeyCode == -1 && !initialDown) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Skipping unhandled key event processing "
                   "since this is not an initial down.  "
                   "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+                  originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
 #endif
             return false;
         }
@@ -4763,15 +5238,15 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
               "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-              keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+              keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
 #endif
-        KeyEvent event = createKeyEvent(*keyEntry);
+        KeyEvent event = createKeyEvent(keyEntry);
 
         mLock.unlock();
 
         bool fallback =
                 mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
-                                              &event, keyEntry->policyFlags, &event);
+                                              &event, keyEntry.policyFlags, &event);
 
         mLock.lock();
 
@@ -4819,7 +5294,7 @@
 
             fallback = false;
             fallbackKeyCode = AKEYCODE_UNKNOWN;
-            if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+            if (keyEntry.action != AKEY_EVENT_ACTION_UP) {
                 connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
             }
         }
@@ -4839,22 +5314,22 @@
 
         if (fallback) {
             // Restart the dispatch cycle using the fallback key.
-            keyEntry->eventTime = event.getEventTime();
-            keyEntry->deviceId = event.getDeviceId();
-            keyEntry->source = event.getSource();
-            keyEntry->displayId = event.getDisplayId();
-            keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
-            keyEntry->keyCode = fallbackKeyCode;
-            keyEntry->scanCode = event.getScanCode();
-            keyEntry->metaState = event.getMetaState();
-            keyEntry->repeatCount = event.getRepeatCount();
-            keyEntry->downTime = event.getDownTime();
-            keyEntry->syntheticRepeat = false;
+            keyEntry.eventTime = event.getEventTime();
+            keyEntry.deviceId = event.getDeviceId();
+            keyEntry.source = event.getSource();
+            keyEntry.displayId = event.getDisplayId();
+            keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+            keyEntry.keyCode = fallbackKeyCode;
+            keyEntry.scanCode = event.getScanCode();
+            keyEntry.metaState = event.getMetaState();
+            keyEntry.repeatCount = event.getRepeatCount();
+            keyEntry.downTime = event.getDownTime();
+            keyEntry.syntheticRepeat = false;
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Dispatching fallback key.  "
                   "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                  originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+                  originalKeyCode, fallbackKeyCode, keyEntry.metaState);
 #endif
             return true; // restart the event
         } else {
@@ -4863,7 +5338,7 @@
 #endif
 
             // Report the key as unhandled, since there is no fallback key.
-            mReporter->reportUnhandledKey(keyEntry->id);
+            mReporter->reportUnhandledKey(keyEntry.id);
         }
     }
     return false;
@@ -4871,7 +5346,7 @@
 
 bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
                                                           DispatchEntry* dispatchEntry,
-                                                          MotionEntry* motionEntry, bool handled) {
+                                                          MotionEntry& motionEntry, bool handled) {
     return false;
 }
 
@@ -4983,4 +5458,146 @@
     return result == std::cv_status::no_timeout;
 }
 
+/**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ *
+ * Params:
+ *  request.token - input channel token used to identify the window that should gain focus.
+ *  request.focusedToken - the token that the caller expects currently to be focused. If the
+ *  specified token does not match the currently focused window, this request will be dropped.
+ *  If the specified focused token matches the currently focused window, the call will succeed.
+ *  Set this to "null" if this call should succeed no matter what the currently focused token is.
+ *  request.timestamp - SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm)
+ *  when requesting the focus change. This determines which request gets
+ *  precedence if there is a focus change request from another source such as pointer down.
+ */
+void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        const int32_t displayId = request.displayId;
+        const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+        if (request.focusedToken && oldFocusedToken != request.focusedToken) {
+            ALOGD_IF(DEBUG_FOCUS,
+                     "setFocusedWindow on display %" PRId32
+                     " ignored, reason: focusedToken is not focused",
+                     displayId);
+            return;
+        }
+
+        mPendingFocusRequests.erase(displayId);
+        FocusResult result = handleFocusRequestLocked(request);
+        if (result == FocusResult::NOT_VISIBLE) {
+            // The requested window is not currently visible. Wait for the window to become visible
+            // and then provide it focus. This is to handle situations where a user action triggers
+            // a new window to appear. We want to be able to queue any key events after the user
+            // action and deliver it to the newly focused window. In order for this to happen, we
+            // take focus from the currently focused window so key events can be queued.
+            ALOGD_IF(DEBUG_FOCUS,
+                     "setFocusedWindow on display %" PRId32
+                     " pending, reason: window is not visible",
+                     displayId);
+            mPendingFocusRequests[displayId] = request;
+            onFocusChangedLocked(oldFocusedToken, nullptr, displayId,
+                                 "setFocusedWindow_AwaitingWindowVisibility");
+        } else if (result != FocusResult::OK) {
+            ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId,
+                  typeToString(result));
+        }
+    } // release lock
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+}
+
+InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked(
+        const FocusRequest& request) {
+    const int32_t displayId = request.displayId;
+    const sp<IBinder> newFocusedToken = request.token;
+    const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+
+    if (oldFocusedToken == request.token) {
+        ALOGD_IF(DEBUG_FOCUS,
+                 "setFocusedWindow on display %" PRId32 " ignored, reason: already focused",
+                 displayId);
+        return FocusResult::OK;
+    }
+
+    FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId);
+    if (result != FocusResult::OK) {
+        return result;
+    }
+
+    std::string_view reason =
+            (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow";
+    onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason);
+    return FocusResult::OK;
+}
+
+void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
+                                           const sp<IBinder>& newFocusedToken, int32_t displayId,
+                                           std::string_view reason) {
+    if (oldFocusedToken) {
+        std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
+        if (focusedInputChannel) {
+            CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+                                       "focus left window");
+            synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+            enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
+        }
+        mFocusedWindowTokenByDisplay.erase(displayId);
+    }
+    if (newFocusedToken) {
+        mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
+        enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
+    }
+
+    if (mFocusedDisplayId == displayId) {
+        notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
+    }
+}
+
+/**
+ * Checks if the window token can be focused on a display. The token can be focused if there is
+ * at least one window handle that is visible with the same token and all window handles with the
+ * same token are focusable.
+ *
+ * In the case of mirroring, two windows may share the same window token and their visibility
+ * might be different. Example, the mirrored window can cover the window its mirroring. However,
+ * we expect the focusability of the windows to match since its hard to reason why one window can
+ * receive focus events and the other cannot when both are backed by the same input channel.
+ */
+InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token,
+                                                                        int32_t displayId) const {
+    bool allWindowsAreFocusable = true;
+    bool visibleWindowFound = false;
+    bool windowFound = false;
+    for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) {
+        if (window->getToken() != token) {
+            continue;
+        }
+        windowFound = true;
+        if (window->getInfo()->visible) {
+            // Check if at least a single window is visible.
+            visibleWindowFound = true;
+        }
+        if (!window->getInfo()->focusable) {
+            // Check if all windows with the window token are focusable.
+            allWindowsAreFocusable = false;
+            break;
+        }
+    }
+
+    if (!windowFound) {
+        return FocusResult::NO_WINDOW;
+    }
+    if (!allWindowsAreFocusable) {
+        return FocusResult::NOT_FOCUSABLE;
+    }
+    if (!visibleWindowFound) {
+        return FocusResult::NOT_VISIBLE;
+    }
+
+    return FocusResult::OK;
+}
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 0c3ea36..5387c40 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_DISPATCHER_H
 #define _UI_INPUT_DISPATCHER_H
 
+#include "AnrTracker.h"
 #include "CancelationOptions.h"
 #include "Entry.h"
 #include "InjectionState.h"
@@ -30,6 +31,7 @@
 #include "TouchState.h"
 #include "TouchedWindow.h"
 
+#include <attestation/HmacKeyManager.h>
 #include <input/Input.h>
 #include <input/InputApplication.h>
 #include <input/InputTransport.h>
@@ -48,6 +50,7 @@
 #include <deque>
 #include <optional>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <InputListener.h>
 #include <InputReporterInterface.h>
@@ -56,16 +59,6 @@
 
 class Connection;
 
-class HmacKeyManager {
-public:
-    HmacKeyManager();
-    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
-
-private:
-    std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
-    const std::array<uint8_t, 128> mHmacKey;
-};
-
 /* Dispatches events to input targets.  Some functions of the input dispatcher, such as
  * identifying input targets, are controlled by a separate policy object.
  *
@@ -102,10 +95,10 @@
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
 
-    virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
-                                     int32_t injectorUid, int32_t syncMode,
-                                     std::chrono::milliseconds timeout,
-                                     uint32_t policyFlags) override;
+    virtual android::os::InputEventInjectionResult injectInputEvent(
+            const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+            android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+            uint32_t policyFlags) override;
 
     virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
 
@@ -113,21 +106,28 @@
             const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
                     handlesPerDisplay) override;
     virtual void setFocusedApplication(
-            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
+            int32_t displayId,
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
     virtual void setFocusedDisplay(int32_t displayId) override;
     virtual void setInputDispatchMode(bool enabled, bool frozen) override;
     virtual void setInputFilterEnabled(bool enabled) override;
     virtual void setInTouchMode(bool inTouchMode) override;
+    virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
+    virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
-    virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
-                                          bool isGestureMonitor) override;
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+            const std::string& name) override;
+    virtual void setFocusedWindow(const FocusRequest&) override;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+            int32_t displayId, bool isGestureMonitor, const std::string& name) override;
+    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
     virtual status_t pilferPointers(const sp<IBinder>& token) override;
 
+    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
@@ -138,6 +138,14 @@
         STALE,
     };
 
+    enum class FocusResult {
+        OK,
+        NO_WINDOW,
+        NOT_FOCUSABLE,
+        NOT_VISIBLE,
+    };
+    static const char* typeToString(FocusResult result);
+
     std::unique_ptr<InputThread> mThread;
 
     sp<InputDispatcherPolicyInterface> mPolicy;
@@ -150,9 +158,9 @@
 
     sp<Looper> mLooper;
 
-    EventEntry* mPendingEvent GUARDED_BY(mLock);
-    std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
-    std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+    std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
+    std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
+    std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
     std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
@@ -167,16 +175,17 @@
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
 
     // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
-    bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+    bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock);
 
     // Cleans up input state when dropping an inbound event.
     void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
 
     // Enqueues a focus event.
-    void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
+    void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
+                                 std::string_view reason) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
-    void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
+    void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
 
     // App switch latency optimization.
     bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
@@ -188,7 +197,7 @@
 
     // Blocked event latency optimization.  Drops old events when the user intends
     // to transfer focus to a new application.
-    EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
+    std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
                                                     TouchState* touchState,
@@ -201,6 +210,8 @@
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
 
+    std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
+
     void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
 
     struct IBinderHash {
@@ -208,14 +219,13 @@
             return std::hash<IBinder*>{}(b.get());
         }
     };
-    std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
-            GUARDED_BY(mLock);
+    std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
+            mInputChannelsByToken GUARDED_BY(mLock);
 
     // Finds the display ID of the gesture monitor identified by the provided token.
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
             REQUIRES(mLock);
 
-
     // Input channels that will receive a copy of all input events sent to the provided display.
     std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
 
@@ -234,20 +244,21 @@
     // Event injection and synchronization.
     std::condition_variable mInjectionResultAvailable;
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
-    void setInjectionResult(EventEntry* entry, int32_t injectionResult);
+    void setInjectionResult(EventEntry& entry,
+                            android::os::InputEventInjectionResult injectionResult);
 
     std::condition_variable mInjectionSyncFinished;
-    void incrementPendingForegroundDispatches(EventEntry* entry);
-    void decrementPendingForegroundDispatches(EventEntry* entry);
+    void incrementPendingForegroundDispatches(EventEntry& entry);
+    void decrementPendingForegroundDispatches(EventEntry& entry);
 
     // Key repeat tracking.
     struct KeyRepeatState {
-        KeyEntry* lastKeyEntry; // or null if no repeat
+        std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat
         nsecs_t nextRepeatTime;
     } mKeyRepeatState GUARDED_BY(mLock);
 
     void resetKeyRepeatLocked() REQUIRES(mLock);
-    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
+    std::shared_ptr<KeyEntry> synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
 
     // Key replacement tracking
     struct KeyReplacement {
@@ -273,6 +284,9 @@
     bool runCommandsLockedInterruptible() REQUIRES(mLock);
     void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
 
+    nsecs_t processAnrsLocked() REQUIRES(mLock);
+    std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
     // Input filter processing.
     bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
     bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
@@ -280,25 +294,38 @@
     // Inbound event processing.
     void drainInboundQueueLocked() REQUIRES(mLock);
     void releasePendingEventLocked() REQUIRES(mLock);
-    void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+    void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
 
     // Dispatch state.
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
     bool mInTouchMode GUARDED_BY(mLock);
+    float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
+    android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
             GUARDED_BY(mLock);
     void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
                                int32_t displayId) REQUIRES(mLock);
-    // Get window handles by display, return an empty vector if not found.
-    std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+    // Get a reference to window handles by display, return an empty vector if not found.
+    const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const
             REQUIRES(mLock);
     sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
             REQUIRES(mLock);
-    sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+
+    // Same function as above, but faster. Since displayId is provided, this avoids the need
+    // to loop through all displays.
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                int displayId) const REQUIRES(mLock);
+    std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
+            REQUIRES(mLock);
+    sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+    bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
+    FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock);
+    FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const
+            REQUIRES(mLock);
 
     /*
      * Validate and update InputWindowHandles for a given display.
@@ -307,15 +334,17 @@
             const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
             REQUIRES(mLock);
 
-    // Focus tracking for keys, trackball, etc.
-    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
-            GUARDED_BY(mLock);
+    // Focus tracking for keys, trackball, etc. A window token can be associated with one or more
+    // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same
+    // token. Focus is tracked by the token per display and the events are dispatched to the
+    // channel associated by this token.
+    std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
 
     // Focused applications.
-    std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
-            GUARDED_BY(mLock);
+    std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
+            mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
 
     // Top focused display.
     int32_t mFocusedDisplayId GUARDED_BY(mLock);
@@ -323,65 +352,101 @@
     // Dispatcher state at time of last ANR.
     std::string mLastAnrState GUARDED_BY(mLock);
 
+    // The connection tokens of the channels that the user last interacted, for debugging
+    std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+    void updateInteractionTokensLocked(const EventEntry& entry,
+                                       const std::vector<InputTarget>& targets) REQUIRES(mLock);
+
     // Dispatch inbound events.
-    bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
+    bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
+                                            const ConfigurationChangedEntry& entry) REQUIRES(mLock);
+    bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
             REQUIRES(mLock);
-    bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
-    bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason,
-                           nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
-                              nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
-    void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
+    bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
+                           DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
+                              DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
+            REQUIRES(mLock);
+    void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
 
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
     void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
-    // Keeping track of ANR timeouts.
-    enum InputTargetWaitCause {
-        INPUT_TARGET_WAIT_CAUSE_NONE,
-        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
-        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
-    };
-
-    InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
-    bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
-    sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+    /**
+     * This field is set if there is no focused window, and we have an event that requires
+     * a focused window to be dispatched (for example, a KeyEvent).
+     * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before
+     * dropping the event and raising an ANR for that application.
+     * This is useful if an application is slow to add a focused window.
+     */
+    std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
 
     bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
 
+    /**
+     * Time to stop waiting for the events to be processed while trying to dispatch a key.
+     * When this time expires, we just send the pending key event to the currently focused window,
+     * without waiting on other events to be processed first.
+     */
+    std::optional<nsecs_t> mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock);
+    bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName)
+            REQUIRES(mLock);
+
+    /**
+     * The focused application at the time when no focused window was present.
+     * Used to raise an ANR when we have no focused window.
+     */
+    std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+    /**
+     * The displayId that the focused application is associated with.
+     */
+    int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+    void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
+
+    /**
+     * This map will store the pending focus requests that cannot be currently processed. This can
+     * happen if the window requested to be focused is not currently visible. Such a window might
+     * become visible later, and these requests would be processed at that time.
+     */
+    std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests
+            GUARDED_BY(mLock);
+
+    // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
+    // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
+    // If a connection is not responsive, then the entries should not be added to the AnrTracker.
+    // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
+    // prevent unneeded wakeups.
+    AnrTracker mAnrTracker GUARDED_BY(mLock);
+    void extendAnrTimeoutsLocked(const std::shared_ptr<InputApplicationHandle>& application,
+                                 const sp<IBinder>& connectionToken,
+                                 std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock);
+
     // Contains the last window which received a hover event.
     sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
 
-    // Finding targets for input events.
-    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
-                                        const sp<InputApplicationHandle>& applicationHandle,
-                                        const sp<InputWindowHandle>& windowHandle,
-                                        nsecs_t* nextWakeupTime, const char* reason)
-            REQUIRES(mLock);
-
-    void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
-    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
-                                                 const sp<IBinder>& inputConnectionToken)
-            REQUIRES(mLock);
+    void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
-    void resetAnrTimeoutsLocked() REQUIRES(mLock);
+    // If a focused application changes, we should stop counting down the "no focused window" time,
+    // because we will have no way of knowing when the previous application actually added a window.
+    // This also means that we will miss cases like pulling down notification shade when the
+    // focused application does not have a focused window (no ANR will be raised if notification
+    // shade is pulled down while we are counting down the timeout).
+    void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
 
     int32_t getTargetDisplayId(const EventEntry& entry);
-    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
-                                           std::vector<InputTarget>& inputTargets,
-                                           nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
-                                           std::vector<InputTarget>& inputTargets,
-                                           nsecs_t* nextWakeupTime,
-                                           bool* outConflictingPointerActions) REQUIRES(mLock);
+    android::os::InputEventInjectionResult findFocusedWindowTargetsLocked(
+            nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
+            nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
+            nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
+            nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
     std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
             int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
             REQUIRES(mLock);
+    std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
+            const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
                                BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
@@ -394,28 +459,37 @@
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
+
+    struct TouchOcclusionInfo {
+        bool hasBlockingOcclusion;
+        float obscuringOpacity;
+        std::string obscuringPackage;
+        int32_t obscuringUid;
+        std::vector<std::string> debugInfo;
+    };
+
+    TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
+                                                       int32_t x, int32_t y) const REQUIRES(mLock);
+    bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
                                        int32_t y) const REQUIRES(mLock);
     bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
-    std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
-                                          const sp<InputWindowHandle>& windowHandle);
-
-    std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
-                                                   const sp<InputWindowHandle>& windowHandle,
-                                                   const EventEntry& eventEntry,
-                                                   const char* targetType) REQUIRES(mLock);
+    std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
+    std::string getApplicationWindowLabel(
+            const std::shared_ptr<InputApplicationHandle>& applicationHandle,
+            const sp<InputWindowHandle>& windowHandle);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
     // If needed, the methods post commands to run later once the critical bits are done.
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                    EventEntry* eventEntry, const InputTarget& inputTarget)
+                                    std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
             REQUIRES(mLock);
     void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                      EventEntry* eventEntry, const InputTarget& inputTarget)
+                                      std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
             REQUIRES(mLock);
-    void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
+    void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
                                     const InputTarget& inputTarget, int32_t dispatchMode)
             REQUIRES(mLock);
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
@@ -438,8 +512,8 @@
     void synthesizeCancelationEventsForMonitorsLocked(
             const CancelationOptions& options,
             std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
-    void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
-                                                          const CancelationOptions& options)
+    void synthesizeCancelationEventsForInputChannelLocked(
+            const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
             REQUIRES(mLock);
     void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
                                                         const CancelationOptions& options)
@@ -449,7 +523,8 @@
             REQUIRES(mLock);
 
     // Splitting motion events across windows.
-    MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
+    std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
+                                                  BitSet32 pointerIds);
 
     // Reset and drop everything the dispatcher is doing.
     void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -458,13 +533,15 @@
     void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
     void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
     void logDispatchStateLocked() REQUIRES(mLock);
+    std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
+    std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
 
     // Registration.
-    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+    void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
     void removeMonitorChannelLocked(
-            const sp<InputChannel>& inputChannel,
+            const sp<IBinder>& connectionToken,
             std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
-    status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+    status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
             REQUIRES(mLock);
 
     // Interesting events that we might like to log or tell the framework about.
@@ -472,11 +549,19 @@
                                        uint32_t seq, bool handled) REQUIRES(mLock);
     void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
-    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-                              const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
-    void onAnrLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
-                     const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
-                     nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+    void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
+                              int32_t displayId, std::string_view reason) REQUIRES(mLock);
+    void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+            REQUIRES(mLock);
+    void onAnrLocked(const Connection& connection) REQUIRES(mLock);
+    void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
+    void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
+    void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
+            REQUIRES(mLock);
+    void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application,
+                                  const std::string& reason) REQUIRES(mLock);
+    void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
+            REQUIRES(mLock);
 
     // Outbound policy interactions.
     void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
@@ -484,14 +569,15 @@
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
             REQUIRES(mLock);
     void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
-                                          DispatchEntry* dispatchEntry, KeyEntry* keyEntry,
+                                          DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
     bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
-                                             DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
+                                             DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     KeyEvent createKeyEvent(const KeyEntry& entry);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 386056d..1656a21 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -265,17 +265,18 @@
     }
 }
 
-std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents(
         nsecs_t currentTime, const CancelationOptions& options) {
-    std::vector<EventEntry*> events;
+    std::vector<std::unique_ptr<EventEntry>> events;
     for (KeyMemento& memento : mKeyMementos) {
         if (shouldCancelKey(memento, options)) {
-            events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
-                                          memento.source, memento.displayId, memento.policyFlags,
-                                          AKEY_EVENT_ACTION_UP,
-                                          memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
-                                          memento.scanCode, memento.metaState, 0 /*repeatCount*/,
-                                          memento.downTime));
+            events.push_back(
+                    std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
+                                               memento.source, memento.displayId,
+                                               memento.policyFlags, AKEY_EVENT_ACTION_UP,
+                                               memento.flags | AKEY_EVENT_FLAG_CANCELED,
+                                               memento.keyCode, memento.scanCode, memento.metaState,
+                                               0 /*repeatCount*/, memento.downTime));
         }
     }
 
@@ -283,22 +284,26 @@
         if (shouldCancelMotion(memento, options)) {
             const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
                                                     : AMOTION_EVENT_ACTION_CANCEL;
-            events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
-                                             memento.source, memento.displayId, memento.policyFlags,
-                                             action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
-                                             0 /*buttonState*/, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
-                                             memento.yPrecision, memento.xCursorPosition,
-                                             memento.yCursorPosition, memento.downTime,
-                                             memento.pointerCount, memento.pointerProperties,
-                                             memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
+            events.push_back(
+                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+                                                  memento.deviceId, memento.source,
+                                                  memento.displayId, memento.policyFlags, action,
+                                                  0 /*actionButton*/, memento.flags, AMETA_NONE,
+                                                  0 /*buttonState*/, MotionClassification::NONE,
+                                                  AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+                                                  memento.yPrecision, memento.xCursorPosition,
+                                                  memento.yCursorPosition, memento.downTime,
+                                                  memento.pointerCount, memento.pointerProperties,
+                                                  memento.pointerCoords, 0 /*xOffset*/,
+                                                  0 /*yOffset*/));
         }
     }
     return events;
 }
 
-std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
-    std::vector<EventEntry*> events;
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents(
+        nsecs_t currentTime) {
+    std::vector<std::unique_ptr<EventEntry>> events;
     for (MotionMemento& memento : mMotionMementos) {
         if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
             continue;
@@ -333,15 +338,17 @@
                     : AMOTION_EVENT_ACTION_POINTER_DOWN
                             | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-            events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
-                                             memento.source, memento.displayId, memento.policyFlags,
-                                             action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
-                                             0 /*buttonState*/, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
-                                             memento.yPrecision, memento.xCursorPosition,
-                                             memento.yCursorPosition, memento.downTime,
-                                             pointerCount, pointerProperties, pointerCoords,
-                                             0 /*xOffset*/, 0 /*yOffset*/));
+            events.push_back(
+                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+                                                  memento.deviceId, memento.source,
+                                                  memento.displayId, memento.policyFlags, action,
+                                                  0 /*actionButton*/, memento.flags, AMETA_NONE,
+                                                  0 /*buttonState*/, MotionClassification::NONE,
+                                                  AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+                                                  memento.yPrecision, memento.xCursorPosition,
+                                                  memento.yCursorPosition, memento.downTime,
+                                                  pointerCount, pointerProperties, pointerCoords,
+                                                  0 /*xOffset*/, 0 /*yOffset*/));
         }
 
         memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d97a664..74ae21f 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -51,11 +51,11 @@
     bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
 
     // Synthesizes cancelation events for the current state and resets the tracked state.
-    std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
-                                                         const CancelationOptions& options);
+    std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
+            nsecs_t currentTime, const CancelationOptions& options);
 
     // Synthesizes down events for the current state.
-    std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
+    std::vector<std::unique_ptr<EventEntry>> synthesizePointerDownEvents(nsecs_t currentTime);
 
     // Clears the current state.
     void clear();
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 0588374..d39113b 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,12 +42,11 @@
     return StringPrintf("%" PRId32, dispatchMode);
 }
 
-void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
-                              float windowXScale, float windowYScale) {
+void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
     // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
     // and non splittable windows since we will just use all the pointers from the input event.
     if (newPointerIds.isEmpty()) {
-        setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+        setDefaultPointerTransform(transform);
         return;
     }
 
@@ -57,47 +56,38 @@
     pointerIds |= newPointerIds;
     while (!newPointerIds.isEmpty()) {
         int32_t pointerId = newPointerIds.clearFirstMarkedBit();
-        pointerInfos[pointerId].xOffset = xOffset;
-        pointerInfos[pointerId].yOffset = yOffset;
-        pointerInfos[pointerId].windowXScale = windowXScale;
-        pointerInfos[pointerId].windowYScale = windowYScale;
+        pointerTransforms[pointerId] = transform;
     }
 }
 
-void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
-                                        float windowYScale) {
+void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
     pointerIds.clear();
-    pointerInfos[0].xOffset = xOffset;
-    pointerInfos[0].yOffset = yOffset;
-    pointerInfos[0].windowXScale = windowXScale;
-    pointerInfos[0].windowYScale = windowYScale;
+    pointerTransforms[0] = transform;
 }
 
-bool InputTarget::useDefaultPointerInfo() const {
+bool InputTarget::useDefaultPointerTransform() const {
     return pointerIds.isEmpty();
 }
 
-const PointerInfo& InputTarget::getDefaultPointerInfo() const {
-    return pointerInfos[0];
+const ui::Transform& InputTarget::getDefaultPointerTransform() const {
+    return pointerTransforms[0];
 }
 
 std::string InputTarget::getPointerInfoString() const {
-    if (useDefaultPointerInfo()) {
-        const PointerInfo& pointerInfo = getDefaultPointerInfo();
-        return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
-                            pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
-                            pointerInfo.windowYScale);
+    std::string out = "\n";
+    if (useDefaultPointerTransform()) {
+        const ui::Transform& transform = getDefaultPointerTransform();
+        transform.dump(out, "default", "        ");
+        return out;
     }
 
-    std::string out;
     for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
         if (!pointerIds.hasBit(i)) {
             continue;
         }
-        out += StringPrintf("\n  pointerId %d: xOffset=%.1f, yOffset=%.1f "
-                            "windowScaleFactor=(%.1f, %.1f)",
-                            i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
-                            pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+
+        const std::string name = "pointerId " + std::to_string(i) + ":";
+        pointerTransforms[i].dump(out, name.c_str(), "        ");
     }
     return out;
 }
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 499a75f..debf805 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -18,28 +18,13 @@
 #define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
 
 #include <input/InputTransport.h>
+#include <ui/Transform.h>
 #include <utils/BitSet.h>
 #include <utils/RefBase.h>
 
 namespace android::inputdispatcher {
 
 /*
- * Information about each pointer for an InputTarget. This includes offset and scale so
- * all pointers can be normalized to a single offset and scale.
- *
- * These values are ignored for KeyEvents
- */
-struct PointerInfo {
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    float xOffset = 0.0f;
-    float yOffset = 0.0f;
-
-    // Scaling factor to apply to MotionEvent as it is delivered.
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
-};
-
-/*
  * An input target specifies how an input event is to be dispatched to a particular window
  * including the window's input channel, control flags, a timeout, and an X / Y offset to
  * be added to input event coordinates to compensate for the absolute position of the
@@ -106,7 +91,7 @@
     };
 
     // The input channel to be targeted.
-    sp<InputChannel> inputChannel;
+    std::shared_ptr<InputChannel> inputChannel;
 
     // Flags for the input target.
     int32_t flags = 0;
@@ -119,13 +104,11 @@
     // if FLAG_SPLIT is set.
     BitSet32 pointerIds;
     // The data is stored by the pointerId. Use the bit position of pointerIds to look up
-    // PointerInfo per pointerId.
-    PointerInfo pointerInfos[MAX_POINTERS];
+    // Transform per pointerId.
+    ui::Transform pointerTransforms[MAX_POINTERS];
 
-    void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
-                     float windowYScale);
-    void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
-                               float windowYScale);
+    void addPointers(BitSet32 pointerIds, const ui::Transform& transform);
+    void setDefaultPointerTransform(const ui::Transform& transform);
 
     /**
      * Returns whether the default pointer information should be used. This will be true when the
@@ -133,13 +116,13 @@
      * and non splittable windows since we want all pointers for the EventEntry to go to this
      * target.
      */
-    bool useDefaultPointerInfo() const;
+    bool useDefaultPointerTransform() const;
 
     /**
-     * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+     * Returns the default Transform object. This should be used when useDefaultPointerTransform is
      * true.
      */
-    const PointerInfo& getDefaultPointerInfo() const;
+    const ui::Transform& getDefaultPointerTransform() const;
 
     std::string getPointerInfoString() const;
 };
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 289b084..b347674 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,7 @@
 namespace android::inputdispatcher {
 
 // --- Monitor ---
-Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
 
 // --- TouchedMonitor ---
 TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index b67c9eb..fc0b020 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -22,9 +22,9 @@
 namespace android::inputdispatcher {
 
 struct Monitor {
-    sp<InputChannel> inputChannel; // never null
+    std::shared_ptr<InputChannel> inputChannel; // never null
 
-    explicit Monitor(const sp<InputChannel>& inputChannel);
+    explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel);
 };
 
 // For tracking the offsets we need to apply when adding gesture monitor targets.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2baceba..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -137,8 +137,7 @@
     for (const TouchedWindow& window : windows) {
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
             if (haveSlipperyForegroundWindow ||
-                !(window.windowHandle->getInfo()->layoutParamsFlags &
-                  InputWindowInfo::FLAG_SLIPPERY)) {
+                !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
                 return false;
             }
             haveSlipperyForegroundWindow = true;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9b002f4..9154d48 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,36 +18,20 @@
 #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
 
 #include <InputListener.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android-base/result.h>
+#include <android/FocusRequest.h>
+#include <android/os/BlockUntrustedTouchesMode.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <android/os/InputEventInjectionResult.h>
+#include <android/os/InputEventInjectionSync.h>
+#include <input/InputApplication.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
 #include <unordered_map>
 
+
 namespace android {
 
-class InputApplicationHandle;
-class InputChannel;
-class InputWindowHandle;
-
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
-    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
-    INPUT_EVENT_INJECTION_PENDING = -1,
-
-    /* Injection succeeded. */
-    INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
-    /* Injection failed because the injector did not have permission to inject
-     * into the application with input focus. */
-    INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
-    /* Injection failed because there were no available input targets. */
-    INPUT_EVENT_INJECTION_FAILED = 2,
-
-    /* Injection failed due to a timeout. */
-    INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
 /* Notifies the system about input events generated by the input reader.
  * The dispatcher is expected to be mostly asynchronous. */
 class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
@@ -89,9 +73,10 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
-                                     int32_t injectorUid, int32_t syncMode,
-                                     std::chrono::milliseconds timeout, uint32_t policyFlags) = 0;
+    virtual android::os::InputEventInjectionResult injectInputEvent(
+            const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+            android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+            uint32_t policyFlags) = 0;
 
     /*
      * Check whether InputEvent actually happened by checking the signature of the event.
@@ -113,7 +98,8 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual void setFocusedApplication(
-            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+            int32_t displayId,
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
 
     /* Sets the focused display.
      *
@@ -143,19 +129,42 @@
      */
     virtual void setInTouchMode(bool inTouchMode) = 0;
 
+    /**
+     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+     * For certain window types (eg. SAWs), the decision of honoring
+     * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of
+     * the windows above the touch-consuming window.
+     */
+    virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0;
+
+    /**
+     * Sets the mode of the block untrusted touches feature.
+     *
+     * TODO(b/169067926): Clean-up feature modes.
+     */
+    virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) = 0;
+
     /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
      */
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
 
-    /* Registers input channels that may be used as targets for input events.
+    /**
+     * Sets focus on the specified window.
+     */
+    virtual void setFocusedWindow(const FocusRequest&) = 0;
+
+    /**
+     * Creates an input channel that may be used as targets for input events.
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+            const std::string& name) = 0;
 
-    /* Registers input channels to be used to monitor input events.
+    /**
+     * Creates an input channel to be used to monitor input events.
      *
      * Each monitor must target a specific display and will only receive input events sent to that
      * display. If the monitor is a gesture monitor, it will only receive pointer events on the
@@ -163,14 +172,14 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
-                                          bool gestureMonitor) = 0;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+            int32_t displayId, bool gestureMonitor, const std::string& name) = 0;
 
-    /* Unregister input channels that will no longer receive input events.
+    /* Removes input channels that will no longer receive input events.
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0;
 
     /* Allows an input monitor steal the current pointer stream away from normal input windows.
      *
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 667af9b..463c5f1 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -21,11 +21,11 @@
 
 #include <binder/IBinder.h>
 #include <input/Input.h>
+#include <input/InputApplication.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
-class InputApplicationHandle;
 
 /*
  * Input dispatcher policy interface.
@@ -47,13 +47,17 @@
 
     /* Notifies the system that an application is not responding.
      * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
-                              const sp<IBinder>& token, const std::string& reason) = 0;
+    virtual std::chrono::nanoseconds notifyAnr(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+            const sp<IBinder>& token, const std::string& reason) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
     virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
 
+    /* Notifies the system that an untrusted touch occurred. */
+    virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0;
+
     /* Gets the input dispatcher configuration. */
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
 
diff --git a/services/inputflinger/docs/anr.md b/services/inputflinger/docs/anr.md
new file mode 100644
index 0000000..ce64fe9
--- /dev/null
+++ b/services/inputflinger/docs/anr.md
@@ -0,0 +1,73 @@
+# ANR detection in InputDispatcher #
+
+'ANR' means 'application not responding'. This is an event that gets triggered when the system thinks that an application is too slow to respond. A dialog may pop up because of an ANR event. ANRs can be triggered by multiple systems within Android.
+
+In InputDispatcher, ANRs are raised in 2 cases:
+
+1. An event was sent to a connection, and the response was not received within a certain timeout.
+2. The application did not have a focused window, and an input event that requires focus was generated by the user.
+
+Let's consider each of these cases.
+
+## 1. Application does not respond to an input event that was sent to it. ##
+
+The most common case is when an application does not respond to input that dispatcher sent to it. Typically, it means an application is performing a long operation on its UI thread.
+
+When the event is being dispatched to an application, the normal flow is: `mPendingEvent` → `connection.outboundQueue` → `connection.waitQueue`.
+
+Every dispatch cycle, InputDispatcher will check all connections to see if any are unresponsive. To determine whether an app is not responding, we look at the oldest entry in the `waitQueue`. If the entry sits in the `waitQueue` past `entry.timeoutTime`, we trigger an ANR.
+
+
+### Checking if a connection is unresponsive ###
+
+When a dispatch entry is sent to the app, its `deliveryTime` and `timeoutTime` fields are populated. The `deliveryTime` is the time that the event is delivered to the app. This is simply the current time inside `publishMotionEvent`. The `timeoutTime` is the time when this entry would be considered overdue. At that time, the ANR process would start for this connection.
+
+Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS`.
+
+The `timeoutTime` field of the `DispatchEntry` is needed because the window associated with a specific connection may change its timeout time. Therefore, entries sent prior to the timeout change would need to follow the previous timeout value. If a window timeout changes, it only affects new events being dispatched, and does not alter the timeout times of events already sent to the app.
+
+For example, if an application is being debugged, the ActivityManager may want to increase the timeout time for a window to prevent the ANR dialog from appearing or the app from getting killed.
+
+Looping through `waitQueue`s of all connections on every dispatch cycle could be costly. To improve this, we introduced the `AnrTracker` class.
+
+`AnrTracker` uses a multiset (a set that allows duplicate entries) to keep track of the next time a dispatch entry would become out of date. Duplicate entries are allowed because there may be two events with an identical timeout time. This is unlikely to happen in practice today, but is possible if the window timeouts are different or if the device has a high input report rate or a low clock resolution.
+
+On each dispatch cycle, InputDispatcher checks `AnrTracker` for the nearest timeout value. If the nearest timeout value is in the past, InputDispatcher will trigger the ANR for the corresponding connection.
+
+When an application sends a response for a particular dispatch entry, that entry is removed from the connection's `waitQueue`, and it is also removed from the `AnrTracker`. During normal operation, the entries are removed from `AnrTracker` quickly.
+
+
+### How to test ###
+
+In order to test this behaviour, you can create an application that calls `SystemClock.sleep` while handling a click event.
+
+When this happens, the expectation is that the ANR dialog will come up within a short period of sending an input event (typically 5 seconds). While the app is not responding, it is expected that touches on other applications and gesture monitors still continue to work.
+
+
+## 2. Application does not have a focused window, and a focused event comes in ##
+
+This is a legacy behaviour that we are maintaining inside InputDispatcher. When an application is launched, WindowManager calls `setFocusedApplication` to tell InputDispatcher that there is a focused application. This is used by InputDispatcher purely for ANR and debugging purposes.
+
+After launching, an application may not add a focused window. This could be either due to a bug in WindowManager or in the app.
+
+The legacy behaviour in this situation is as follows: touches will continue to function normally, without causing an ANR. If there is a focused event, however, it would require a focused window to be dispatched. InputDispatcher will keep this focused event inside mPendingEvent until:
+
+* A focused window is added
+* Timeout occurs
+* User touches another application
+
+To keep track of this timeout, when this situation is detected initially, `mInputTargetWaitTimeoutTime` and `mAwaitedFocusedApplication` are set. When the `mInputTargetWaitTimeoutTime` expires, an ANR will be raised.
+
+
+### How to test ###
+
+Create an empty application that sets `FLAG_NOT_FOCUSABLE` on its window in `onCreate`. Touching the application's window should not cause an ANR. Sending a key event to the application, however, should cause ANR. One easy way to do this is by pressing or gesturing the BACK key. In this scenario, `adb shell dumpsys input` will reveal that there's no focused window in the current display.
+
+
+## Extending the timeout based on the response from policy ##
+
+When the policy processes the ANR notification and responds with a positive timeout, InputDispatcher marks the connection as "responsive" by setting `inputPublisherBlocked = false`. All of the entries for this connection inside AnrTracker will be modified to expire at `time = (current time) + (timeout extension returned by policy)`.
+
+If the policy wants to abort dispatch, it returns a timeout value of 0. In this case, InputDispatcher will synthesize cancel events for the connection.
+
+When an app is unresponsive, new touches do not go to the app. They get dropped with a warning log. This is done to prevent overwhelming the app with events in case it later becomes responsive.
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index b56f356..9e797e4 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -23,6 +23,7 @@
 
     header_libs: ["jni_headers"],
     shared_libs: [
+        "libbase",
         "libbinder",
         "libcrypto",
         "libcutils",
@@ -59,9 +60,11 @@
     shared_libs: [
         "libbinder",
         "libinputflingerhost",
-        "libutils"
+        "libutils",
+        "libinput"
     ],
     static_libs: [
         "libarect",
+        "libui-types",
     ],
 }
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 683c05d..2ebdbcf 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -29,14 +29,14 @@
 
 #include <hardware/input.h>
 #include <input/InputDevice.h>
+#include <input/PropertyMap.h>
 #include <utils/Log.h>
-#include <utils/PropertyMap.h>
 #include <utils/String8.h>
 
 #define INDENT2 "    "
 
 struct input_property_map {
-    android::PropertyMap* propertyMap;
+    std::unique_ptr<android::PropertyMap> propertyMap;
 };
 
 struct input_property {
@@ -217,22 +217,25 @@
     idi.product = id->productId;
     idi.version = id->version;
 
-    std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
-            idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+    std::string configFile =
+            getInputDeviceConfigurationFilePathByDeviceIdentifier(idi,
+                                                                  InputDeviceConfigurationFileType::
+                                                                          CONFIGURATION);
     if (configFile.empty()) {
         ALOGD("No input device configuration file found for device '%s'.",
                 idi.name.c_str());
     } else {
-        auto propMap = new input_property_map_t();
-        status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
-        if (status) {
+        std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>();
+        android::base::Result<std::unique_ptr<PropertyMap>> result =
+                PropertyMap::load(configFile.c_str());
+        if (!result.ok()) {
             ALOGE("Error loading input device configuration file for device '%s'. "
                     "Using default configuration.",
                     idi.name.c_str());
-            delete propMap;
             return nullptr;
         }
-        return propMap;
+        propMap->propertyMap = std::move(*result);
+        return propMap.release();
     }
     return nullptr;
 }
@@ -276,7 +279,6 @@
 
 void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
     if (map != nullptr) {
-        delete map->propertyMap;
         delete map;
     }
 }
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 973b4f9..47773d9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -22,13 +22,17 @@
 
 #include "InputHost.h"
 
+#include <android/os/BnInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <binder/Binder.h>
 #include <cutils/compiler.h>
-#include <input/IInputFlinger.h>
-#include <input/ISetInputWindowsListener.h>
-#include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
+
 namespace android {
 
 class InputFlinger : public BnInputFlinger {
@@ -40,10 +44,15 @@
     InputFlinger() ANDROID_API;
 
     virtual status_t dump(int fd, const Vector<String16>& args);
-    void setInputWindows(const std::vector<InputWindowInfo>&,
-            const sp<ISetInputWindowsListener>&) {}
-    void registerInputChannel(const sp<InputChannel>&) {}
-    void unregisterInputChannel(const sp<InputChannel>&) {}
+    binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
+                                   const sp<ISetInputWindowsListener>&) {
+        return binder::Status::ok();
+    }
+    binder::Status createInputChannel(const std::string&, InputChannel*) {
+        return binder::Status::ok();
+    }
+    binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
+    binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
 
 private:
     virtual ~InputFlinger();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index f8d5351..ffd8bf2 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -17,31 +17,28 @@
 #ifndef _UI_INPUT_READER_BASE_H
 #define _UI_INPUT_READER_BASE_H
 
-#include "PointerControllerInterface.h"
-
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
+#include <stddef.h>
+#include <unistd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
-#include <stddef.h>
-#include <unistd.h>
 #include <optional>
 #include <set>
 #include <unordered_map>
 #include <vector>
 
+#include "PointerControllerInterface.h"
+#include "VibrationElement.h"
+
 // Maximum supported size of a vibration pattern.
 // Must be at least 2.
 #define MAX_VIBRATE_PATTERN_SIZE 100
 
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
-
 namespace android {
 
 // --- InputReaderInterface ---
@@ -81,7 +78,7 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) = 0;
+    virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
 
     /* Query current input state. */
     virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
@@ -104,8 +101,8 @@
     virtual void requestRefreshConfiguration(uint32_t changes) = 0;
 
     /* Controls the vibrator of a particular input device. */
-    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-            ssize_t repeat, int32_t token) = 0;
+    virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
+                         ssize_t repeat, int32_t token) = 0;
     virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
 
     /* Return true if the device can send input events to the specified display. */
@@ -335,7 +332,8 @@
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
 
     /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
-    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+    virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(
+            int32_t deviceId) = 0;
 
     /* Notifies the input reader policy that some input devices have changed
      * and provides information about all current input devices.
@@ -343,7 +341,7 @@
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
 
     /* Gets the keyboard layout for a particular input device. */
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+    virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier) = 0;
 
     /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 194c665..85d7247 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -33,7 +33,7 @@
  * The pointer controller is responsible for providing synchronization and for tracking
  * display orientation changes if needed.
  */
-class PointerControllerInterface : public virtual RefBase {
+class PointerControllerInterface {
 protected:
     PointerControllerInterface() { }
     virtual ~PointerControllerInterface() { }
@@ -59,11 +59,11 @@
     /* Gets the absolute location of the pointer. */
     virtual void getPosition(float* outX, float* outY) const = 0;
 
-    enum Transition {
+    enum class Transition {
         // Fade/unfade immediately.
-        TRANSITION_IMMEDIATE,
+        IMMEDIATE,
         // Fade/unfade gradually.
-        TRANSITION_GRADUAL,
+        GRADUAL,
     };
 
     /* Fades the pointer out now. */
@@ -75,11 +75,11 @@
      * wants to ensure that the pointer becomes visible again. */
     virtual void unfade(Transition transition) = 0;
 
-    enum Presentation {
+    enum class Presentation {
         // Show the mouse pointer.
-        PRESENTATION_POINTER,
+        POINTER,
         // Show spots and a spot anchor in place of the mouse pointer.
-        PRESENTATION_SPOT,
+        SPOT,
     };
 
     /* Sets the mode of the pointer controller. */
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
new file mode 100644
index 0000000..b60ffac
--- /dev/null
+++ b/services/inputflinger/include/VibrationElement.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VIBRATION_ELEMENT_H
+#define _VIBRATION_ELEMENT_H
+
+#include <array>
+#include <chrono>
+#include <cstdint>
+#include <string>
+
+namespace android {
+
+// evdev FF_RUMBLE effect only supports two channels of vibration.
+constexpr size_t CHANNEL_SIZE = 2;
+/*
+ * Describes a rumble effect
+ */
+struct VibrationElement {
+    std::chrono::milliseconds duration;
+    // Channel amplitude range 0-255.
+    std::array<uint8_t, CHANNEL_SIZE> channels = {0, 0};
+
+    const std::string toString() const;
+    uint16_t getMagnitude(size_t channelIndex) const;
+    bool isOn() const;
+};
+
+} // namespace android
+
+#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 83a610f..0ccada9 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -81,4 +81,7 @@
     export_header_lib_headers: [
         "libinputreader_headers",
     ],
+    static_libs: [
+        "libc++fs"
+    ],
 }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a1514af..c5210b5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -34,45 +34,36 @@
 #define LOG_TAG "EventHub"
 
 // #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/VirtualKeyMap.h>
 #include <openssl/sha.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
-#include <utils/threads.h>
 
-#include <input/KeyCharacterMap.h>
-#include <input/KeyLayoutMap.h>
-#include <input/VirtualKeyMap.h>
+#include <filesystem>
 
-/* this macro is used to tell if "bit" is set in "array"
- * it selects a byte from the array, and does a boolean AND
- * operation with a byte that only has the relevant bit set.
- * eg. to check for the 12th bit, we do (array[1] & 1<<4)
- */
-#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
-
-/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) (((bits) + 7) / 8)
+#include "EventHub.h"
 
 #define INDENT "  "
 #define INDENT2 "    "
 #define INDENT3 "      "
 
 using android::base::StringPrintf;
+using namespace android::flag_operators;
 
 namespace android {
 
-static constexpr bool DEBUG = false;
-
 static const char* DEVICE_PATH = "/dev/input";
 // v4l2 devices go directly into /dev
 static const char* VIDEO_DEVICE_PATH = "/dev";
 
+static constexpr size_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
+static constexpr size_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+
 static inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
@@ -94,8 +85,8 @@
 /**
  * Return true if name matches "v4l-touch*"
  */
-static bool isV4lTouchNode(const char* name) {
-    return strstr(name, "v4l-touch") == name;
+static bool isV4lTouchNode(std::string name) {
+    return name.find("v4l-touch") != std::string::npos;
 }
 
 /**
@@ -138,9 +129,9 @@
 
 // --- Global Functions ---
 
-uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
+Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses) {
     // Touch devices get dibs on touch-related axes.
-    if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
+    if (deviceClasses.test(InputDeviceClass::TOUCH)) {
         switch (axis) {
             case ABS_X:
             case ABS_Y:
@@ -162,27 +153,26 @@
             case ABS_MT_TRACKING_ID:
             case ABS_MT_PRESSURE:
             case ABS_MT_DISTANCE:
-                return INPUT_DEVICE_CLASS_TOUCH;
+                return InputDeviceClass::TOUCH;
         }
     }
 
     // External stylus gets the pressure axis
-    if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) {
         if (axis == ABS_PRESSURE) {
-            return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+            return InputDeviceClass::EXTERNAL_STYLUS;
         }
     }
 
     // Joystick devices get the rest.
-    return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
+    return deviceClasses & InputDeviceClass::JOYSTICK;
 }
 
 // --- EventHub::Device ---
 
 EventHub::Device::Device(int fd, int32_t id, const std::string& path,
                          const InputDeviceIdentifier& identifier)
-      : next(nullptr),
-        fd(fd),
+      : fd(fd),
         id(id),
         path(path),
         identifier(identifier),
@@ -193,19 +183,10 @@
         ffEffectId(-1),
         controllerNumber(0),
         enabled(true),
-        isVirtual(fd < 0) {
-    memset(keyBitmask, 0, sizeof(keyBitmask));
-    memset(absBitmask, 0, sizeof(absBitmask));
-    memset(relBitmask, 0, sizeof(relBitmask));
-    memset(swBitmask, 0, sizeof(swBitmask));
-    memset(ledBitmask, 0, sizeof(ledBitmask));
-    memset(ffBitmask, 0, sizeof(ffBitmask));
-    memset(propBitmask, 0, sizeof(propBitmask));
-}
+        isVirtual(fd < 0) {}
 
 EventHub::Device::~Device() {
     close();
-    delete configuration;
 }
 
 void EventHub::Device::close() {
@@ -231,10 +212,159 @@
     return OK;
 }
 
-bool EventHub::Device::hasValidFd() {
+bool EventHub::Device::hasValidFd() const {
     return !isVirtual && enabled;
 }
 
+const std::shared_ptr<KeyCharacterMap> EventHub::Device::getKeyCharacterMap() const {
+    return keyMap.keyCharacterMap;
+}
+
+template <std::size_t N>
+status_t EventHub::Device::readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray) {
+    if (!hasValidFd()) {
+        return BAD_VALUE;
+    }
+    if ((_IOC_SIZE(ioctlCode) == 0)) {
+        ioctlCode |= _IOC(0, 0, 0, bitArray.bytes());
+    }
+
+    typename BitArray<N>::Buffer buffer;
+    status_t ret = ioctl(fd, ioctlCode, buffer.data());
+    bitArray.loadFromBuffer(buffer);
+    return ret;
+}
+
+void EventHub::Device::configureFd() {
+    // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
+    if (classes.test(InputDeviceClass::KEYBOARD)) {
+        // Disable kernel key repeat since we handle it ourselves
+        unsigned int repeatRate[] = {0, 0};
+        if (ioctl(fd, EVIOCSREP, repeatRate)) {
+            ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno));
+        }
+    }
+
+    // Tell the kernel that we want to use the monotonic clock for reporting timestamps
+    // associated with input events.  This is important because the input system
+    // uses the timestamps extensively and assumes they were recorded using the monotonic
+    // clock.
+    int clockId = CLOCK_MONOTONIC;
+    bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
+    ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+}
+
+bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+    if (!keyMap.haveKeyLayout()) {
+        return false;
+    }
+
+    std::vector<int32_t> scanCodes;
+    keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
+    const size_t N = scanCodes.size();
+    for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
+        int32_t sc = scanCodes[i];
+        if (sc >= 0 && sc <= KEY_MAX && keyBitmask.test(sc)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void EventHub::Device::loadConfigurationLocked() {
+    configurationFile =
+            getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
+                                                                  InputDeviceConfigurationFileType::
+                                                                          CONFIGURATION);
+    if (configurationFile.empty()) {
+        ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
+    } else {
+        android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+                PropertyMap::load(configurationFile.c_str());
+        if (!propertyMap.ok()) {
+            ALOGE("Error loading input device configuration file for device '%s'.  "
+                  "Using default configuration.",
+                  identifier.name.c_str());
+        } else {
+            configuration = std::move(*propertyMap);
+        }
+    }
+}
+
+bool EventHub::Device::loadVirtualKeyMapLocked() {
+    // The virtual key map is supplied by the kernel as a system board property file.
+    std::string propPath = "/sys/board_properties/virtualkeys.";
+    propPath += identifier.getCanonicalName();
+    if (access(propPath.c_str(), R_OK)) {
+        return false;
+    }
+    virtualKeyMap = VirtualKeyMap::load(propPath);
+    return virtualKeyMap != nullptr;
+}
+
+status_t EventHub::Device::loadKeyMapLocked() {
+    return keyMap.load(identifier, configuration.get());
+}
+
+bool EventHub::Device::isExternalDeviceLocked() {
+    if (configuration) {
+        bool value;
+        if (configuration->tryGetProperty(String8("device.internal"), value)) {
+            return !value;
+        }
+    }
+    return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH;
+}
+
+bool EventHub::Device::deviceHasMicLocked() {
+    if (configuration) {
+        bool value;
+        if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+            return value;
+        }
+    }
+    return false;
+}
+
+void EventHub::Device::setLedStateLocked(int32_t led, bool on) {
+    int32_t sc;
+    if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) {
+        struct input_event ev;
+        ev.time.tv_sec = 0;
+        ev.time.tv_usec = 0;
+        ev.type = EV_LED;
+        ev.code = sc;
+        ev.value = on ? 1 : 0;
+
+        ssize_t nWrite;
+        do {
+            nWrite = write(fd, &ev, sizeof(struct input_event));
+        } while (nWrite == -1 && errno == EINTR);
+    }
+}
+
+void EventHub::Device::setLedForControllerLocked() {
+    for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
+        setLedStateLocked(ALED_CONTROLLER_1 + i, controllerNumber == i + 1);
+    }
+}
+
+status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const {
+    if (!keyMap.haveKeyLayout()) {
+        return NAME_NOT_FOUND;
+    }
+
+    int32_t scanCode;
+    if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
+        if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) {
+            *outScanCode = scanCode;
+            return NO_ERROR;
+        }
+    }
+    return NAME_NOT_FOUND;
+}
+
 /**
  * Get the capabilities for the current process.
  * Crashes the system if unable to create / check / destroy the capabilities object.
@@ -284,8 +414,6 @@
       : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
         mNextDeviceId(1),
         mControllerNumbers(),
-        mOpeningDevices(nullptr),
-        mClosingDevices(nullptr),
         mNeedToSendFinishedDeviceScan(false),
         mNeedToReopenDevices(false),
         mNeedToScanDevices(true),
@@ -340,12 +468,6 @@
 EventHub::~EventHub(void) {
     closeAllDevicesLocked();
 
-    while (mClosingDevices) {
-        Device* device = mClosingDevices;
-        mClosingDevices = device->next;
-        delete device;
-    }
-
     ::close(mEpollFd);
     ::close(mINotifyFd);
     ::close(mWakeReadPipeFd);
@@ -355,28 +477,25 @@
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) return InputDeviceIdentifier();
-    return device->identifier;
+    return device != nullptr ? device->identifier : InputDeviceIdentifier();
 }
 
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
+Flags<InputDeviceClass> EventHub::getDeviceClasses(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) return 0;
-    return device->classes;
+    return device != nullptr ? device->classes : Flags<InputDeviceClass>(0);
 }
 
 int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) return 0;
-    return device->controllerNumber;
+    return device != nullptr ? device->controllerNumber : 0;
 }
 
 void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->configuration) {
+    if (device != nullptr && device->configuration) {
         *outConfiguration = *device->configuration;
     } else {
         outConfiguration->clear();
@@ -391,7 +510,7 @@
         AutoMutex _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
-        if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+        if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
             struct input_absinfo info;
             if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
                 ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -416,25 +535,19 @@
 bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
     if (axis >= 0 && axis <= REL_MAX) {
         AutoMutex _l(mLock);
-
         Device* device = getDeviceLocked(deviceId);
-        if (device) {
-            return test_bit(axis, device->relBitmask);
-        }
+        return device != nullptr ? device->relBitmask.test(axis) : false;
     }
     return false;
 }
 
 bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
-    if (property >= 0 && property <= INPUT_PROP_MAX) {
-        AutoMutex _l(mLock);
+    AutoMutex _l(mLock);
 
-        Device* device = getDeviceLocked(deviceId);
-        if (device) {
-            return test_bit(property, device->propBitmask);
-        }
-    }
-    return false;
+    Device* device = getDeviceLocked(deviceId);
+    return property >= 0 && property <= INPUT_PROP_MAX && device != nullptr
+            ? device->propBitmask.test(property)
+            : false;
 }
 
 int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
@@ -442,11 +555,9 @@
         AutoMutex _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
-        if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) {
-            uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
-            memset(keyState, 0, sizeof(keyState));
-            if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
-                return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+        if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
+            if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
+                return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
             }
         }
     }
@@ -457,16 +568,14 @@
     AutoMutex _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
+    if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
         std::vector<int32_t> scanCodes;
         device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
         if (scanCodes.size() != 0) {
-            uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
-            memset(keyState, 0, sizeof(keyState));
-            if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
+            if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
                 for (size_t i = 0; i < scanCodes.size(); i++) {
                     int32_t sc = scanCodes[i];
-                    if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
+                    if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
                         return AKEY_STATE_DOWN;
                     }
                 }
@@ -482,11 +591,9 @@
         AutoMutex _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
-        if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) {
-            uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
-            memset(swState, 0, sizeof(swState));
-            if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
-                return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+        if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
+            if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
+                return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
             }
         }
     }
@@ -500,7 +607,7 @@
         AutoMutex _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
-        if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+        if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
             struct input_absinfo info;
             if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
                 ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -520,7 +627,7 @@
     AutoMutex _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->keyMap.haveKeyLayout()) {
+    if (device != nullptr && device->keyMap.haveKeyLayout()) {
         std::vector<int32_t> scanCodes;
         for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
             scanCodes.clear();
@@ -531,7 +638,7 @@
                 // check the possible scan codes identified by the layout map against the
                 // map of codes actually emitted by the driver
                 for (size_t sc = 0; sc < scanCodes.size(); sc++) {
-                    if (test_bit(scanCodes[sc], device->keyBitmask)) {
+                    if (device->keyBitmask.test(scanCodes[sc])) {
                         outFlags[codeIndex] = 1;
                         break;
                     }
@@ -549,10 +656,10 @@
     Device* device = getDeviceLocked(deviceId);
     status_t status = NAME_NOT_FOUND;
 
-    if (device) {
+    if (device != nullptr) {
         // Check the key character map first.
-        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
-        if (kcm != nullptr) {
+        const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+        if (kcm) {
             if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                 *outFlags = 0;
                 status = NO_ERROR;
@@ -567,7 +674,7 @@
         }
 
         if (status == NO_ERROR) {
-            if (kcm != nullptr) {
+            if (kcm) {
                 kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
             } else {
                 *outMetaState = metaState;
@@ -588,7 +695,7 @@
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
 
-    if (device && device->keyMap.haveKeyLayout()) {
+    if (device != nullptr && device->keyMap.haveKeyLayout()) {
         status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
         if (err == NO_ERROR) {
             return NO_ERROR;
@@ -607,10 +714,8 @@
 bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
-        if (test_bit(scanCode, device->keyBitmask)) {
-            return true;
-        }
+    if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) {
+        return device->keyBitmask.test(scanCode);
     }
     return false;
 }
@@ -619,10 +724,8 @@
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     int32_t sc;
-    if (device && mapLed(device, led, &sc) == NO_ERROR) {
-        if (test_bit(sc, device->ledBitmask)) {
-            return true;
-        }
+    if (device != nullptr && device->mapLed(led, &sc) == NO_ERROR) {
+        return device->ledBitmask.test(sc);
     }
     return false;
 }
@@ -630,23 +733,8 @@
 void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    setLedStateLocked(device, led, on);
-}
-
-void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
-    int32_t sc;
-    if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
-        struct input_event ev;
-        ev.time.tv_sec = 0;
-        ev.time.tv_usec = 0;
-        ev.type = EV_LED;
-        ev.code = sc;
-        ev.value = on ? 1 : 0;
-
-        ssize_t nWrite;
-        do {
-            nWrite = write(device->fd, &ev, sizeof(struct input_event));
-        } while (nWrite == -1 && errno == EINTR);
+    if (device != nullptr && device->hasValidFd()) {
+        device->setLedStateLocked(led, on);
     }
 }
 
@@ -656,31 +744,29 @@
 
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->virtualKeyMap) {
+    if (device != nullptr && device->virtualKeyMap) {
         const std::vector<VirtualKeyDefinition> virtualKeys =
                 device->virtualKeyMap->getVirtualKeys();
         outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end());
     }
 }
 
-sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
+const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device) {
+    if (device != nullptr) {
         return device->getKeyCharacterMap();
     }
     return nullptr;
 }
 
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device) {
-        if (map != device->overlayKeyMap) {
-            device->overlayKeyMap = map;
-            device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
-            return true;
-        }
+    if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
+        device->keyMap.keyCharacterMap->combine(*map);
+        device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName();
+        return true;
     }
     return false;
 }
@@ -735,17 +821,18 @@
           identifier.descriptor.c_str());
 }
 
-void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->hasValidFd()) {
+    if (device != nullptr && device->hasValidFd()) {
         ff_effect effect;
         memset(&effect, 0, sizeof(effect));
         effect.type = FF_RUMBLE;
         effect.id = device->ffEffectId;
-        effect.u.rumble.strong_magnitude = 0xc000;
-        effect.u.rumble.weak_magnitude = 0xc000;
-        effect.replay.length = (duration + 999999LL) / 1000000LL;
+        // evdev FF_RUMBLE effect only supports two channels of vibration.
+        effect.u.rumble.strong_magnitude = element.getMagnitude(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+        effect.u.rumble.weak_magnitude = element.getMagnitude(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
+        effect.replay.length = element.duration.count();
         effect.replay.delay = 0;
         if (ioctl(device->fd, EVIOCSFF, &effect)) {
             ALOGW("Could not upload force feedback effect to device %s due to error %d.",
@@ -772,7 +859,7 @@
 void EventHub::cancelVibrate(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device && device->hasValidFd()) {
+    if (device != nullptr && device->hasValidFd()) {
         if (device->ffEffectPlaying) {
             device->ffEffectPlaying = false;
 
@@ -792,11 +879,9 @@
 }
 
 EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
-    size_t size = mDevices.size();
-    for (size_t i = 0; i < size; i++) {
-        Device* device = mDevices.valueAt(i);
+    for (const auto& [id, device] : mDevices) {
         if (descriptor == device->identifier.descriptor) {
-            return device;
+            return device.get();
         }
     }
     return nullptr;
@@ -806,15 +891,14 @@
     if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) {
         deviceId = mBuiltInKeyboardId;
     }
-    ssize_t index = mDevices.indexOfKey(deviceId);
-    return index >= 0 ? mDevices.valueAt(index) : NULL;
+    const auto& it = mDevices.find(deviceId);
+    return it != mDevices.end() ? it->second.get() : nullptr;
 }
 
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        Device* device = mDevices.valueAt(i);
+EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const {
+    for (const auto& [id, device] : mDevices) {
         if (device->path == devicePath) {
-            return device;
+            return device.get();
         }
     }
     return nullptr;
@@ -828,15 +912,14 @@
  * devices are ignored.
  */
 EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        Device* device = mDevices.valueAt(i);
+    for (const auto& [id, device] : mDevices) {
         if (device->fd == fd) {
             // This is an input device event
-            return device;
+            return device.get();
         }
         if (device->videoDevice && device->videoDevice->getFd() == fd) {
             // This is a video device event
-            return device;
+            return device.get();
         }
     }
     // We do not check mUnattachedVideoDevices here because they should not participate in epoll,
@@ -869,17 +952,16 @@
         }
 
         // Report any devices that had last been added/removed.
-        while (mClosingDevices) {
-            Device* device = mClosingDevices;
+        for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
+            std::unique_ptr<Device> device = std::move(*it);
             ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
-            mClosingDevices = device->next;
             event->when = now;
             event->deviceId = (device->id == mBuiltInKeyboardId)
                     ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                     : device->id;
             event->type = DEVICE_REMOVED;
             event += 1;
-            delete device;
+            it = mClosingDevices.erase(it);
             mNeedToSendFinishedDeviceScan = true;
             if (--capacity == 0) {
                 break;
@@ -892,14 +974,30 @@
             mNeedToSendFinishedDeviceScan = true;
         }
 
-        while (mOpeningDevices != nullptr) {
-            Device* device = mOpeningDevices;
+        while (!mOpeningDevices.empty()) {
+            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+            mOpeningDevices.pop_back();
             ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
-            mOpeningDevices = device->next;
             event->when = now;
             event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
             event->type = DEVICE_ADDED;
             event += 1;
+
+            // Try to find a matching video device by comparing device names
+            for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+                 it++) {
+                std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+                if (tryAddVideoDevice(*device, videoDevice)) {
+                    // videoDevice was transferred to 'device'
+                    it = mUnattachedVideoDevices.erase(it);
+                    break;
+                }
+            }
+
+            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+            if (!inserted) {
+                ALOGW("Device id %d exists, replaced.", device->id);
+            }
             mNeedToSendFinishedDeviceScan = true;
             if (--capacity == 0) {
                 break;
@@ -933,11 +1031,11 @@
                 if (eventItem.events & EPOLLIN) {
                     ALOGV("awoken after wake()");
                     awoken = true;
-                    char buffer[16];
+                    char wakeReadBuffer[16];
                     ssize_t nRead;
                     do {
-                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
-                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+                        nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
+                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
                 } else {
                     ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                           eventItem.events);
@@ -946,7 +1044,7 @@
             }
 
             Device* device = getDeviceByFdLocked(eventItem.data.fd);
-            if (!device) {
+            if (device == nullptr) {
                 ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
                       eventItem.data.fd);
                 ALOG_ASSERT(!DEBUG);
@@ -982,7 +1080,7 @@
                           " bufferSize: %zu capacity: %zu errno: %d)\n",
                           device->fd, readSize, bufferSize, capacity, errno);
                     deviceChanged = true;
-                    closeDeviceLocked(device);
+                    closeDeviceLocked(*device);
                 } else if (readSize < 0) {
                     if (errno != EAGAIN && errno != EINTR) {
                         ALOGW("could not get event (errno=%d)", errno);
@@ -1014,7 +1112,7 @@
                 ALOGI("Removing device %s due to epoll hang-up event.",
                       device->identifier.name.c_str());
                 deviceChanged = true;
-                closeDeviceLocked(device);
+                closeDeviceLocked(*device);
             } else {
                 ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
                       device->identifier.name.c_str());
@@ -1089,7 +1187,7 @@
     AutoMutex _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
-    if (!device || !device->videoDevice) {
+    if (device == nullptr || !device->videoDevice) {
         return {};
     }
     return device->videoDevice->consumeFrames();
@@ -1119,24 +1217,13 @@
             ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
         }
     }
-    if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
+    if (mDevices.find(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) == mDevices.end()) {
         createVirtualKeyboardLocked();
     }
 }
 
 // ----------------------------------------------------------------------------
 
-static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
-    const uint8_t* end = array + endIndex;
-    array += startIndex;
-    while (array != end) {
-        if (*(array++) != 0) {
-            return true;
-        }
-    }
-    return false;
-}
-
 static const int32_t GAMEPAD_KEYCODES[] = {
         AKEYCODE_BUTTON_A,      AKEYCODE_BUTTON_B,      AKEYCODE_BUTTON_C,    //
         AKEYCODE_BUTTON_X,      AKEYCODE_BUTTON_Y,      AKEYCODE_BUTTON_Z,    //
@@ -1166,20 +1253,14 @@
     return OK;
 }
 
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
-    if (device == nullptr) {
-        if (DEBUG) {
-            LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
-        }
-        return BAD_VALUE;
-    }
-    status_t result = registerFdForEpoll(device->fd);
+status_t EventHub::registerDeviceForEpollLocked(Device& device) {
+    status_t result = registerFdForEpoll(device.fd);
     if (result != OK) {
-        ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+        ALOGE("Could not add input device fd to epoll for device %" PRId32, device.id);
         return result;
     }
-    if (device->videoDevice) {
-        registerVideoDeviceForEpollLocked(*device->videoDevice);
+    if (device.videoDevice) {
+        registerVideoDeviceForEpollLocked(*device.videoDevice);
     }
     return result;
 }
@@ -1191,16 +1272,16 @@
     }
 }
 
-status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
-    if (device->hasValidFd()) {
-        status_t result = unregisterFdFromEpoll(device->fd);
+status_t EventHub::unregisterDeviceFromEpollLocked(Device& device) {
+    if (device.hasValidFd()) {
+        status_t result = unregisterFdFromEpoll(device.fd);
         if (result != OK) {
-            ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+            ALOGW("Could not remove input device fd from epoll for device %" PRId32, device.id);
             return result;
         }
     }
-    if (device->videoDevice) {
-        unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+    if (device.videoDevice) {
+        unregisterVideoDeviceFromEpollLocked(*device.videoDevice);
     }
     return OK;
 }
@@ -1215,14 +1296,14 @@
     }
 }
 
-status_t EventHub::openDeviceLocked(const char* devicePath) {
+status_t EventHub::openDeviceLocked(const std::string& devicePath) {
     char buffer[80];
 
-    ALOGV("Opening device: %s", devicePath);
+    ALOGV("Opening device: %s", devicePath.c_str());
 
-    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+    int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
     if (fd < 0) {
-        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
+        ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
         return -1;
     }
 
@@ -1230,7 +1311,7 @@
 
     // Get device name.
     if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
-        ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
+        ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
         identifier.name = buffer;
@@ -1240,7 +1321,7 @@
     for (size_t i = 0; i < mExcludedDevices.size(); i++) {
         const std::string& item = mExcludedDevices[i];
         if (identifier.name == item) {
-            ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
+            ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
             close(fd);
             return -1;
         }
@@ -1249,7 +1330,7 @@
     // Get device driver version.
     int driverVersion;
     if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
-        ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+        ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
         close(fd);
         return -1;
     }
@@ -1257,7 +1338,7 @@
     // Get device identifier.
     struct input_id inputId;
     if (ioctl(fd, EVIOCGID, &inputId)) {
-        ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+        ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
         close(fd);
         return -1;
     }
@@ -1287,9 +1368,9 @@
 
     // Allocate device.  (The device object takes ownership of the fd at this point.)
     int32_t deviceId = mNextDeviceId++;
-    Device* device = new Device(fd, deviceId, devicePath, identifier);
+    std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);
 
-    ALOGV("add device %d: %s\n", deviceId, devicePath);
+    ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
     ALOGV("  bus:        %04x\n"
           "  vendor      %04x\n"
           "  product     %04x\n"
@@ -1303,35 +1384,31 @@
           driverVersion & 0xff);
 
     // Load the configuration file for the device.
-    loadConfigurationLocked(device);
+    device->loadConfigurationLocked();
 
     // Figure out the kinds of events the device reports.
-    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
-    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
-    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
-    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
-    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
-    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
-    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
+    device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
 
     // See if this is a keyboard.  Ignore everything in the button range except for
     // joystick and gamepad buttons which are handled like keyboards for the most part.
     bool haveKeyboardKeys =
-            containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
-            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL),
-                                sizeof_bit_array(KEY_MAX + 1));
-    bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
-                                                  sizeof_bit_array(BTN_MOUSE)) ||
-            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
-                                sizeof_bit_array(BTN_DIGI));
+            device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
+    bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
+            device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
     if (haveKeyboardKeys || haveGamepadButtons) {
-        device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+        device->classes |= InputDeviceClass::KEYBOARD;
     }
 
     // See if this is a cursor device such as a trackball or mouse.
-    if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
-        test_bit(REL_Y, device->relBitmask)) {
-        device->classes |= INPUT_DEVICE_CLASS_CURSOR;
+    if (device->keyBitmask.test(BTN_MOUSE) && device->relBitmask.test(REL_X) &&
+        device->relBitmask.test(REL_Y)) {
+        device->classes |= InputDeviceClass::CURSOR;
     }
 
     // See if this is a rotary encoder type device.
@@ -1339,43 +1416,41 @@
     if (device->configuration &&
         device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
         if (!deviceType.compare(String8("rotaryEncoder"))) {
-            device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
+            device->classes |= InputDeviceClass::ROTARY_ENCODER;
         }
     }
 
     // See if this is a touch pad.
     // Is this a new modern multi-touch driver?
-    if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
-        test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+    if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
         // Some joysticks such as the PS3 controller report axes that conflict
         // with the ABS_MT range.  Try to confirm that the device really is
         // a touch screen.
-        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
-            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
+        if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
+            device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
         }
         // Is this an old style single-touch driver?
-    } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
-               test_bit(ABS_Y, device->absBitmask)) {
-        device->classes |= INPUT_DEVICE_CLASS_TOUCH;
+    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
+               device->absBitmask.test(ABS_Y)) {
+        device->classes |= InputDeviceClass::TOUCH;
         // Is this a BT stylus?
-    } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
-                test_bit(BTN_TOUCH, device->keyBitmask)) &&
-               !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
-        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+    } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
+               !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
+        device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
         // Keyboard will try to claim some of the buttons but we really want to reserve those so we
         // can fuse it with the touch screen data, so just take them back. Note this means an
         // external stylus cannot also be a keyboard device.
-        device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
+        device->classes &= ~InputDeviceClass::KEYBOARD;
     }
 
     // See if this device is a joystick.
     // Assumes that joysticks always have gamepad buttons in order to distinguish them
     // from other devices such as accelerometers that also have absolute axes.
     if (haveGamepadButtons) {
-        uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
+        auto assumedClasses = device->classes | InputDeviceClass::JOYSTICK;
         for (int i = 0; i <= ABS_MAX; i++) {
-            if (test_bit(i, device->absBitmask) &&
-                (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+            if (device->absBitmask.test(i) &&
+                (getAbsAxisUsage(i, assumedClasses).test(InputDeviceClass::JOYSTICK))) {
                 device->classes = assumedClasses;
                 break;
             }
@@ -1384,142 +1459,107 @@
 
     // Check whether this device has switches.
     for (int i = 0; i <= SW_MAX; i++) {
-        if (test_bit(i, device->swBitmask)) {
-            device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+        if (device->swBitmask.test(i)) {
+            device->classes |= InputDeviceClass::SWITCH;
             break;
         }
     }
 
     // Check whether this device supports the vibrator.
-    if (test_bit(FF_RUMBLE, device->ffBitmask)) {
-        device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+    if (device->ffBitmask.test(FF_RUMBLE)) {
+        device->classes |= InputDeviceClass::VIBRATOR;
     }
 
     // Configure virtual keys.
-    if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
+    if ((device->classes.test(InputDeviceClass::TOUCH))) {
         // Load the virtual keys for the touch screen, if any.
         // We do this now so that we can make sure to load the keymap if necessary.
-        bool success = loadVirtualKeyMapLocked(device);
+        bool success = device->loadVirtualKeyMapLocked();
         if (success) {
-            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+            device->classes |= InputDeviceClass::KEYBOARD;
         }
     }
 
     // Load the key map.
     // We need to do this for joysticks too because the key layout may specify axes.
     status_t keyMapStatus = NAME_NOT_FOUND;
-    if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
+    if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK)) {
         // Load the keymap for the device.
-        keyMapStatus = loadKeyMapLocked(device);
+        keyMapStatus = device->loadKeyMapLocked();
     }
 
     // Configure the keyboard, gamepad or virtual keyboard.
-    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+    if (device->classes.test(InputDeviceClass::KEYBOARD)) {
         // Register the keyboard as a built-in keyboard if it is eligible.
         if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
-            isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
+            isEligibleBuiltInKeyboard(device->identifier, device->configuration.get(),
+                                      &device->keyMap)) {
             mBuiltInKeyboardId = device->id;
         }
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
-        if (hasKeycodeLocked(device, AKEYCODE_Q)) {
-            device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+        if (device->hasKeycodeLocked(AKEYCODE_Q)) {
+            device->classes |= InputDeviceClass::ALPHAKEY;
         }
 
         // See if this device has a DPAD.
-        if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
-            hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
-            hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
-            hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
-            hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
-            device->classes |= INPUT_DEVICE_CLASS_DPAD;
+        if (device->hasKeycodeLocked(AKEYCODE_DPAD_UP) &&
+            device->hasKeycodeLocked(AKEYCODE_DPAD_DOWN) &&
+            device->hasKeycodeLocked(AKEYCODE_DPAD_LEFT) &&
+            device->hasKeycodeLocked(AKEYCODE_DPAD_RIGHT) &&
+            device->hasKeycodeLocked(AKEYCODE_DPAD_CENTER)) {
+            device->classes |= InputDeviceClass::DPAD;
         }
 
         // See if this device has a gamepad.
         for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
-            if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
-                device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+            if (device->hasKeycodeLocked(GAMEPAD_KEYCODES[i])) {
+                device->classes |= InputDeviceClass::GAMEPAD;
                 break;
             }
         }
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
-    if (device->classes == 0) {
-        ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+    if (device->classes == Flags<InputDeviceClass>(0)) {
+        ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
               device->identifier.name.c_str());
-        delete device;
         return -1;
     }
 
     // Determine whether the device has a mic.
-    if (deviceHasMicLocked(device)) {
-        device->classes |= INPUT_DEVICE_CLASS_MIC;
+    if (device->deviceHasMicLocked()) {
+        device->classes |= InputDeviceClass::MIC;
     }
 
     // Determine whether the device is external or internal.
-    if (isExternalDeviceLocked(device)) {
-        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
+    if (device->isExternalDeviceLocked()) {
+        device->classes |= InputDeviceClass::EXTERNAL;
     }
 
-    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) &&
-        device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        device->controllerNumber = getNextControllerNumberLocked(device);
-        setLedForControllerLocked(device);
+    if (device->classes.any(InputDeviceClass::JOYSTICK | InputDeviceClass::DPAD) &&
+        device->classes.test(InputDeviceClass::GAMEPAD)) {
+        device->controllerNumber = getNextControllerNumberLocked(device->identifier.name);
+        device->setLedForControllerLocked();
     }
 
-    // Find a matching video device by comparing device names
-    // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
-    for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
-        if (device->identifier.name == videoDevice->getName()) {
-            device->videoDevice = std::move(videoDevice);
-            break;
-        }
-    }
-    mUnattachedVideoDevices
-            .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
-                                  [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
-                                      return videoDevice == nullptr;
-                                  }),
-                   mUnattachedVideoDevices.end());
-
-    if (registerDeviceForEpollLocked(device) != OK) {
-        delete device;
+    if (registerDeviceForEpollLocked(*device) != OK) {
         return -1;
     }
 
-    configureFd(device);
+    device->configureFd();
 
-    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
+    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
           "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
-          deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
-          device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
-          device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
+          deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
+          device->classes.string().c_str(), device->configurationFile.c_str(),
+          device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
+          toString(mBuiltInKeyboardId == deviceId));
 
-    addDeviceLocked(device);
+    addDeviceLocked(std::move(device));
     return OK;
 }
 
-void EventHub::configureFd(Device* device) {
-    // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
-    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        // Disable kernel key repeat since we handle it ourselves
-        unsigned int repeatRate[] = {0, 0};
-        if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
-            ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(),
-                  strerror(errno));
-        }
-    }
-
-    // Tell the kernel that we want to use the monotonic clock for reporting timestamps
-    // associated with input events.  This is important because the input system
-    // uses the timestamps extensively and assumes they were recorded using the monotonic
-    // clock.
-    int clockId = CLOCK_MONOTONIC;
-    bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
-    ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
-}
-
 void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
     std::unique_ptr<TouchVideoDevice> videoDevice = TouchVideoDevice::create(devicePath);
     if (!videoDevice) {
@@ -1527,14 +1567,9 @@
         return;
     }
     // Transfer ownership of this video device to a matching input device
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        Device* device = mDevices.valueAt(i);
-        if (videoDevice->getName() == device->identifier.name) {
-            device->videoDevice = std::move(videoDevice);
-            if (device->enabled) {
-                registerVideoDeviceForEpollLocked(*device->videoDevice);
-            }
-            return;
+    for (const auto& [id, device] : mDevices) {
+        if (tryAddVideoDevice(*device, videoDevice)) {
+            return; // 'device' now owns 'videoDevice'
         }
     }
 
@@ -1545,6 +1580,18 @@
     mUnattachedVideoDevices.push_back(std::move(videoDevice));
 }
 
+bool EventHub::tryAddVideoDevice(EventHub::Device& device,
+                                 std::unique_ptr<TouchVideoDevice>& videoDevice) {
+    if (videoDevice->getName() != device.identifier.name) {
+        return false;
+    }
+    device.videoDevice = std::move(videoDevice);
+    if (device.enabled) {
+        registerVideoDeviceForEpollLocked(*device.videoDevice);
+    }
+    return true;
+}
+
 bool EventHub::isDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
@@ -1572,9 +1619,9 @@
         return result;
     }
 
-    configureFd(device);
+    device->configureFd();
 
-    return registerDeviceForEpollLocked(device);
+    return registerDeviceForEpollLocked(*device);
 }
 
 status_t EventHub::disableDevice(int32_t deviceId) {
@@ -1588,7 +1635,7 @@
         ALOGW("Duplicate call to %s, input device already disabled", __func__);
         return OK;
     }
-    unregisterDeviceFromEpollLocked(device);
+    unregisterDeviceFromEpollLocked(*device);
     return device->disable();
 }
 
@@ -1598,77 +1645,23 @@
     identifier.uniqueId = "<virtual>";
     assignDescriptorLocked(identifier);
 
-    Device* device =
-            new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
-    device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY |
-            INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL;
-    loadKeyMapLocked(device);
-    addDeviceLocked(device);
+    std::unique_ptr<Device> device =
+            std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
+                                     identifier);
+    device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
+            InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL;
+    device->loadKeyMapLocked();
+    addDeviceLocked(std::move(device));
 }
 
-void EventHub::addDeviceLocked(Device* device) {
-    mDevices.add(device->id, device);
-    device->next = mOpeningDevices;
-    mOpeningDevices = device;
+void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+    mOpeningDevices.push_back(std::move(device));
 }
 
-void EventHub::loadConfigurationLocked(Device* device) {
-    device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
-            device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
-    if (device->configurationFile.empty()) {
-        ALOGD("No input device configuration file found for device '%s'.",
-              device->identifier.name.c_str());
-    } else {
-        status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
-                                            &device->configuration);
-        if (status) {
-            ALOGE("Error loading input device configuration file for device '%s'.  "
-                  "Using default configuration.",
-                  device->identifier.name.c_str());
-        }
-    }
-}
-
-bool EventHub::loadVirtualKeyMapLocked(Device* device) {
-    // The virtual key map is supplied by the kernel as a system board property file.
-    std::string path;
-    path += "/sys/board_properties/virtualkeys.";
-    path += device->identifier.getCanonicalName();
-    if (access(path.c_str(), R_OK)) {
-        return false;
-    }
-    device->virtualKeyMap = VirtualKeyMap::load(path);
-    return device->virtualKeyMap != nullptr;
-}
-
-status_t EventHub::loadKeyMapLocked(Device* device) {
-    return device->keyMap.load(device->identifier, device->configuration);
-}
-
-bool EventHub::isExternalDeviceLocked(Device* device) {
-    if (device->configuration) {
-        bool value;
-        if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
-            return !value;
-        }
-    }
-    return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
-}
-
-bool EventHub::deviceHasMicLocked(Device* device) {
-    if (device->configuration) {
-        bool value;
-        if (device->configuration->tryGetProperty(String8("audio.mic"), value)) {
-            return value;
-        }
-    }
-    return false;
-}
-
-int32_t EventHub::getNextControllerNumberLocked(Device* device) {
+int32_t EventHub::getNextControllerNumberLocked(const std::string& name) {
     if (mControllerNumbers.isFull()) {
         ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
-              device->identifier.name.c_str());
+              name.c_str());
         return 0;
     }
     // Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1676,61 +1669,19 @@
     return static_cast<int32_t>(mControllerNumbers.markFirstUnmarkedBit() + 1);
 }
 
-void EventHub::releaseControllerNumberLocked(Device* device) {
-    int32_t num = device->controllerNumber;
-    device->controllerNumber = 0;
-    if (num == 0) {
-        return;
-    }
-    mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
-}
-
-void EventHub::setLedForControllerLocked(Device* device) {
-    for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
-        setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1);
+void EventHub::releaseControllerNumberLocked(int32_t num) {
+    if (num > 0) {
+        mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
     }
 }
 
-bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
-    if (!device->keyMap.haveKeyLayout()) {
-        return false;
-    }
-
-    std::vector<int32_t> scanCodes;
-    device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
-    const size_t N = scanCodes.size();
-    for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
-        int32_t sc = scanCodes[i];
-        if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const {
-    if (!device->keyMap.haveKeyLayout()) {
-        return NAME_NOT_FOUND;
-    }
-
-    int32_t scanCode;
-    if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
-        if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
-            *outScanCode = scanCode;
-            return NO_ERROR;
-        }
-    }
-    return NAME_NOT_FOUND;
-}
-
-void EventHub::closeDeviceByPathLocked(const char* devicePath) {
+void EventHub::closeDeviceByPathLocked(const std::string& devicePath) {
     Device* device = getDeviceByPathLocked(devicePath);
-    if (device) {
-        closeDeviceLocked(device);
+    if (device != nullptr) {
+        closeDeviceLocked(*device);
         return;
     }
-    ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+    ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str());
 }
 
 /**
@@ -1741,8 +1692,7 @@
 void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) {
     // A video device may be owned by an existing input device, or it may be stored in
     // the mUnattachedVideoDevices queue. Check both locations.
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        Device* device = mDevices.valueAt(i);
+    for (const auto& [id, device] : mDevices) {
         if (device->videoDevice && device->videoDevice->getPath() == devicePath) {
             unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
             device->videoDevice = nullptr;
@@ -1760,60 +1710,33 @@
 
 void EventHub::closeAllDevicesLocked() {
     mUnattachedVideoDevices.clear();
-    while (mDevices.size() > 0) {
-        closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
+    while (!mDevices.empty()) {
+        closeDeviceLocked(*(mDevices.begin()->second));
     }
 }
 
-void EventHub::closeDeviceLocked(Device* device) {
-    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
-          device->identifier.name.c_str(), device->id, device->fd, device->classes);
+void EventHub::closeDeviceLocked(Device& device) {
+    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=%s", device.path.c_str(),
+          device.identifier.name.c_str(), device.id, device.fd, device.classes.string().c_str());
 
-    if (device->id == mBuiltInKeyboardId) {
+    if (device.id == mBuiltInKeyboardId) {
         ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
-              device->path.c_str(), mBuiltInKeyboardId);
+              device.path.c_str(), mBuiltInKeyboardId);
         mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
     }
 
     unregisterDeviceFromEpollLocked(device);
-    if (device->videoDevice) {
+    if (device.videoDevice) {
         // This must be done after the video device is removed from epoll
-        mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
+        mUnattachedVideoDevices.push_back(std::move(device.videoDevice));
     }
 
-    releaseControllerNumberLocked(device);
+    releaseControllerNumberLocked(device.controllerNumber);
+    device.controllerNumber = 0;
+    device.close();
+    mClosingDevices.push_back(std::move(mDevices[device.id]));
 
-    mDevices.removeItem(device->id);
-    device->close();
-
-    // Unlink for opening devices list if it is present.
-    Device* pred = nullptr;
-    bool found = false;
-    for (Device* entry = mOpeningDevices; entry != nullptr;) {
-        if (entry == device) {
-            found = true;
-            break;
-        }
-        pred = entry;
-        entry = entry->next;
-    }
-    if (found) {
-        // Unlink the device from the opening devices list then delete it.
-        // We don't need to tell the client that the device was closed because
-        // it does not even know it was opened in the first place.
-        ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
-        if (pred) {
-            pred->next = device->next;
-        } else {
-            mOpeningDevices = device->next;
-        }
-        delete device;
-    } else {
-        // Link into closing devices list.
-        // The device will be deleted later after we have informed the client.
-        device->next = mClosingDevices;
-        mClosingDevices = device;
-    }
+    mDevices.erase(device.id);
 }
 
 status_t EventHub::readNotifyLocked() {
@@ -1835,16 +1758,16 @@
         event = (struct inotify_event*)(event_buf + event_pos);
         if (event->len) {
             if (event->wd == mInputWd) {
-                std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+                std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
                 if (event->mask & IN_CREATE) {
-                    openDeviceLocked(filename.c_str());
+                    openDeviceLocked(filename);
                 } else {
                     ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
-                    closeDeviceByPathLocked(filename.c_str());
+                    closeDeviceByPathLocked(filename);
                 }
             } else if (event->wd == mVideoWd) {
                 if (isV4lTouchNode(event->name)) {
-                    std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+                    std::string filename = std::string(VIDEO_DEVICE_PATH) + "/" + event->name;
                     if (event->mask & IN_CREATE) {
                         openVideoDeviceLocked(filename);
                     } else {
@@ -1863,24 +1786,10 @@
     return 0;
 }
 
-status_t EventHub::scanDirLocked(const char* dirname) {
-    char devname[PATH_MAX];
-    char* filename;
-    DIR* dir;
-    struct dirent* de;
-    dir = opendir(dirname);
-    if (dir == nullptr) return -1;
-    strcpy(devname, dirname);
-    filename = devname + strlen(devname);
-    *filename++ = '/';
-    while ((de = readdir(dir))) {
-        if (de->d_name[0] == '.' &&
-            (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
-            continue;
-        strcpy(filename, de->d_name);
-        openDeviceLocked(devname);
+status_t EventHub::scanDirLocked(const std::string& dirname) {
+    for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+        openDeviceLocked(entry.path());
     }
-    closedir(dir);
     return 0;
 }
 
@@ -1888,22 +1797,12 @@
  * Look for all dirname/v4l-touch* devices, and open them.
  */
 status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
-    DIR* dir;
-    struct dirent* de;
-    dir = opendir(dirname.c_str());
-    if (!dir) {
-        ALOGE("Could not open video directory %s", dirname.c_str());
-        return BAD_VALUE;
-    }
-
-    while ((de = readdir(dir))) {
-        const char* name = de->d_name;
-        if (isV4lTouchNode(name)) {
-            ALOGI("Found touch video device %s", name);
-            openVideoDeviceLocked(dirname + "/" + name);
+    for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+        if (isV4lTouchNode(entry.path())) {
+            ALOGI("Found touch video device %s", entry.path().c_str());
+            openVideoDeviceLocked(entry.path());
         }
     }
-    closedir(dir);
     return OK;
 }
 
@@ -1924,8 +1823,7 @@
 
         dump += INDENT "Devices:\n";
 
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            const Device* device = mDevices.valueAt(i);
+        for (const auto& [id, device] : mDevices) {
             if (mBuiltInKeyboardId == device->id) {
                 dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
                                      device->id, device->identifier.name.c_str());
@@ -1933,7 +1831,7 @@
                 dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
                                      device->identifier.name.c_str());
             }
-            dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
+            dump += StringPrintf(INDENT3 "Classes: %s\n", device->classes.string().c_str());
             dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
             dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled));
             dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str());
@@ -1950,8 +1848,6 @@
                                  device->keyMap.keyCharacterMapFile.c_str());
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
                                  device->configurationFile.c_str());
-            dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
-                                 toString(device->overlayKeyMap != nullptr));
             dump += INDENT3 "VideoDevice: ";
             if (device->videoDevice) {
                 dump += device->videoDevice->dump() + "\n";
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 4b19e5e..271bc2f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,6 +18,7 @@
 
 #include "InputDevice.h"
 
+#include <input/Flags.h>
 #include <algorithm>
 
 #include "CursorInputMapper.h"
@@ -84,12 +85,13 @@
     bumpGeneration();
 }
 
-void InputDevice::dump(std::string& dump) {
+void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
     InputDeviceInfo deviceInfo;
     getDeviceInfo(&deviceInfo);
 
     dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
                          deviceInfo.getDisplayName().c_str());
+    dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
     dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
     dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
     dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
@@ -101,13 +103,14 @@
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
     dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+    dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
 
     const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     if (!ranges.empty()) {
         dump += INDENT2 "Motion Ranges:\n";
         for (size_t i = 0; i < ranges.size(); i++) {
             const InputDeviceInfo::MotionRange& range = ranges[i];
-            const char* label = getAxisLabel(range.axis);
+            const char* label = InputEventLookup::getAxisLabel(range.axis);
             char name[32];
             if (label) {
                 strncpy(name, label, sizeof(name));
@@ -131,7 +134,7 @@
         return;
     }
     std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
-    uint32_t classes = contextPtr->getDeviceClasses();
+    Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();
     std::vector<std::unique_ptr<InputMapper>> mappers;
 
     // Check if we should skip population
@@ -141,33 +144,33 @@
     }
 
     // Switch-like devices.
-    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+    if (classes.test(InputDeviceClass::SWITCH)) {
         mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));
     }
 
     // Scroll wheel-like devices.
-    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+    if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
         mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));
     }
 
     // Vibrator-like devices.
-    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+    if (classes.test(InputDeviceClass::VIBRATOR)) {
         mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
     }
 
     // Keyboard-like devices.
     uint32_t keyboardSource = 0;
     int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+    if (classes.test(InputDeviceClass::KEYBOARD)) {
         keyboardSource |= AINPUT_SOURCE_KEYBOARD;
     }
-    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+    if (classes.test(InputDeviceClass::ALPHAKEY)) {
         keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
     }
-    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+    if (classes.test(InputDeviceClass::DPAD)) {
         keyboardSource |= AINPUT_SOURCE_DPAD;
     }
-    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+    if (classes.test(InputDeviceClass::GAMEPAD)) {
         keyboardSource |= AINPUT_SOURCE_GAMEPAD;
     }
 
@@ -177,29 +180,31 @@
     }
 
     // Cursor-like devices.
-    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+    if (classes.test(InputDeviceClass::CURSOR)) {
         mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));
     }
 
     // Touchscreens and touchpad devices.
-    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+    if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
-    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+    } else if (classes.test(InputDeviceClass::TOUCH)) {
         mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
     }
 
     // Joystick-like devices.
-    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+    if (classes.test(InputDeviceClass::JOYSTICK)) {
         mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
     }
 
     // External stylus-like devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
         mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
     }
 
     // insert the context into the devices set
     mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+    // Must change generation to flag this device as changed
+    bumpGeneration();
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
@@ -209,7 +214,7 @@
 void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                             uint32_t changes) {
     mSources = 0;
-    mClasses = 0;
+    mClasses = Flags<InputDeviceClass>(0);
     mControllerNumber = 0;
 
     for_each_subdevice([this](InputDeviceContext& context) {
@@ -224,8 +229,8 @@
         }
     });
 
-    mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
-    mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
+    mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
+    mHasMic = mClasses.test(InputDeviceClass::MIC);
 
     if (!isIgnored()) {
         if (!changes) { // first time only
@@ -238,8 +243,8 @@
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
-            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
-                sp<KeyCharacterMap> keyboardLayout =
+            if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
+                std::shared_ptr<KeyCharacterMap> keyboardLayout =
                         mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
                 bool shouldBumpGeneration = false;
                 for_each_subdevice(
@@ -255,7 +260,7 @@
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
-            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+            if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
                 std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
                 if (mAlias != alias) {
                     mAlias = alias;
@@ -424,10 +429,10 @@
     return result;
 }
 
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
                           int32_t token) {
-    for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
-        mapper.vibrate(pattern, patternSize, repeat, token);
+    for_each_mapper([pattern, repeat, token](InputMapper& mapper) {
+        mapper.vibrate(pattern, repeat, token);
     });
 }
 
@@ -480,6 +485,10 @@
     return count;
 }
 
+void InputDevice::updateLedState(bool reset) {
+    for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 657a134..2028b91 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -47,6 +47,7 @@
         mEventHub(eventHub),
         mPolicy(policy),
         mGlobalMetaState(0),
+        mLedMetaState(AMETA_NUM_LOCK_ON),
         mGeneration(1),
         mNextInputDeviceId(END_RESERVED_ID),
         mDisableVirtualKeysTimeout(LLONG_MIN),
@@ -86,7 +87,6 @@
     int32_t oldGeneration;
     int32_t timeoutMillis;
     bool inputDevicesChanged = false;
-    std::vector<InputDeviceInfo> inputDevices;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -127,13 +127,12 @@
 
         if (oldGeneration != mGeneration) {
             inputDevicesChanged = true;
-            getInputDevicesLocked(inputDevices);
         }
     } // release lock
 
     // Send out a message that the describes the changed input devices.
     if (inputDevicesChanged) {
-        mPolicy->notifyInputDevicesChanged(inputDevices);
+        mPolicy->notifyInputDevicesChanged(getInputDevicesLocked());
     }
 
     // Flush queued events out to the listener.
@@ -206,9 +205,17 @@
     }
 
     mDevices.emplace(eventHubId, device);
+    // Add device to device to EventHub ids map.
+    const auto mapIt = mDeviceToEventHubIdsMap.find(device);
+    if (mapIt == mDeviceToEventHubIdsMap.end()) {
+        std::vector<int32_t> ids = {eventHubId};
+        mDeviceToEventHubIdsMap.emplace(device, ids);
+    } else {
+        mapIt->second.push_back(eventHubId);
+    }
     bumpGenerationLocked();
 
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
         notifyExternalStylusPresenceChanged();
     }
 }
@@ -222,6 +229,17 @@
 
     std::shared_ptr<InputDevice> device = std::move(deviceIt->second);
     mDevices.erase(deviceIt);
+    // Erase device from device to EventHub ids map.
+    auto mapIt = mDeviceToEventHubIdsMap.find(device);
+    if (mapIt != mDeviceToEventHubIdsMap.end()) {
+        std::vector<int32_t>& eventHubIds = mapIt->second;
+        eventHubIds.erase(std::remove_if(eventHubIds.begin(), eventHubIds.end(),
+                                         [eventHubId](int32_t eId) { return eId == eventHubId; }),
+                          eventHubIds.end());
+        if (eventHubIds.size() == 0) {
+            mDeviceToEventHubIdsMap.erase(mapIt);
+        }
+    }
     bumpGenerationLocked();
 
     if (device->isIgnored()) {
@@ -237,7 +255,7 @@
 
     device->removeEventHubDevice(eventHubId);
 
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
         notifyExternalStylusPresenceChanged();
     }
 
@@ -353,6 +371,18 @@
     return mGlobalMetaState;
 }
 
+void InputReader::updateLedMetaStateLocked(int32_t metaState) {
+    mLedMetaState = metaState;
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
+        device->updateLedState(false);
+    }
+}
+
+int32_t InputReader::getLedMetaStateLocked() {
+    return mLedMetaState;
+}
+
 void InputReader::notifyExternalStylusPresenceChanged() {
     refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
 }
@@ -360,7 +390,7 @@
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
-        if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+        if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS) && !device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
             outDevices.push_back(info);
@@ -390,8 +420,9 @@
     }
 }
 
-sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
-    sp<PointerControllerInterface> controller = mPointerController.promote();
+std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked(
+        int32_t deviceId) {
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
     if (controller == nullptr) {
         controller = mPolicy->obtainPointerController(deviceId);
         mPointerController = controller;
@@ -401,7 +432,7 @@
 }
 
 void InputReader::updatePointerDisplayLocked() {
-    sp<PointerControllerInterface> controller = mPointerController.promote();
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
     if (controller == nullptr) {
         return;
     }
@@ -424,9 +455,9 @@
 }
 
 void InputReader::fadePointerLocked() {
-    sp<PointerControllerInterface> controller = mPointerController.promote();
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
     if (controller != nullptr) {
-        controller->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        controller->fade(PointerControllerInterface::Transition::GRADUAL);
     }
 }
 
@@ -441,22 +472,23 @@
     return ++mGeneration;
 }
 
-void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
+std::vector<InputDeviceInfo> InputReader::getInputDevices() const {
     AutoMutex _l(mLock);
-    getInputDevicesLocked(outInputDevices);
+    return getInputDevicesLocked();
 }
 
-void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
-    outInputDevices.clear();
+std::vector<InputDeviceInfo> InputReader::getInputDevicesLocked() const {
+    std::vector<InputDeviceInfo> outInputDevices;
+    outInputDevices.reserve(mDeviceToEventHubIdsMap.size());
 
-    for (auto& devicePair : mDevices) {
-        std::shared_ptr<InputDevice>& device = devicePair.second;
+    for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) {
         if (!device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
             outInputDevices.push_back(info);
         }
     }
+    return outInputDevices;
 }
 
 int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
@@ -558,12 +590,12 @@
     }
 }
 
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
                           ssize_t repeat, int32_t token) {
     AutoMutex _l(mLock);
     InputDevice* device = findInputDevice(deviceId);
     if (device) {
-        device->vibrate(pattern, patternSize, repeat, token);
+        device->vibrate(pattern, repeat, token);
     }
 }
 
@@ -621,11 +653,17 @@
     mEventHub->dump(dump);
     dump += "\n";
 
-    dump += "Input Reader State:\n";
+    dump += StringPrintf("Input Reader State (Nums of device: %zu):\n",
+                         mDeviceToEventHubIdsMap.size());
 
-    for (const auto& devicePair : mDevices) {
-        const std::shared_ptr<InputDevice>& device = devicePair.second;
-        device->dump(dump);
+    for (const auto& devicePair : mDeviceToEventHubIdsMap) {
+        const std::shared_ptr<InputDevice>& device = devicePair.first;
+        std::string eventHubDevStr = INDENT "EventHub Devices: [ ";
+        for (const auto& eId : devicePair.second) {
+            eventHubDevStr += StringPrintf("%d ", eId);
+        }
+        eventHubDevStr += "] \n";
+        device->dump(dump, eventHubDevStr);
     }
 
     dump += INDENT "Configuration:\n";
@@ -709,6 +747,16 @@
     return mReader->getGlobalMetaStateLocked();
 }
 
+void InputReader::ContextImpl::updateLedMetaState(int32_t metaState) {
+    // lock is already held by the input loop
+    mReader->updateLedMetaStateLocked(metaState);
+}
+
+int32_t InputReader::ContextImpl::getLedMetaState() {
+    // lock is already held by the input loop
+    return mReader->getLedMetaStateLocked();
+}
+
 void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
     // lock is already held by the input loop
     mReader->disableVirtualKeysUntilLocked(time);
@@ -725,7 +773,8 @@
     mReader->fadePointerLocked();
 }
 
-sp<PointerControllerInterface> InputReader::ContextImpl::getPointerController(int32_t deviceId) {
+std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController(
+        int32_t deviceId) {
     // lock is already held by the input loop
     return mReader->getPointerControllerLocked(deviceId);
 }
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index c17f3a1..edb82d3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -17,31 +17,30 @@
 #ifndef _RUNTIME_EVENT_HUB_H
 #define _RUNTIME_EVENT_HUB_H
 
+#include <bitset>
+#include <climits>
+#include <unordered_map>
 #include <vector>
 
+#include <input/Flags.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
+#include <input/PropertyMap.h>
 #include <input/VirtualKeyMap.h>
+#include <linux/input.h>
+#include <sys/epoll.h>
 #include <utils/BitSet.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/List.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
-#include <utils/PropertyMap.h>
-
-#include <linux/input.h>
-#include <sys/epoll.h>
 
 #include "TouchVideoDevice.h"
-
-/* Convenience constants. */
-
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f  // last button code
+#include "VibrationElement.h"
 
 namespace android {
 
@@ -79,58 +78,58 @@
 /*
  * Input device classes.
  */
-enum {
+enum class InputDeviceClass : uint32_t {
     /* The input device is a keyboard or has buttons. */
-    INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
+    KEYBOARD = 0x00000001,
 
     /* The input device is an alpha-numeric keyboard (not just a dial pad). */
-    INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
+    ALPHAKEY = 0x00000002,
 
     /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
-    INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
+    TOUCH = 0x00000004,
 
     /* The input device is a cursor device such as a trackball or mouse. */
-    INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
+    CURSOR = 0x00000008,
 
     /* The input device is a multi-touch touchscreen. */
-    INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
+    TOUCH_MT = 0x00000010,
 
     /* The input device is a directional pad (implies keyboard, has DPAD keys). */
-    INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+    DPAD = 0x00000020,
 
     /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
-    INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+    GAMEPAD = 0x00000040,
 
     /* The input device has switches. */
-    INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+    SWITCH = 0x00000080,
 
     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
-    INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+    JOYSTICK = 0x00000100,
 
     /* The input device has a vibrator (supports FF_RUMBLE). */
-    INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+    VIBRATOR = 0x00000200,
 
     /* The input device has a microphone. */
-    INPUT_DEVICE_CLASS_MIC = 0x00000400,
+    MIC = 0x00000400,
 
     /* The input device is an external stylus (has data we want to fuse with touch data). */
-    INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800,
+    EXTERNAL_STYLUS = 0x00000800,
 
     /* The input device has a rotary encoder */
-    INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000,
+    ROTARY_ENCODER = 0x00001000,
 
     /* The input device is virtual (not a real device, not part of UI configuration). */
-    INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
+    VIRTUAL = 0x40000000,
 
     /* The input device is external (not built-in). */
-    INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
+    EXTERNAL = 0x80000000,
 };
 
 /*
  * Gets the class that owns an axis, in cases where multiple classes might claim
  * the same axis for different purposes.
  */
-extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
+extern Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses);
 
 /*
  * Grand Central Station for events.
@@ -163,7 +162,7 @@
         FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
     };
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
+    virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const = 0;
 
     virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
 
@@ -227,11 +226,12 @@
     virtual void getVirtualKeyDefinitions(
             int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
+    virtual const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                          std::shared_ptr<KeyCharacterMap> map) = 0;
 
     /* Control the vibrator. */
-    virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+    virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
     virtual void cancelVibrate(int32_t deviceId) = 0;
 
     /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
@@ -256,73 +256,150 @@
     virtual status_t disableDevice(int32_t deviceId) = 0;
 };
 
+template <std::size_t BITS>
+class BitArray {
+    /* Array element type and vector of element type. */
+    using Element = std::uint32_t;
+    /* Number of bits in each BitArray element. */
+    static constexpr size_t WIDTH = sizeof(Element) * CHAR_BIT;
+    /* Number of elements to represent a bit array of the specified size of bits. */
+    static constexpr size_t COUNT = (BITS + WIDTH - 1) / WIDTH;
+
+public:
+    /* BUFFER type declaration for BitArray */
+    using Buffer = std::array<Element, COUNT>;
+    /* To tell if a bit is set in array, it selects an element from the array, and test
+     * if the relevant bit set.
+     * Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
+     */
+    inline bool test(size_t bit) const {
+        return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+    }
+    /* Returns total number of bytes needed for the array */
+    inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
+    /* Returns true if array contains any non-zero bit from the range defined by start and end
+     * bit index [startIndex, endIndex).
+     */
+    bool any(size_t startIndex, size_t endIndex) {
+        if (startIndex >= endIndex || startIndex > BITS || endIndex > BITS + 1) {
+            ALOGE("Invalid start/end index. start = %zu, end = %zu, total bits = %zu", startIndex,
+                  endIndex, BITS);
+            return false;
+        }
+        size_t se = startIndex / WIDTH; // Start of element
+        size_t ee = endIndex / WIDTH;   // End of element
+        size_t si = startIndex % WIDTH; // Start index in start element
+        size_t ei = endIndex % WIDTH;   // End index in end element
+        // Need to check first unaligned bitset for any non zero bit
+        if (si > 0) {
+            size_t nBits = se == ee ? ei - si : WIDTH - si;
+            // Generate the mask of interested bit range
+            Element mask = ((1 << nBits) - 1) << si;
+            if (mData[se++].to_ulong() & mask) {
+                return true;
+            }
+        }
+        // Check whole bitset for any bit set
+        for (; se < ee; se++) {
+            if (mData[se].any()) {
+                return true;
+            }
+        }
+        // Need to check last unaligned bitset for any non zero bit
+        if (ei > 0 && se <= ee) {
+            // Generate the mask of interested bit range
+            Element mask = (1 << ei) - 1;
+            if (mData[se].to_ulong() & mask) {
+                return true;
+            }
+        }
+        return false;
+    }
+    /* Load bit array values from buffer */
+    void loadFromBuffer(const Buffer& buffer) {
+        for (size_t i = 0; i < COUNT; i++) {
+            mData[i] = std::bitset<WIDTH>(buffer[i]);
+        }
+    }
+
+private:
+    std::array<std::bitset<WIDTH>, COUNT> mData;
+};
+
 class EventHub : public EventHubInterface {
 public:
     EventHub();
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
+    Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override final;
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override final;
 
-    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
+    int32_t getDeviceControllerNumber(int32_t deviceId) const override final;
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final;
 
-    virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-                                         RawAbsoluteAxisInfo* outAxisInfo) const override;
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override final;
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override final;
 
-    virtual bool hasInputProperty(int32_t deviceId, int property) const override;
+    bool hasInputProperty(int32_t deviceId, int property) const override final;
 
-    virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
-                            int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
-                            uint32_t* outFlags) const override;
+    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                    int32_t* outKeycode, int32_t* outMetaState,
+                    uint32_t* outFlags) const override final;
 
-    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
-                             AxisInfo* outAxisInfo) const override;
+    status_t mapAxis(int32_t deviceId, int32_t scanCode,
+                     AxisInfo* outAxisInfo) const override final;
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
+    void setExcludedDevices(const std::vector<std::string>& devices) override final;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
-                                          int32_t* outValue) const override;
+    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;
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                  int32_t* outValue) const override final;
 
-    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-                                       uint8_t* outFlags) const override;
+    bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags) const override final;
 
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
-    virtual bool hasLed(int32_t deviceId, int32_t led) const override;
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
+    bool hasLed(int32_t deviceId, int32_t led) const override final;
+    void setLedState(int32_t deviceId, int32_t led, bool on) override final;
 
-    virtual void getVirtualKeyDefinitions(
-            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+    void getVirtualKeyDefinitions(
+            int32_t deviceId,
+            std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
-                                          const sp<KeyCharacterMap>& map) override;
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(
+            int32_t deviceId) const override final;
+    bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                  std::shared_ptr<KeyCharacterMap> map) override final;
 
-    virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
-    virtual void cancelVibrate(int32_t deviceId) override;
+    void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
+    void cancelVibrate(int32_t deviceId) override final;
 
-    virtual void requestReopenDevices() override;
+    void requestReopenDevices() override final;
 
-    virtual void wake() override;
+    void wake() override final;
 
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
+    void dump(std::string& dump) override final;
 
-    virtual ~EventHub() override;
+    void monitor() override final;
+
+    bool isDeviceEnabled(int32_t deviceId) override final;
+
+    status_t enableDevice(int32_t deviceId) override final;
+
+    status_t disableDevice(int32_t deviceId) override final;
+
+    ~EventHub() override;
 
 private:
     struct Device {
-        Device* next;
-
         int fd; // may be -1 if device is closed
         const int32_t id;
         const std::string path;
@@ -330,24 +407,23 @@
 
         std::unique_ptr<TouchVideoDevice> videoDevice;
 
-        uint32_t classes;
+        Flags<InputDeviceClass> classes;
 
-        uint8_t keyBitmask[(KEY_MAX + 1) / 8];
-        uint8_t absBitmask[(ABS_MAX + 1) / 8];
-        uint8_t relBitmask[(REL_MAX + 1) / 8];
-        uint8_t swBitmask[(SW_MAX + 1) / 8];
-        uint8_t ledBitmask[(LED_MAX + 1) / 8];
-        uint8_t ffBitmask[(FF_MAX + 1) / 8];
-        uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
+        BitArray<KEY_MAX> keyBitmask;
+        BitArray<KEY_MAX> keyState;
+        BitArray<ABS_MAX> absBitmask;
+        BitArray<REL_MAX> relBitmask;
+        BitArray<SW_MAX> swBitmask;
+        BitArray<SW_MAX> swState;
+        BitArray<LED_MAX> ledBitmask;
+        BitArray<FF_MAX> ffBitmask;
+        BitArray<INPUT_PROP_MAX> propBitmask;
 
         std::string configurationFile;
-        PropertyMap* configuration;
+        std::unique_ptr<PropertyMap> configuration;
         std::unique_ptr<VirtualKeyMap> virtualKeyMap;
         KeyMap keyMap;
 
-        sp<KeyCharacterMap> overlayKeyMap;
-        sp<KeyCharacterMap> combinedKeyMap;
-
         bool ffEffectPlaying;
         int16_t ffEffectId; // initially -1
 
@@ -362,69 +438,68 @@
         bool enabled; // initially true
         status_t enable();
         status_t disable();
-        bool hasValidFd();
+        bool hasValidFd() const;
         const bool isVirtual; // set if fd < 0 is passed to constructor
 
-        const sp<KeyCharacterMap>& getKeyCharacterMap() const {
-            if (combinedKeyMap != nullptr) {
-                return combinedKeyMap;
-            }
-            return keyMap.keyCharacterMap;
-        }
+        const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const;
+
+        template <std::size_t N>
+        status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
+
+        void configureFd();
+        bool hasKeycodeLocked(int keycode) const;
+        void loadConfigurationLocked();
+        bool loadVirtualKeyMapLocked();
+        status_t loadKeyMapLocked();
+        bool isExternalDeviceLocked();
+        bool deviceHasMicLocked();
+        void setLedForControllerLocked();
+        status_t mapLed(int32_t led, int32_t* outScanCode) const;
+        void setLedStateLocked(int32_t led, bool on);
     };
 
-    status_t openDeviceLocked(const char* devicePath);
+    status_t openDeviceLocked(const std::string& devicePath);
     void openVideoDeviceLocked(const std::string& devicePath);
+    /**
+     * Try to associate a video device with an input device. If the association succeeds,
+     * the videoDevice is moved into the input device. 'videoDevice' will become null if this
+     * happens.
+     * Return true if the association succeeds.
+     * Return false otherwise.
+     */
+    bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
     void createVirtualKeyboardLocked();
-    void addDeviceLocked(Device* device);
+    void addDeviceLocked(std::unique_ptr<Device> device);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier);
 
-    void closeDeviceByPathLocked(const char* devicePath);
+    void closeDeviceByPathLocked(const std::string& devicePath);
     void closeVideoDeviceByPathLocked(const std::string& devicePath);
-    void closeDeviceLocked(Device* device);
+    void closeDeviceLocked(Device& device);
     void closeAllDevicesLocked();
 
-    void configureFd(Device* device);
-
-    bool isDeviceEnabled(int32_t deviceId) override;
-    status_t enableDevice(int32_t deviceId) override;
-    status_t disableDevice(int32_t deviceId) override;
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
-    status_t registerDeviceForEpollLocked(Device* device);
+    status_t registerDeviceForEpollLocked(Device& device);
     void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
-    status_t unregisterDeviceFromEpollLocked(Device* device);
+    status_t unregisterDeviceFromEpollLocked(Device& device);
     void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
 
-    status_t scanDirLocked(const char* dirname);
+    status_t scanDirLocked(const std::string& dirname);
     status_t scanVideoDirLocked(const std::string& dirname);
     void scanDevicesLocked();
     status_t readNotifyLocked();
 
     Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
     Device* getDeviceLocked(int32_t deviceId) const;
-    Device* getDeviceByPathLocked(const char* devicePath) const;
+    Device* getDeviceByPathLocked(const std::string& devicePath) const;
     /**
      * Look through all available fd's (both for input devices and for video devices),
      * and return the device pointer.
      */
     Device* getDeviceByFdLocked(int fd) const;
 
-    bool hasKeycodeLocked(Device* device, int keycode) const;
-
-    void loadConfigurationLocked(Device* device);
-    bool loadVirtualKeyMapLocked(Device* device);
-    status_t loadKeyMapLocked(Device* device);
-
-    bool isExternalDeviceLocked(Device* device);
-    bool deviceHasMicLocked(Device* device);
-
-    int32_t getNextControllerNumberLocked(Device* device);
-    void releaseControllerNumberLocked(Device* device);
-    void setLedForControllerLocked(Device* device);
-
-    status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
-    void setLedStateLocked(Device* device, int32_t led, bool on);
+    int32_t getNextControllerNumberLocked(const std::string& name);
+    void releaseControllerNumberLocked(int32_t num);
 
     // Protect all internal state.
     mutable Mutex mLock;
@@ -442,7 +517,7 @@
 
     BitSet32 mControllerNumbers;
 
-    KeyedVector<int32_t, Device*> mDevices;
+    std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;
     /**
      * Video devices that report touchscreen heatmap, but have not (yet) been paired
      * with a specific input device. Video device discovery is independent from input device
@@ -452,8 +527,8 @@
      */
     std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
 
-    Device* mOpeningDevices;
-    Device* mClosingDevices;
+    std::vector<std::unique_ptr<Device>> mOpeningDevices;
+    std::vector<std::unique_ptr<Device>> mClosingDevices;
 
     bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 71313fc..7d160eb 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -18,9 +18,10 @@
 #define _UI_INPUTREADER_INPUT_DEVICE_H
 
 #include <input/DisplayViewport.h>
+#include <input/Flags.h>
 #include <input/InputDevice.h>
+#include <input/PropertyMap.h>
 #include <stdint.h>
-#include <utils/PropertyMap.h>
 
 #include <optional>
 #include <unordered_map>
@@ -48,7 +49,7 @@
     inline int32_t getGeneration() const { return mGeneration; }
     inline const std::string getName() const { return mIdentifier.name; }
     inline const std::string getDescriptor() { return mIdentifier.descriptor; }
-    inline uint32_t getClasses() const { return mClasses; }
+    inline Flags<InputDeviceClass> getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
     inline bool hasEventHubDevices() const { return !mDevices.empty(); }
 
@@ -66,7 +67,7 @@
     bool isEnabled();
     void setEnabled(bool enabled, nsecs_t when);
 
-    void dump(std::string& dump);
+    void dump(std::string& dump, const std::string& eventHubDevStr);
     void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
     void removeEventHubDevice(int32_t eventHubId);
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
@@ -81,7 +82,7 @@
     int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
     bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
                                uint8_t* outFlags);
-    void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+    void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token);
     void cancelVibrate(int32_t token);
     void cancelTouch(nsecs_t when);
 
@@ -97,6 +98,8 @@
 
     std::optional<int32_t> getAssociatedDisplayId();
 
+    void updateLedState(bool reset);
+
     size_t getMapperCount();
 
     // construct and add a mapper to the input device
@@ -121,7 +124,7 @@
     int32_t mControllerNumber;
     InputDeviceIdentifier mIdentifier;
     std::string mAlias;
-    uint32_t mClasses;
+    Flags<InputDeviceClass> mClasses;
 
     // map from eventHubId to device context and mappers
     using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
@@ -206,7 +209,9 @@
     inline int32_t getId() { return mDeviceId; }
     inline int32_t getEventHubId() { return mId; }
 
-    inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); }
+    inline Flags<InputDeviceClass> getDeviceClasses() const {
+        return mEventHub->getDeviceClasses(mId);
+    }
     inline InputDeviceIdentifier getDeviceIdentifier() const {
         return mEventHub->getDeviceIdentifier(mId);
     }
@@ -256,13 +261,15 @@
     inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
         return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys);
     }
-    inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+    inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
         return mEventHub->getKeyCharacterMap(mId);
     }
-    inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
+    inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) {
         return mEventHub->setKeyboardLayoutOverlay(mId, map);
     }
-    inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); }
+    inline void vibrate(const VibrationElement& element) {
+        return mEventHub->vibrate(mId, element);
+    }
     inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
 
     inline bool hasAbsoluteAxis(int32_t code) const {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 693ec30..563018a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -17,19 +17,20 @@
 #ifndef _UI_INPUTREADER_INPUT_READER_H
 #define _UI_INPUTREADER_INPUT_READER_H
 
+#include <PointerControllerInterface.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
 #include "EventHub.h"
 #include "InputListener.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
 #include "InputThread.h"
 
-#include <PointerControllerInterface.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-
-#include <unordered_map>
-#include <vector>
-
 namespace android {
 
 class InputDevice;
@@ -54,34 +55,32 @@
                 const sp<InputListenerInterface>& listener);
     virtual ~InputReader();
 
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
+    void dump(std::string& dump) override;
+    void monitor() override;
 
-    virtual status_t start() override;
-    virtual status_t stop() override;
+    status_t start() override;
+    status_t stop() override;
 
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+    std::vector<InputDeviceInfo> getInputDevices() const override;
 
-    virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+    bool isInputDeviceEnabled(int32_t deviceId) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-                                     int32_t scanCode) override;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-                                    int32_t keyCode) override;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+    int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override;
+    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;
 
-    virtual void toggleCapsLockState(int32_t deviceId) override;
+    void toggleCapsLockState(int32_t deviceId) override;
 
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-                         const int32_t* keyCodes, uint8_t* outFlags) override;
+    bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+                 uint8_t* outFlags) override;
 
-    virtual void requestRefreshConfiguration(uint32_t changes) override;
+    void requestRefreshConfiguration(uint32_t changes) override;
 
-    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-                         ssize_t repeat, int32_t token) override;
-    virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+    void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat,
+                 int32_t token) override;
+    void cancelVibrate(int32_t deviceId, int32_t token) override;
 
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+    bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
 
 protected:
     // These members are protected so they can be instrumented by test cases.
@@ -99,20 +98,22 @@
     public:
         explicit ContextImpl(InputReader* reader);
 
-        virtual void updateGlobalMetaState() override;
-        virtual int32_t getGlobalMetaState() override;
-        virtual void disableVirtualKeysUntil(nsecs_t time) override;
-        virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
-        virtual void fadePointer() override;
-        virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) override;
-        virtual void requestTimeoutAtTime(nsecs_t when) override;
-        virtual int32_t bumpGeneration() override;
-        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
-        virtual void dispatchExternalStylusState(const StylusState& outState) override;
-        virtual InputReaderPolicyInterface* getPolicy() override;
-        virtual InputListenerInterface* getListener() override;
-        virtual EventHubInterface* getEventHub() override;
-        virtual int32_t getNextId() override;
+        void updateGlobalMetaState() override;
+        int32_t getGlobalMetaState() override;
+        void disableVirtualKeysUntil(nsecs_t time) override;
+        bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
+        void fadePointer() override;
+        std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override;
+        void requestTimeoutAtTime(nsecs_t when) override;
+        int32_t bumpGeneration() override;
+        void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+        void dispatchExternalStylusState(const StylusState& outState) override;
+        InputReaderPolicyInterface* getPolicy() override;
+        InputListenerInterface* getListener() override;
+        EventHubInterface* getEventHub() override;
+        int32_t getNextId() override;
+        void updateLedMetaState(int32_t metaState) override;
+        int32_t getLedMetaState() override;
     } mContext;
 
     friend class ContextImpl;
@@ -120,7 +121,7 @@
 private:
     std::unique_ptr<InputThread> mThread;
 
-    Mutex mLock;
+    mutable Mutex mLock;
 
     Condition mReaderIsAliveCondition;
 
@@ -141,6 +142,11 @@
     // to lookup the input device instance from the EventHub device id.
     std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
 
+    // An input device contains one or more eventHubId, this map provides a way to lookup the
+    // EventHubIds contained in the input device from the input device instance.
+    std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
+            mDeviceToEventHubIdsMap;
+
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
 
@@ -155,13 +161,17 @@
     void updateGlobalMetaStateLocked();
     int32_t getGlobalMetaStateLocked();
 
+    int32_t mLedMetaState;
+    void updateLedMetaStateLocked(int32_t metaState);
+    int32_t getLedMetaStateLocked();
+
     void notifyExternalStylusPresenceChanged();
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
     void dispatchExternalStylusState(const StylusState& state);
 
     // The PointerController that is shared among all the input devices that need it.
-    wp<PointerControllerInterface> mPointerController;
-    sp<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
+    std::weak_ptr<PointerControllerInterface> mPointerController;
+    std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
     void updatePointerDisplayLocked();
     void fadePointerLocked();
 
@@ -171,7 +181,7 @@
     int32_t mNextInputDeviceId;
     int32_t nextInputDeviceIdLocked();
 
-    void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
+    std::vector<InputDeviceInfo> getInputDevicesLocked() const;
 
     nsecs_t mDisableVirtualKeysTimeout;
     void disableVirtualKeysUntilLocked(nsecs_t time);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 85701e4..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -46,7 +46,7 @@
     virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0;
 
     virtual void fadePointer() = 0;
-    virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
+    virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
 
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
     virtual int32_t bumpGeneration() = 0;
@@ -59,6 +59,9 @@
     virtual EventHubInterface* getEventHub() = 0;
 
     virtual int32_t getNextId() = 0;
+
+    virtual void updateLedMetaState(int32_t metaState) = 0;
+    virtual int32_t getLedMetaState() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 5a32443..7de9b830 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -102,7 +102,7 @@
      * How many buffers to keep for the internal queue. When the internal buffer
      * exceeds this capacity, oldest frames will be dropped.
      */
-    static constexpr size_t MAX_QUEUE_SIZE = 10;
+    static constexpr size_t MAX_QUEUE_SIZE = 20;
     std::vector<TouchVideoFrame> mFrames;
 
     /**
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 887ab53..254b64b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+// clang-format off
 #include "../Macros.h"
+// clang-format on
 
 #include "CursorInputMapper.h"
 
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
+#include "PointerControllerInterface.h"
 #include "TouchCursorInputMapperCommon.h"
 
 namespace android {
@@ -79,6 +82,10 @@
     } else {
         info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
         info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
+                             0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
+                             0.0f);
     }
     info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
 
@@ -154,7 +161,7 @@
                 mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
                 mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
                 // Keep PointerController around in order to preserve the pointer position.
-                mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+                mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
             } else {
                 ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
             }
@@ -183,7 +190,7 @@
         mOrientation = DISPLAY_ORIENTATION_0;
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+                    config->getDisplayViewportByType(ViewportType::INTERNAL);
             if (internalViewport) {
                 mOrientation = internalViewport->orientation;
             }
@@ -311,12 +318,12 @@
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-    int32_t displayId;
+    int32_t displayId = ADISPLAY_ID_NONE;
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
         if (moved || scrolled || buttonsChanged) {
-            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+            mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
 
             if (moved) {
                 mPointerController->move(deltaX, deltaY);
@@ -326,7 +333,7 @@
                 mPointerController->setButtonState(currentButtonState);
             }
 
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
         }
 
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
@@ -335,10 +342,12 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
         displayId = mPointerController->getDisplayId();
-    } else {
+    } else if (mSource == AINPUT_SOURCE_MOUSE_RELATIVE) {
+        // Pointer capture mode
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
-        displayId = ADISPLAY_ID_NONE;
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
     }
 
     pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index f65ac39..05bbb26 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -106,7 +106,7 @@
 
     int32_t mOrientation;
 
-    sp<PointerControllerInterface> mPointerController;
+    std::shared_ptr<PointerControllerInterface> mPointerController;
 
     int32_t mButtonState;
     nsecs_t mDownTime;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index a8fe39a..1db829f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,7 +56,7 @@
     return false;
 }
 
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
                           int32_t token) {}
 
 void InputMapper::cancelVibrate(int32_t token) {}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 949c7ea..56ab928 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -22,6 +22,7 @@
 #include "InputListener.h"
 #include "InputReaderContext.h"
 #include "StylusState.h"
+#include "VibrationElement.h"
 
 namespace android {
 
@@ -62,7 +63,8 @@
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                        const int32_t* keyCodes, uint8_t* outFlags);
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+    virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
+                         int32_t token);
     virtual void cancelVibrate(int32_t token);
     virtual void cancelTouch(nsecs_t when);
 
@@ -72,6 +74,7 @@
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+    virtual void updateLedState(bool reset) {}
 
 protected:
     InputDeviceContext& mDeviceContext;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 030a846..abd8aa9 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -32,8 +32,8 @@
 void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    for (size_t i = 0; i < mAxes.size(); i++) {
-        const Axis& axis = mAxes.valueAt(i);
+    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+        const Axis& axis = pair.second;
         addMotionRange(axis.axisInfo.axis, axis, info);
 
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
@@ -72,17 +72,15 @@
     dump += INDENT2 "Joystick Input Mapper:\n";
 
     dump += INDENT3 "Axes:\n";
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        const char* label = getAxisLabel(axis.axisInfo.axis);
+    for (const auto& [rawAxis, axis] : mAxes) {
+        const char* label = InputEventLookup::getAxisLabel(axis.axisInfo.axis);
         if (label) {
             dump += StringPrintf(INDENT4 "%s", label);
         } else {
             dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
         }
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            label = getAxisLabel(axis.axisInfo.highAxis);
+            label = InputEventLookup::getAxisLabel(axis.axisInfo.highAxis);
             if (label) {
                 dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
             } else {
@@ -100,7 +98,7 @@
                              axis.scale, axis.offset, axis.highScale, axis.highOffset);
         dump += StringPrintf(INDENT4 "  rawAxis=%d, rawMin=%d, rawMax=%d, "
                                      "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
-                             mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+                             rawAxis, axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
                              axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
                              axis.rawAxisInfo.resolution);
     }
@@ -113,8 +111,8 @@
     if (!changes) { // first time only
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
-            if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) &
-                  INPUT_DEVICE_CLASS_JOYSTICK)) {
+            if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
+                          .test(InputDeviceClass::JOYSTICK))) {
                 continue; // axis must be claimed by a different device
             }
 
@@ -123,43 +121,14 @@
             if (rawAxisInfo.valid) {
                 // Map axis.
                 AxisInfo axisInfo;
-                bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+                const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+
                 if (!explicitlyMapped) {
                     // Axis is not explicitly mapped, will choose a generic axis later.
                     axisInfo.mode = AxisInfo::MODE_NORMAL;
                     axisInfo.axis = -1;
                 }
-
-                // Apply flat override.
-                int32_t rawFlat =
-                        axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
-
-                // Calculate scaling factors and limits.
-                Axis axis;
-                if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
-                    float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
-                    float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
-                                    0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                                    rawAxisInfo.resolution * scale);
-                } else if (isCenteredAxis(axisInfo.axis)) {
-                    float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                    float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
-                                    offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                                    rawAxisInfo.resolution * scale);
-                } else {
-                    float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
-                                    0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                                    rawAxisInfo.resolution * scale);
-                }
-
-                // To eliminate noise while the joystick is at rest, filter out small variations
-                // in axis values up front.
-                axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
-                mAxes.add(abs, axis);
+                mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
             }
         }
 
@@ -174,9 +143,8 @@
 
         // Assign generic axis ids to remaining axes.
         int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
-        size_t numAxes = mAxes.size();
-        for (size_t i = 0; i < numAxes; i++) {
-            Axis& axis = mAxes.editValueAt(i);
+        for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
+            Axis& axis = it->second;
             if (axis.axisInfo.axis < 0) {
                 while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
                        haveAxis(nextGenericAxisId)) {
@@ -189,19 +157,57 @@
                 } else {
                     ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
                           "have already been assigned to other axes.",
-                          getDeviceName().c_str(), mAxes.keyAt(i));
-                    mAxes.removeItemsAt(i--);
-                    numAxes -= 1;
+                          getDeviceName().c_str(), it->first);
+                    it = mAxes.erase(it);
+                    continue;
                 }
             }
+            it++;
         }
     }
 }
 
+JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
+                                                          const RawAbsoluteAxisInfo& rawAxisInfo,
+                                                          bool explicitlyMapped) {
+    // Apply flat override.
+    int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+    float scale = std::numeric_limits<float>::signaling_NaN();
+    float highScale = std::numeric_limits<float>::signaling_NaN();
+    float highOffset = 0;
+    float offset = 0;
+    float min = 0;
+    // Calculate scaling factors and limits.
+    if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+        scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+        highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+    } else if (isCenteredAxis(axisInfo.axis)) {
+        scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+        offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+        highOffset = offset;
+        highScale = scale;
+        min = -1.0f;
+    } else {
+        scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+        highScale = scale;
+    }
+
+    constexpr float max = 1.0;
+    const float flat = rawFlat * scale;
+    const float fuzz = rawAxisInfo.fuzz * scale;
+    const float resolution = rawAxisInfo.resolution * scale;
+
+    // To eliminate noise while the joystick is at rest, filter out small variations
+    // in axis values up front.
+    const float filter = fuzz ? fuzz : flat * 0.25f;
+    return Axis(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, highScale, highOffset, min,
+                max, flat, fuzz, resolution, filter);
+}
+
 bool JoystickInputMapper::haveAxis(int32_t axisId) {
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
+    for (const std::pair<int32_t, Axis>& pair : mAxes) {
+        const Axis& axis = pair.second;
         if (axis.axisInfo.axis == axisId ||
             (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
             return true;
@@ -211,14 +217,14 @@
 }
 
 void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
-    size_t i = mAxes.size();
-    while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
-        if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+    while (mAxes.size() > PointerCoords::MAX_AXES) {
+        auto it = mAxes.begin();
+        if (ignoreExplicitlyMappedAxes && it->second.explicitlyMapped) {
             continue;
         }
         ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
-              getDeviceName().c_str(), mAxes.keyAt(i));
-        mAxes.removeItemsAt(i);
+              getDeviceName().c_str(), it->first);
+        mAxes.erase(it);
     }
 }
 
@@ -243,9 +249,8 @@
 
 void JoystickInputMapper::reset(nsecs_t when) {
     // Recenter all axes.
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
+    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+        Axis& axis = pair.second;
         axis.resetValue();
     }
 
@@ -255,9 +260,9 @@
 void JoystickInputMapper::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
         case EV_ABS: {
-            ssize_t index = mAxes.indexOfKey(rawEvent->code);
-            if (index >= 0) {
-                Axis& axis = mAxes.editValueAt(index);
+            auto it = mAxes.find(rawEvent->code);
+            if (it != mAxes.end()) {
+                Axis& axis = it->second;
                 float newValue, highNewValue;
                 switch (axis.axisInfo.mode) {
                     case AxisInfo::MODE_INVERT:
@@ -317,9 +322,8 @@
     PointerCoords pointerCoords;
     pointerCoords.clear();
 
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
+    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+        const Axis& axis = pair.second;
         setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
             setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
@@ -357,9 +361,8 @@
 
 bool JoystickInputMapper::filterAxes(bool force) {
     bool atLeastOneSignificantChange = force;
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
+    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+        Axis& axis = pair.second;
         if (force ||
             hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
                                          axis.max)) {
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 823a096..0cf60a2 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -36,6 +36,26 @@
 
 private:
     struct Axis {
+        explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+                      bool explicitlyMapped, float scale, float offset, float highScale,
+                      float highOffset, float min, float max, float flat, float fuzz,
+                      float resolution, float filter)
+              : rawAxisInfo(rawAxisInfo),
+                axisInfo(axisInfo),
+                explicitlyMapped(explicitlyMapped),
+                scale(scale),
+                offset(offset),
+                highScale(highScale),
+                highOffset(highOffset),
+                min(min),
+                max(max),
+                flat(flat),
+                fuzz(fuzz),
+                resolution(resolution),
+                filter(filter) {
+            resetValue();
+        }
+
         RawAbsoluteAxisInfo rawAxisInfo;
         AxisInfo axisInfo;
 
@@ -58,26 +78,6 @@
         float highCurrentValue; // current value of high split
         float highNewValue;     // most recent value of high split
 
-        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
-                        bool explicitlyMapped, float scale, float offset, float highScale,
-                        float highOffset, float min, float max, float flat, float fuzz,
-                        float resolution) {
-            this->rawAxisInfo = rawAxisInfo;
-            this->axisInfo = axisInfo;
-            this->explicitlyMapped = explicitlyMapped;
-            this->scale = scale;
-            this->offset = offset;
-            this->highScale = highScale;
-            this->highOffset = highOffset;
-            this->min = min;
-            this->max = max;
-            this->flat = flat;
-            this->fuzz = fuzz;
-            this->resolution = resolution;
-            this->filter = 0;
-            resetValue();
-        }
-
         void resetValue() {
             this->currentValue = 0;
             this->newValue = 0;
@@ -86,8 +86,11 @@
         }
     };
 
+    static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
+                           bool explicitlyMapped);
+
     // Axes indexed by raw ABS_* axis index.
-    KeyedVector<int32_t, Axis> mAxes;
+    std::unordered_map<int32_t, Axis> mAxes;
 
     void sync(nsecs_t when, bool force);
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e009221..8b9f235 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+// clang-format off
 #include "../Macros.h"
+// clang-format on
 
 #include "KeyboardInputMapper.h"
 
@@ -138,7 +140,7 @@
 
     // No associated display defined, try to find default display if orientationAware.
     if (mParameters.orientationAware) {
-        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+        return config->getDisplayViewportByType(ViewportType::INTERNAL);
     }
 
     return std::nullopt;
@@ -388,11 +390,14 @@
 bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
     int32_t oldMetaState = mMetaState;
     int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
-    bool metaStateChanged = oldMetaState != newMetaState;
+    int32_t metaStateChanged = oldMetaState ^ newMetaState;
     if (metaStateChanged) {
         mMetaState = newMetaState;
-        updateLedState(false);
-
+        constexpr int32_t allLedMetaState =
+                AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON;
+        if ((metaStateChanged & allLedMetaState) != 0) {
+            getContext()->updateLedMetaState(newMetaState & allLedMetaState);
+        }
         getContext()->updateGlobalMetaState();
     }
 
@@ -413,6 +418,26 @@
 }
 
 void KeyboardInputMapper::updateLedState(bool reset) {
+    mMetaState |= getContext()->getLedMetaState();
+
+    constexpr int32_t META_NUM = 3;
+    const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+                                                    AKEYCODE_SCROLL_LOCK};
+    const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
+                                                     AMETA_SCROLL_LOCK_ON};
+    std::array<uint8_t, META_NUM> flags = {0, 0, 0};
+    bool hasKeyLayout =
+            getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data());
+    // If the device doesn't have the physical meta key it shouldn't generate the corresponding
+    // meta state.
+    if (hasKeyLayout) {
+        for (int i = 0; i < META_NUM; i++) {
+            if (!flags[i]) {
+                mMetaState &= ~metaCodes[i];
+            }
+        }
+    }
+
     updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
     updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
     updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0bdeded..4c0b42a 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -42,6 +42,7 @@
     virtual int32_t getMetaState() override;
     virtual void updateMetaState(int32_t keyCode) override;
     virtual std::optional<int32_t> getAssociatedDisplayId() override;
+    virtual void updateLedState(bool reset);
 
 private:
     // The current viewport.
@@ -93,7 +94,6 @@
 
     void resetLedState();
     void initializeLedState(LedState& ledState, int32_t led);
-    void updateLedState(bool reset);
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
     std::optional<DisplayViewport> findViewport(nsecs_t when,
                                                 const InputReaderConfiguration* config);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 43bd9f1..0440f49 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -236,6 +236,20 @@
     mMultiTouchMotionAccumulator.process(rawEvent);
 }
 
+std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
+        const MultiTouchMotionAccumulator::Slot& inSlot) {
+    if (mHavePointerIds) {
+        int32_t trackingId = inSlot.getTrackingId();
+        for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
+            int32_t n = idBits.clearFirstMarkedBit();
+            if (mPointerTrackingIdMap[n] == trackingId) {
+                return std::make_optional(n);
+            }
+        }
+    }
+    return std::nullopt;
+}
+
 void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
     size_t outCount = 0;
@@ -250,10 +264,9 @@
         }
 
         if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
-            if (!mCurrentMotionAborted) {
-                ALOGI("Canceling touch gesture from device %s because the palm event was detected",
-                      getDeviceName().c_str());
-                cancelTouch(when);
+            std::optional<int32_t> id = getActiveBitId(*inSlot);
+            if (id) {
+                outState->rawPointerData.canceledIdBits.markBit(id.value());
             }
             continue;
         }
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 89ef41d..ea6f207 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -92,17 +92,19 @@
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
     explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
-    virtual ~MultiTouchInputMapper();
+    ~MultiTouchInputMapper() override;
 
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    void reset(nsecs_t when) override;
+    void process(const RawEvent* rawEvent) override;
 
 protected:
-    virtual void syncTouch(nsecs_t when, RawState* outState);
-    virtual void configureRawPointerAxes();
-    virtual bool hasStylus() const;
+    void syncTouch(nsecs_t when, RawState* outState) override;
+    void configureRawPointerAxes() override;
+    bool hasStylus() const override;
 
 private:
+    // If the slot is in use, return the bit id. Return std::nullopt otherwise.
+    std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
     MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
 
     // Specifies the pointer id bits that are in use, and their associated tracking id.
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 9885889..594ff42 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+// clang-format off
 #include "../Macros.h"
+// clang-format on
 
 #include "RotaryEncoderInputMapper.h"
 
@@ -66,7 +68,7 @@
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
-                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+                config->getDisplayViewportByType(ViewportType::INTERNAL);
         if (internalViewport) {
             mOrientation = internalViewport->orientation;
         } else {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index f5befb3..9cb3f67 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -25,15 +25,15 @@
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
     explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
-    virtual ~SingleTouchInputMapper();
+    ~SingleTouchInputMapper() override;
 
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    void reset(nsecs_t when) override;
+    void process(const RawEvent* rawEvent) override;
 
 protected:
-    virtual void syncTouch(nsecs_t when, RawState* outState);
-    virtual void configureRawPointerAxes();
-    virtual bool hasStylus() const;
+    void syncTouch(nsecs_t when, RawState* outState) override;
+    void configureRawPointerAxes() override;
+    bool hasStylus() const override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 2a3e263..a86443d 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,12 +17,13 @@
 #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 
+#include <input/DisplayViewport.h>
+#include <stdint.h>
+
 #include "EventHub.h"
 #include "InputListener.h"
 #include "InputReaderContext.h"
 
-#include <stdint.h>
-
 namespace android {
 
 // --- Static Definitions ---
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 99a572a..17f37c3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+// clang-format off
 #include "../Macros.h"
+// clang-format on
 
+#include <input/NamedEnum.h>
 #include "TouchInputMapper.h"
 
 #include "CursorButtonAccumulator.h"
@@ -102,6 +105,7 @@
     pointerCount = other.pointerCount;
     hoveringIdBits = other.hoveringIdBits;
     touchingIdBits = other.touchingIdBits;
+    canceledIdBits = other.canceledIdBits;
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointers[i] = other.pointers[i];
@@ -138,12 +142,15 @@
     pointerCount = 0;
     hoveringIdBits.clear();
     touchingIdBits.clear();
+    canceledIdBits.clear();
+    validIdBits.clear();
 }
 
 void CookedPointerData::copyFrom(const CookedPointerData& other) {
     pointerCount = other.pointerCount;
     hoveringIdBits = other.hoveringIdBits;
     touchingIdBits = other.touchingIdBits;
+    validIdBits = other.validIdBits;
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -159,11 +166,13 @@
 TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
       : InputMapper(deviceContext),
         mSource(0),
-        mDeviceMode(DEVICE_MODE_DISABLED),
+        mDeviceMode(DeviceMode::DISABLED),
         mRawSurfaceWidth(-1),
         mRawSurfaceHeight(-1),
         mSurfaceLeft(0),
         mSurfaceTop(0),
+        mSurfaceRight(0),
+        mSurfaceBottom(0),
         mPhysicalWidth(-1),
         mPhysicalHeight(-1),
         mPhysicalLeft(0),
@@ -179,11 +188,25 @@
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    if (mDeviceMode != DEVICE_MODE_DISABLED) {
+    if (mDeviceMode != DeviceMode::DISABLED) {
         info->addMotionRange(mOrientedRanges.x);
         info->addMotionRange(mOrientedRanges.y);
         info->addMotionRange(mOrientedRanges.pressure);
 
+        if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
+            // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
+            //
+            // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
+            // motion, i.e. the hardware dimensions, as the finger could move completely across the
+            // touchpad in one sample cycle.
+            const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+            const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+            info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat,
+                                 x.fuzz, x.resolution);
+            info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat,
+                                 y.fuzz, y.resolution);
+        }
+
         if (mOrientedRanges.haveSize) {
             info->addMotionRange(mOrientedRanges.size);
         }
@@ -218,7 +241,7 @@
             info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
                                  0.0f);
         }
-        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+        if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
             const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
             const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
             info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
@@ -235,7 +258,8 @@
 }
 
 void TouchInputMapper::dump(std::string& dump) {
-    dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
+    dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
+                         NamedEnum::string(mDeviceMode).c_str());
     dumpParameters(dump);
     dumpVirtualKeys(dump);
     dumpRawPointerAxes(dump);
@@ -284,12 +308,14 @@
         const PointerProperties& pointerProperties =
                 mLastCookedState.cookedPointerData.pointerProperties[i];
         const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
-        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
-                                     "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, "
-                                     "toolMinor=%0.3f, "
+        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, dx=%0.3f, dy=%0.3f, "
+                                     "pressure=%0.3f, touchMajor=%0.3f, touchMinor=%0.3f, "
+                                     "toolMajor=%0.3f, toolMinor=%0.3f, "
                                      "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
                                      "toolType=%d, isHovering=%s\n",
                              i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y),
                              pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                              pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
                              pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
@@ -311,7 +337,7 @@
     dump += INDENT3 "External Stylus State:\n";
     dumpStylusState(dump, mExternalStylusState);
 
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
+    if (mDeviceMode == DeviceMode::POINTER) {
         dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
         dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale);
         dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale);
@@ -321,22 +347,6 @@
     }
 }
 
-const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
-    switch (deviceMode) {
-        case DEVICE_MODE_DISABLED:
-            return "disabled";
-        case DEVICE_MODE_DIRECT:
-            return "direct";
-        case DEVICE_MODE_UNSCALED:
-            return "unscaled";
-        case DEVICE_MODE_NAVIGATION:
-            return "navigation";
-        case DEVICE_MODE_POINTER:
-            return "pointer";
-    }
-    return "unknown";
-}
-
 void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                  uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -375,6 +385,7 @@
     if (!changes ||
         (changes &
          (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+          InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
           InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
           InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
           InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
@@ -406,16 +417,16 @@
     // multitouch.  The spot-based presentation relies on being able to accurately
     // locate two or more fingers on the touch pad.
     mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
-            ? Parameters::GESTURE_MODE_SINGLE_TOUCH
-            : Parameters::GESTURE_MODE_MULTI_TOUCH;
+            ? Parameters::GestureMode::SINGLE_TOUCH
+            : Parameters::GestureMode::MULTI_TOUCH;
 
     String8 gestureModeString;
     if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
                                                              gestureModeString)) {
         if (gestureModeString == "single-touch") {
-            mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
+            mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
         } else if (gestureModeString == "multi-touch") {
-            mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
+            mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
         } else if (gestureModeString != "default") {
             ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
         }
@@ -423,18 +434,18 @@
 
     if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
         // The device is a touch screen.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+        mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
     } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
         // The device is a pointing device like a track pad.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+        mParameters.deviceType = Parameters::DeviceType::POINTER;
     } else if (getDeviceContext().hasRelativeAxis(REL_X) ||
                getDeviceContext().hasRelativeAxis(REL_Y)) {
         // The device is a cursor device with a touch pad attached.
         // By default don't use the touch pad to move the pointer.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
     } else {
         // The device is a touch pad of unknown purpose.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+        mParameters.deviceType = Parameters::DeviceType::POINTER;
     }
 
     mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
@@ -443,29 +454,29 @@
     if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
                                                              deviceTypeString)) {
         if (deviceTypeString == "touchScreen") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+            mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
         } else if (deviceTypeString == "touchPad") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+            mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
         } else if (deviceTypeString == "touchNavigation") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
+            mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
         } else if (deviceTypeString == "pointer") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+            mParameters.deviceType = Parameters::DeviceType::POINTER;
         } else if (deviceTypeString != "default") {
             ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
         }
     }
 
-    mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+    mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
     getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
                                                          mParameters.orientationAware);
 
     mParameters.hasAssociatedDisplay = false;
     mParameters.associatedDisplayIsExternal = false;
     if (mParameters.orientationAware ||
-        mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN ||
-        mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+        mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
+        mParameters.deviceType == Parameters::DeviceType::POINTER) {
         mParameters.hasAssociatedDisplay = true;
-        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+        if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
             mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
             String8 uniqueDisplayId;
             getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
@@ -487,33 +498,9 @@
 void TouchInputMapper::dumpParameters(std::string& dump) {
     dump += INDENT3 "Parameters:\n";
 
-    switch (mParameters.gestureMode) {
-        case Parameters::GESTURE_MODE_SINGLE_TOUCH:
-            dump += INDENT4 "GestureMode: single-touch\n";
-            break;
-        case Parameters::GESTURE_MODE_MULTI_TOUCH:
-            dump += INDENT4 "GestureMode: multi-touch\n";
-            break;
-        default:
-            assert(false);
-    }
+    dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
 
-    switch (mParameters.deviceType) {
-        case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
-            dump += INDENT4 "DeviceType: touchScreen\n";
-            break;
-        case Parameters::DEVICE_TYPE_TOUCH_PAD:
-            dump += INDENT4 "DeviceType: touchPad\n";
-            break;
-        case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
-            dump += INDENT4 "DeviceType: touchNavigation\n";
-            break;
-        case Parameters::DEVICE_TYPE_POINTER:
-            dump += INDENT4 "DeviceType: pointer\n";
-            break;
-        default:
-            ALOG_ASSERT(false);
-    }
+    dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
 
     dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
                                  "displayId='%s'\n",
@@ -558,14 +545,14 @@
  * 4. Otherwise, use a non-display viewport.
  */
 std::optional<DisplayViewport> TouchInputMapper::findViewport() {
-    if (mParameters.hasAssociatedDisplay) {
+    if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
         const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
         if (displayPort) {
             // Find the viewport that contains the same port
             return getDeviceContext().getAssociatedViewport();
         }
 
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
+        if (mDeviceMode == DeviceMode::POINTER) {
             std::optional<DisplayViewport> viewport =
                     mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
             if (viewport) {
@@ -583,18 +570,18 @@
 
         ViewportType viewportTypeToUse;
         if (mParameters.associatedDisplayIsExternal) {
-            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+            viewportTypeToUse = ViewportType::EXTERNAL;
         } else {
-            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+            viewportTypeToUse = ViewportType::INTERNAL;
         }
 
         std::optional<DisplayViewport> viewport =
                 mConfig.getDisplayViewportByType(viewportTypeToUse);
-        if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+        if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
             ALOGW("Input device %s should be associated with external display, "
                   "fallback to internal one for the external viewport is not found.",
                   getDeviceName().c_str());
-            viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+            viewport = mConfig.getDisplayViewportByType(ViewportType::INTERNAL);
         }
 
         return viewport;
@@ -610,34 +597,34 @@
 }
 
 void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
-    int32_t oldDeviceMode = mDeviceMode;
+    DeviceMode oldDeviceMode = mDeviceMode;
 
     resolveExternalStylusPresence();
 
     // Determine device mode.
-    if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&
-        mConfig.pointerGesturesEnabled) {
+    if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+        mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
         mSource = AINPUT_SOURCE_MOUSE;
-        mDeviceMode = DEVICE_MODE_POINTER;
+        mDeviceMode = DeviceMode::POINTER;
         if (hasStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
         }
-    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN &&
+    } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
                mParameters.hasAssociatedDisplay) {
         mSource = AINPUT_SOURCE_TOUCHSCREEN;
-        mDeviceMode = DEVICE_MODE_DIRECT;
+        mDeviceMode = DeviceMode::DIRECT;
         if (hasStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
         }
         if (hasExternalStylus()) {
             mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
         }
-    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+    } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
         mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
-        mDeviceMode = DEVICE_MODE_NAVIGATION;
+        mDeviceMode = DeviceMode::NAVIGATION;
     } else {
         mSource = AINPUT_SOURCE_TOUCHPAD;
-        mDeviceMode = DEVICE_MODE_UNSCALED;
+        mDeviceMode = DeviceMode::UNSCALED;
     }
 
     // Ensure we have valid X and Y axes.
@@ -645,7 +632,7 @@
         ALOGW("Touch device '%s' did not report support for X or Y axis!  "
               "The device will be inoperable.",
               getDeviceName().c_str());
-        mDeviceMode = DEVICE_MODE_DISABLED;
+        mDeviceMode = DeviceMode::DISABLED;
         return;
     }
 
@@ -656,7 +643,7 @@
               "display.  The device will be inoperable until the display size "
               "becomes available.",
               getDeviceName().c_str());
-        mDeviceMode = DEVICE_MODE_DISABLED;
+        mDeviceMode = DeviceMode::DISABLED;
         return;
     }
 
@@ -668,7 +655,7 @@
     if (viewportChanged) {
         mViewport = *newViewport;
 
-        if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+        if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
             // Convert rotated viewport to natural surface coordinates.
             int32_t naturalLogicalWidth, naturalLogicalHeight;
             int32_t naturalPhysicalWidth, naturalPhysicalHeight;
@@ -759,13 +746,13 @@
     }
 
     // Create pointer controller if needed.
-    if (mDeviceMode == DEVICE_MODE_POINTER ||
-        (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+    if (mDeviceMode == DeviceMode::POINTER ||
+        (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches)) {
         if (mPointerController == nullptr) {
             mPointerController = getContext()->getPointerController(getDeviceId());
         }
     } else {
-        mPointerController.clear();
+        mPointerController.reset();
     }
 
     if (viewportChanged || deviceModeChanged) {
@@ -798,7 +785,7 @@
         float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
 
         // Size factors.
-        if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+        if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
             if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
                 mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
             } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
@@ -847,8 +834,8 @@
         // Pressure factors.
         mPressureScale = 0;
         float pressureMax = 1.0;
-        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL ||
-            mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+        if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
+            mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
             if (mCalibration.havePressureScale) {
                 mPressureScale = mCalibration.pressureScale;
                 pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
@@ -901,9 +888,9 @@
             mOrientedRanges.orientation.fuzz = 0;
             mOrientedRanges.orientation.resolution = 0;
         } else if (mCalibration.orientationCalibration !=
-                   Calibration::ORIENTATION_CALIBRATION_NONE) {
+                   Calibration::OrientationCalibration::NONE) {
             if (mCalibration.orientationCalibration ==
-                Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+                Calibration::OrientationCalibration::INTERPOLATED) {
                 if (mRawPointerAxes.orientation.valid) {
                     if (mRawPointerAxes.orientation.maxValue > 0) {
                         mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
@@ -928,8 +915,8 @@
 
         // Distance
         mDistanceScale = 0;
-        if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
-            if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) {
+        if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
+            if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
                 if (mCalibration.haveDistanceScale) {
                     mDistanceScale = mCalibration.distanceScale;
                 } else {
@@ -991,7 +978,7 @@
         // Location
         updateAffineTransformation();
 
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
+        if (mDeviceMode == DeviceMode::POINTER) {
             // Compute pointer gesture detection parameters.
             float rawDiagonal = hypotf(rawWidth, rawHeight);
             float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
@@ -1112,19 +1099,19 @@
     Calibration& out = mCalibration;
 
     // Size
-    out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+    out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
     String8 sizeCalibrationString;
     if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
         if (sizeCalibrationString == "none") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+            out.sizeCalibration = Calibration::SizeCalibration::NONE;
         } else if (sizeCalibrationString == "geometric") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+            out.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
         } else if (sizeCalibrationString == "diameter") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
+            out.sizeCalibration = Calibration::SizeCalibration::DIAMETER;
         } else if (sizeCalibrationString == "box") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
+            out.sizeCalibration = Calibration::SizeCalibration::BOX;
         } else if (sizeCalibrationString == "area") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
+            out.sizeCalibration = Calibration::SizeCalibration::AREA;
         } else if (sizeCalibrationString != "default") {
             ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
         }
@@ -1135,15 +1122,15 @@
     out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
 
     // Pressure
-    out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+    out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
     String8 pressureCalibrationString;
     if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
         if (pressureCalibrationString == "none") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+            out.pressureCalibration = Calibration::PressureCalibration::NONE;
         } else if (pressureCalibrationString == "physical") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+            out.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
         } else if (pressureCalibrationString == "amplitude") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+            out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
         } else if (pressureCalibrationString != "default") {
             ALOGW("Invalid value for touch.pressure.calibration: '%s'",
                   pressureCalibrationString.string());
@@ -1153,15 +1140,15 @@
     out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
 
     // Orientation
-    out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+    out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
     String8 orientationCalibrationString;
     if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
         if (orientationCalibrationString == "none") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+            out.orientationCalibration = Calibration::OrientationCalibration::NONE;
         } else if (orientationCalibrationString == "interpolated") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+            out.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
         } else if (orientationCalibrationString == "vector") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
+            out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
         } else if (orientationCalibrationString != "default") {
             ALOGW("Invalid value for touch.orientation.calibration: '%s'",
                   orientationCalibrationString.string());
@@ -1169,13 +1156,13 @@
     }
 
     // Distance
-    out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+    out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
     String8 distanceCalibrationString;
     if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
         if (distanceCalibrationString == "none") {
-            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+            out.distanceCalibration = Calibration::DistanceCalibration::NONE;
         } else if (distanceCalibrationString == "scaled") {
-            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+            out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
         } else if (distanceCalibrationString != "default") {
             ALOGW("Invalid value for touch.distance.calibration: '%s'",
                   distanceCalibrationString.string());
@@ -1184,13 +1171,13 @@
 
     out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
 
-    out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
+    out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
     String8 coverageCalibrationString;
     if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
         if (coverageCalibrationString == "none") {
-            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+            out.coverageCalibration = Calibration::CoverageCalibration::NONE;
         } else if (coverageCalibrationString == "box") {
-            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
+            out.coverageCalibration = Calibration::CoverageCalibration::BOX;
         } else if (coverageCalibrationString != "default") {
             ALOGW("Invalid value for touch.coverage.calibration: '%s'",
                   coverageCalibrationString.string());
@@ -1201,43 +1188,43 @@
 void TouchInputMapper::resolveCalibration() {
     // Size
     if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
-        if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
-            mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+        if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
+            mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
         }
     } else {
-        mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+        mCalibration.sizeCalibration = Calibration::SizeCalibration::NONE;
     }
 
     // Pressure
     if (mRawPointerAxes.pressure.valid) {
-        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
-            mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+        if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
+            mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
         }
     } else {
-        mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+        mCalibration.pressureCalibration = Calibration::PressureCalibration::NONE;
     }
 
     // Orientation
     if (mRawPointerAxes.orientation.valid) {
-        if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
-            mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+        if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
+            mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
         }
     } else {
-        mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+        mCalibration.orientationCalibration = Calibration::OrientationCalibration::NONE;
     }
 
     // Distance
     if (mRawPointerAxes.distance.valid) {
-        if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
-            mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+        if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
+            mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
         }
     } else {
-        mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+        mCalibration.distanceCalibration = Calibration::DistanceCalibration::NONE;
     }
 
     // Coverage
-    if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
-        mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+    if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::DEFAULT) {
+        mCalibration.coverageCalibration = Calibration::CoverageCalibration::NONE;
     }
 }
 
@@ -1246,19 +1233,19 @@
 
     // Size
     switch (mCalibration.sizeCalibration) {
-        case Calibration::SIZE_CALIBRATION_NONE:
+        case Calibration::SizeCalibration::NONE:
             dump += INDENT4 "touch.size.calibration: none\n";
             break;
-        case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+        case Calibration::SizeCalibration::GEOMETRIC:
             dump += INDENT4 "touch.size.calibration: geometric\n";
             break;
-        case Calibration::SIZE_CALIBRATION_DIAMETER:
+        case Calibration::SizeCalibration::DIAMETER:
             dump += INDENT4 "touch.size.calibration: diameter\n";
             break;
-        case Calibration::SIZE_CALIBRATION_BOX:
+        case Calibration::SizeCalibration::BOX:
             dump += INDENT4 "touch.size.calibration: box\n";
             break;
-        case Calibration::SIZE_CALIBRATION_AREA:
+        case Calibration::SizeCalibration::AREA:
             dump += INDENT4 "touch.size.calibration: area\n";
             break;
         default:
@@ -1280,13 +1267,13 @@
 
     // Pressure
     switch (mCalibration.pressureCalibration) {
-        case Calibration::PRESSURE_CALIBRATION_NONE:
+        case Calibration::PressureCalibration::NONE:
             dump += INDENT4 "touch.pressure.calibration: none\n";
             break;
-        case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+        case Calibration::PressureCalibration::PHYSICAL:
             dump += INDENT4 "touch.pressure.calibration: physical\n";
             break;
-        case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+        case Calibration::PressureCalibration::AMPLITUDE:
             dump += INDENT4 "touch.pressure.calibration: amplitude\n";
             break;
         default:
@@ -1299,13 +1286,13 @@
 
     // Orientation
     switch (mCalibration.orientationCalibration) {
-        case Calibration::ORIENTATION_CALIBRATION_NONE:
+        case Calibration::OrientationCalibration::NONE:
             dump += INDENT4 "touch.orientation.calibration: none\n";
             break;
-        case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+        case Calibration::OrientationCalibration::INTERPOLATED:
             dump += INDENT4 "touch.orientation.calibration: interpolated\n";
             break;
-        case Calibration::ORIENTATION_CALIBRATION_VECTOR:
+        case Calibration::OrientationCalibration::VECTOR:
             dump += INDENT4 "touch.orientation.calibration: vector\n";
             break;
         default:
@@ -1314,10 +1301,10 @@
 
     // Distance
     switch (mCalibration.distanceCalibration) {
-        case Calibration::DISTANCE_CALIBRATION_NONE:
+        case Calibration::DistanceCalibration::NONE:
             dump += INDENT4 "touch.distance.calibration: none\n";
             break;
-        case Calibration::DISTANCE_CALIBRATION_SCALED:
+        case Calibration::DistanceCalibration::SCALED:
             dump += INDENT4 "touch.distance.calibration: scaled\n";
             break;
         default:
@@ -1329,10 +1316,10 @@
     }
 
     switch (mCalibration.coverageCalibration) {
-        case Calibration::COVERAGE_CALIBRATION_NONE:
+        case Calibration::CoverageCalibration::NONE:
             dump += INDENT4 "touch.coverage.calibration: none\n";
             break;
-        case Calibration::COVERAGE_CALIBRATION_BOX:
+        case Calibration::CoverageCalibration::BOX:
             dump += INDENT4 "touch.coverage.calibration: box\n";
             break;
         default:
@@ -1370,7 +1357,7 @@
     mCurrentCookedState.clear();
     mLastRawState.clear();
     mLastCookedState.clear();
-    mPointerUsage = POINTER_USAGE_NONE;
+    mPointerUsage = PointerUsage::NONE;
     mSentHoverEnter = false;
     mHavePointerIds = false;
     mCurrentMotionAborted = false;
@@ -1383,7 +1370,7 @@
     resetExternalStylus();
 
     if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
 
@@ -1442,17 +1429,18 @@
 
 #if DEBUG_RAW_EVENTS
     ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
-          "hovering ids 0x%08x -> 0x%08x",
+          "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
           last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
           last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
-          last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
+          last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value,
+          next->rawPointerData.canceledIdBits.value);
 #endif
 
     processRawTouches(false /*timeout*/);
 }
 
 void TouchInputMapper::processRawTouches(bool timeout) {
-    if (mDeviceMode == DEVICE_MODE_DISABLED) {
+    if (mDeviceMode == DeviceMode::DISABLED) {
         // Drop all input if the device is disabled.
         mCurrentRawState.clear();
         mRawStatesPending.clear();
@@ -1517,7 +1505,7 @@
     bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
     if (initialDown || buttonsPressed) {
         // If this is a touch screen, hide the pointer on an initial down.
-        if (mDeviceMode == DEVICE_MODE_DIRECT) {
+        if (mDeviceMode == DeviceMode::DIRECT) {
             getContext()->fadePointer();
         }
 
@@ -1546,7 +1534,7 @@
                          mCurrentCookedState.buttonState);
 
     // Dispatch the touches either directly or by translation through a pointer on screen.
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
+    if (mDeviceMode == DeviceMode::POINTER) {
         for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
@@ -1576,21 +1564,21 @@
         if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
             mCurrentCookedState.mouseIdBits.clear();
             mCurrentCookedState.fingerIdBits.clear();
-            pointerUsage = POINTER_USAGE_STYLUS;
+            pointerUsage = PointerUsage::STYLUS;
         } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
             mCurrentCookedState.fingerIdBits.clear();
-            pointerUsage = POINTER_USAGE_MOUSE;
+            pointerUsage = PointerUsage::MOUSE;
         } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
                    isPointerDown(mCurrentRawState.buttonState)) {
-            pointerUsage = POINTER_USAGE_GESTURES;
+            pointerUsage = PointerUsage::GESTURES;
         }
 
         dispatchPointerUsage(when, policyFlags, pointerUsage);
     } else {
-        if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
+        if (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches &&
             mPointerController != nullptr) {
-            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+            mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
+            mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
 
             mPointerController->setButtonState(mCurrentRawState.buttonState);
             mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -1627,7 +1615,7 @@
 }
 
 void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
-    if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
+    if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
         mCurrentRawState.buttonState |= mExternalStylusState.buttons;
     }
 }
@@ -1654,7 +1642,7 @@
 }
 
 bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
-    if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+    if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
         return false;
     }
 
@@ -1697,11 +1685,11 @@
 }
 
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
-        if (mPointerUsage == POINTER_USAGE_GESTURES) {
+    if (mDeviceMode == DeviceMode::POINTER) {
+        if (mPointerUsage == PointerUsage::GESTURES) {
             dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
         }
-    } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+    } else if (mDeviceMode == DeviceMode::DIRECT) {
         if (mExternalStylusFusionTimeout < when) {
             processRawTouches(true /*timeout*/);
         } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
@@ -1770,7 +1758,8 @@
         // Pointer just went down.  Check for virtual key press or off-screen touches.
         uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
         const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
-        if (!isPointInsideSurface(pointer.x, pointer.y)) {
+        // Exclude unscaled device for inside surface checking.
+        if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) {
             // If exactly one pointer went down, check for virtual key hit.
             // Otherwise we will drop the entire stroke.
             if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1890,14 +1879,15 @@
         // Dispatch pointer up events.
         while (!upIdBits.isEmpty()) {
             uint32_t upId = upIdBits.clearFirstMarkedBit();
-
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
-                           metaState, buttonState, 0,
+            bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
+            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+                           isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
                            mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
                            mOrientedXPrecision, mOrientedYPrecision, mDownTime);
             dispatchedIdBits.clearBit(upId);
+            mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
         }
 
         // Dispatch move events if any of the remaining pointers moved from their old locations.
@@ -2023,6 +2013,8 @@
             mCurrentRawState.rawPointerData.hoveringIdBits;
     mCurrentCookedState.cookedPointerData.touchingIdBits =
             mCurrentRawState.rawPointerData.touchingIdBits;
+    mCurrentCookedState.cookedPointerData.canceledIdBits =
+            mCurrentRawState.rawPointerData.canceledIdBits;
 
     if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
         mCurrentCookedState.buttonState = 0;
@@ -2038,10 +2030,10 @@
         // Size
         float touchMajor, touchMinor, toolMajor, toolMinor, size;
         switch (mCalibration.sizeCalibration) {
-            case Calibration::SIZE_CALIBRATION_GEOMETRIC:
-            case Calibration::SIZE_CALIBRATION_DIAMETER:
-            case Calibration::SIZE_CALIBRATION_BOX:
-            case Calibration::SIZE_CALIBRATION_AREA:
+            case Calibration::SizeCalibration::GEOMETRIC:
+            case Calibration::SizeCalibration::DIAMETER:
+            case Calibration::SizeCalibration::BOX:
+            case Calibration::SizeCalibration::AREA:
                 if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
                     touchMajor = in.touchMajor;
                     touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
@@ -2083,17 +2075,17 @@
                     }
                 }
 
-                if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
+                if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
                     touchMajor *= mGeometricScale;
                     touchMinor *= mGeometricScale;
                     toolMajor *= mGeometricScale;
                     toolMinor *= mGeometricScale;
-                } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
+                } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::AREA) {
                     touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
                     touchMinor = touchMajor;
                     toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
                     toolMinor = toolMajor;
-                } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
+                } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DIAMETER) {
                     touchMinor = touchMajor;
                     toolMinor = toolMajor;
                 }
@@ -2116,8 +2108,8 @@
         // Pressure
         float pressure;
         switch (mCalibration.pressureCalibration) {
-            case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-            case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+            case Calibration::PressureCalibration::PHYSICAL:
+            case Calibration::PressureCalibration::AMPLITUDE:
                 pressure = in.pressure * mPressureScale;
                 break;
             default:
@@ -2137,10 +2129,10 @@
             tilt = 0;
 
             switch (mCalibration.orientationCalibration) {
-                case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+                case Calibration::OrientationCalibration::INTERPOLATED:
                     orientation = in.orientation * mOrientationScale;
                     break;
-                case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+                case Calibration::OrientationCalibration::VECTOR: {
                     int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
                     int32_t c2 = signExtendNybble(in.orientation & 0x0f);
                     if (c1 != 0 || c2 != 0) {
@@ -2164,7 +2156,7 @@
         // Distance
         float distance;
         switch (mCalibration.distanceCalibration) {
-            case Calibration::DISTANCE_CALIBRATION_SCALED:
+            case Calibration::DistanceCalibration::SCALED:
                 distance = in.distance * mDistanceScale;
                 break;
             default:
@@ -2174,7 +2166,7 @@
         // Coverage
         int32_t rawLeft, rawTop, rawRight, rawBottom;
         switch (mCalibration.coverageCalibration) {
-            case Calibration::COVERAGE_CALIBRATION_BOX:
+            case Calibration::CoverageCalibration::BOX:
                 rawLeft = (in.toolMinor & 0xffff0000) >> 16;
                 rawRight = in.toolMinor & 0x0000ffff;
                 rawBottom = in.toolMajor & 0x0000ffff;
@@ -2251,7 +2243,7 @@
         out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
         out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
         out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
-        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+        if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
             out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
             out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
             out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
@@ -2261,15 +2253,26 @@
             out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
         }
 
+        // Write output relative fields if applicable.
+        uint32_t id = in.id;
+        if (mSource == AINPUT_SOURCE_TOUCHPAD &&
+            mLastCookedState.cookedPointerData.hasPointerCoordsForId(id)) {
+            const PointerCoords& p = mLastCookedState.cookedPointerData.pointerCoordsForId(id);
+            float dx = xTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_X);
+            float dy = yTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_Y);
+            out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx);
+            out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
+        }
+
         // Write output properties.
         PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
-        uint32_t id = in.id;
         properties.clear();
         properties.id = id;
         properties.toolType = in.toolType;
 
-        // Write id index.
+        // Write id index and mark id as valid.
         mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
+        mCurrentCookedState.cookedPointerData.validIdBits.markBit(id);
     }
 }
 
@@ -2281,36 +2284,36 @@
     }
 
     switch (mPointerUsage) {
-        case POINTER_USAGE_GESTURES:
+        case PointerUsage::GESTURES:
             dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
             break;
-        case POINTER_USAGE_STYLUS:
+        case PointerUsage::STYLUS:
             dispatchPointerStylus(when, policyFlags);
             break;
-        case POINTER_USAGE_MOUSE:
+        case PointerUsage::MOUSE:
             dispatchPointerMouse(when, policyFlags);
             break;
-        default:
+        case PointerUsage::NONE:
             break;
     }
 }
 
 void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
     switch (mPointerUsage) {
-        case POINTER_USAGE_GESTURES:
+        case PointerUsage::GESTURES:
             abortPointerGestures(when, policyFlags);
             break;
-        case POINTER_USAGE_STYLUS:
+        case PointerUsage::STYLUS:
             abortPointerStylus(when, policyFlags);
             break;
-        case POINTER_USAGE_MOUSE:
+        case PointerUsage::MOUSE:
             abortPointerMouse(when, policyFlags);
             break;
-        default:
+        case PointerUsage::NONE:
             break;
     }
 
-    mPointerUsage = POINTER_USAGE_NONE;
+    mPointerUsage = PointerUsage::NONE;
 }
 
 void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
@@ -2326,49 +2329,49 @@
     }
 
     // Update the pointer presentation and spots.
-    if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+    if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
+        mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
         if (finishPreviousGesture || cancelPreviousGesture) {
             mPointerController->clearSpots();
         }
 
-        if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+        if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
             mPointerController->setSpots(mPointerGesture.currentGestureCoords,
                                          mPointerGesture.currentGestureIdToIndex,
                                          mPointerGesture.currentGestureIdBits,
                                          mPointerController->getDisplayId());
         }
     } else {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+        mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
     }
 
     // Show or hide the pointer if needed.
     switch (mPointerGesture.currentGestureMode) {
-        case PointerGesture::NEUTRAL:
-        case PointerGesture::QUIET:
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH &&
-                mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
+        case PointerGesture::Mode::NEUTRAL:
+        case PointerGesture::Mode::QUIET:
+            if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH &&
+                mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) {
                 // Remind the user of where the pointer is after finishing a gesture with spots.
-                mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
+                mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL);
             }
             break;
-        case PointerGesture::TAP:
-        case PointerGesture::TAP_DRAG:
-        case PointerGesture::BUTTON_CLICK_OR_DRAG:
-        case PointerGesture::HOVER:
-        case PointerGesture::PRESS:
-        case PointerGesture::SWIPE:
+        case PointerGesture::Mode::TAP:
+        case PointerGesture::Mode::TAP_DRAG:
+        case PointerGesture::Mode::BUTTON_CLICK_OR_DRAG:
+        case PointerGesture::Mode::HOVER:
+        case PointerGesture::Mode::PRESS:
+        case PointerGesture::Mode::SWIPE:
             // Unfade the pointer when the current gesture manipulates the
             // area directly under the pointer.
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
             break;
-        case PointerGesture::FREEFORM:
+        case PointerGesture::Mode::FREEFORM:
             // Fade the pointer when the current gesture manipulates a different
             // area and there are spots to guide the user experience.
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
-                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+            if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
+                mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
             } else {
-                mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+                mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
             }
             break;
     }
@@ -2379,12 +2382,12 @@
 
     // Update last coordinates of pointers that have moved so that we observe the new
     // pointer positions at the same time as other pointers that have just gone up.
-    bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP ||
-            mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG ||
-            mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG ||
-            mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
-            mPointerGesture.currentGestureMode == PointerGesture::SWIPE ||
-            mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+    bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM;
     bool moveNeeded = false;
     if (down && !cancelPreviousGesture && !finishPreviousGesture &&
         !mPointerGesture.lastGestureIdBits.isEmpty() &&
@@ -2467,7 +2470,7 @@
     }
 
     // Send motion events for hover.
-    if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+    if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                        buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
@@ -2537,7 +2540,7 @@
 
     // Remove any current spots.
     if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
 }
@@ -2553,7 +2556,7 @@
         ALOGD("Gestures: Processing timeout");
 #endif
 
-        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+        if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
                 // The tap/drag timeout has not yet expired.
                 getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime +
@@ -2566,7 +2569,7 @@
                 *outFinishPreviousGesture = true;
 
                 mPointerGesture.activeGestureId = -1;
-                mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+                mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
                 mPointerGesture.currentGestureIdBits.clear();
 
                 mPointerVelocityControl.reset();
@@ -2583,14 +2586,14 @@
 
     // Update the velocity tracker.
     {
-        VelocityTracker::Position positions[MAX_POINTERS];
-        uint32_t count = 0;
-        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+        std::vector<VelocityTracker::Position> positions;
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            positions[count].x = pointer.x * mPointerXMovementScale;
-            positions[count].y = pointer.y * mPointerYMovementScale;
+            float x = pointer.x * mPointerXMovementScale;
+            float y = pointer.y * mPointerYMovementScale;
+            positions.push_back({x, y});
         }
         mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
                                                     positions);
@@ -2598,9 +2601,9 @@
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
     // to NEUTRAL, then we should not generate tap event.
-    if (mPointerGesture.lastGestureMode != PointerGesture::HOVER &&
-        mPointerGesture.lastGestureMode != PointerGesture::TAP &&
-        mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+    if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER &&
+        mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP &&
+        mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP_DRAG) {
         mPointerGesture.resetTap();
     }
 
@@ -2633,15 +2636,16 @@
     } else {
         isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
         if (!isQuietTime) {
-            if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
-                 mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
-                 mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) &&
+            if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
+                 mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
+                 mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
                 currentFingerCount < 2) {
                 // Enter quiet time when exiting swipe or freeform state.
                 // This is to prevent accidentally entering the hover state and flinging the
                 // pointer when finishing a swipe and there is still one pointer left onscreen.
                 isQuietTime = true;
-            } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG &&
+            } else if (mPointerGesture.lastGestureMode ==
+                               PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
                        currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
                 // Enter quiet time when releasing the button and there are still two or more
                 // fingers down.  This may indicate that one finger was used to press the button
@@ -2661,12 +2665,12 @@
         ALOGD("Gestures: QUIET for next %0.3fms",
               (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
 #endif
-        if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+        if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
             *outFinishPreviousGesture = true;
         }
 
         mPointerGesture.activeGestureId = -1;
-        mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+        mPointerGesture.currentGestureMode = PointerGesture::Mode::QUIET;
         mPointerGesture.currentGestureIdBits.clear();
 
         mPointerVelocityControl.reset();
@@ -2690,7 +2694,7 @@
               activeTouchId, currentFingerCount);
 #endif
         // Reset state when just starting.
-        if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+        if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
             *outFinishPreviousGesture = true;
             mPointerGesture.activeGestureId = 0;
         }
@@ -2744,7 +2748,7 @@
         float x, y;
         mPointerController->getPosition(&x, &y);
 
-        mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+        mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
         mPointerGesture.currentGestureIdBits.clear();
         mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
         mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2757,15 +2761,15 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
     } else if (currentFingerCount == 0) {
         // Case 3. No fingers down and button is not pressed. (NEUTRAL)
-        if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+        if (mPointerGesture.lastGestureMode != PointerGesture::Mode::NEUTRAL) {
             *outFinishPreviousGesture = true;
         }
 
         // Watch for taps coming out of HOVER or TAP_DRAG mode.
         // Checking for taps after TAP_DRAG allows us to detect double-taps.
         bool tapped = false;
-        if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
-             mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
+        if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::HOVER ||
+             mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
             lastFingerCount == 1) {
             if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
                 float x, y;
@@ -2781,7 +2785,7 @@
                                                        mConfig.pointerGestureTapDragInterval);
 
                     mPointerGesture.activeGestureId = 0;
-                    mPointerGesture.currentGestureMode = PointerGesture::TAP;
+                    mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP;
                     mPointerGesture.currentGestureIdBits.clear();
                     mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
                     mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2824,7 +2828,7 @@
             ALOGD("Gestures: NEUTRAL");
 #endif
             mPointerGesture.activeGestureId = -1;
-            mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+            mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
         }
     } else if (currentFingerCount == 1) {
@@ -2834,14 +2838,14 @@
         // When in TAP_DRAG, emit MOVE events at the pointer location.
         ALOG_ASSERT(activeTouchId >= 0);
 
-        mPointerGesture.currentGestureMode = PointerGesture::HOVER;
-        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+        mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
+        if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-                    mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+                    mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
                 } else {
 #if DEBUG_GESTURES
                     ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
@@ -2854,8 +2858,8 @@
                       (when - mPointerGesture.tapUpTime) * 0.000001f);
 #endif
             }
-        } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
-            mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+        } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
+            mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
         }
 
         float deltaX = 0, deltaY = 0;
@@ -2878,7 +2882,7 @@
         }
 
         bool down;
-        if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+        if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
 #if DEBUG_GESTURES
             ALOGD("Gestures: TAP_DRAG");
 #endif
@@ -2887,7 +2891,7 @@
 #if DEBUG_GESTURES
             ALOGD("Gestures: HOVER");
 #endif
-            if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+            if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
                 *outFinishPreviousGesture = true;
             }
             mPointerGesture.activeGestureId = 0;
@@ -2933,9 +2937,9 @@
 
         bool settled = when >=
                 mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
-        if (mPointerGesture.lastGestureMode != PointerGesture::PRESS &&
-            mPointerGesture.lastGestureMode != PointerGesture::SWIPE &&
-            mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+        if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
+            mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
+            mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
             *outFinishPreviousGesture = true;
         } else if (!settled && currentFingerCount > lastFingerCount) {
             // Additional pointers have gone down but not yet settled.
@@ -2953,7 +2957,7 @@
         }
 
         if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
-            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+            mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
             mPointerGesture.activeGestureId = 0;
             mPointerGesture.referenceIdBits.clear();
             mPointerVelocityControl.reset();
@@ -3005,7 +3009,7 @@
         }
 
         // Consider transitions from PRESS to SWIPE or MULTITOUCH.
-        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+        if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
             float dist[MAX_POINTER_ID + 1];
             int32_t distOverThreshold = 0;
             for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
@@ -3027,7 +3031,7 @@
                           currentFingerCount);
 #endif
                     *outCancelPreviousGesture = true;
-                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                    mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                 } else {
                     // There are exactly two pointers.
                     BitSet32 idBits(mCurrentCookedState.fingerIdBits);
@@ -3046,7 +3050,7 @@
                               mutualDistance, mPointerGestureMaxSwipeWidth);
 #endif
                         *outCancelPreviousGesture = true;
-                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                        mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                     } else {
                         // There are two pointers.  Wait for both pointers to start moving
                         // before deciding whether this is a SWIPE or FREEFORM gesture.
@@ -3077,7 +3081,7 @@
                                       mConfig.pointerGestureMultitouchMinDistance, cosine,
                                       mConfig.pointerGestureSwipeTransitionAngleCosine);
 #endif
-                                mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+                                mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
                             } else {
                                 // Pointers are moving in different directions.  Switch to FREEFORM.
 #if DEBUG_GESTURES
@@ -3089,13 +3093,13 @@
                                       mConfig.pointerGestureSwipeTransitionAngleCosine);
 #endif
                                 *outCancelPreviousGesture = true;
-                                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                                mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                             }
                         }
                     }
                 }
             }
-        } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+        } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
             // Switch from SWIPE to FREEFORM if additional pointers go down.
             // Cancel previous gesture.
             if (currentFingerCount > 2) {
@@ -3104,13 +3108,13 @@
                       currentFingerCount);
 #endif
                 *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
             }
         }
 
         // Move the reference points based on the overall group motion of the fingers
         // except in PRESS mode while waiting for a transition to occur.
-        if (mPointerGesture.currentGestureMode != PointerGesture::PRESS &&
+        if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
             (commonDeltaX || commonDeltaY)) {
             for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
                 uint32_t id = idBits.clearFirstMarkedBit();
@@ -3133,8 +3137,8 @@
         }
 
         // Report gestures.
-        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
-            mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+        if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
             // PRESS or SWIPE mode.
 #if DEBUG_GESTURES
             ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
@@ -3155,7 +3159,7 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                                                                  mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-        } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+        } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
             // FREEFORM mode.
 #if DEBUG_GESTURES
             ALOGD("Gestures: FREEFORM activeTouchId=%d,"
@@ -3168,7 +3172,7 @@
 
             BitSet32 mappedTouchIdBits;
             BitSet32 usedGestureIdBits;
-            if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+            if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
                 // Initially, assign the active gesture id to the active touch point
                 // if there is one.  No other touch id bits are mapped yet.
                 if (!*outCancelPreviousGesture) {
@@ -3393,17 +3397,16 @@
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
                                              bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t displayId = mViewport.displayId;
 
     if (down || hovering) {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+        mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
         mPointerController->clearSpots();
         mPointerController->setButtonState(mCurrentRawState.buttonState);
-        mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+        mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
     } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
     }
-    displayId = mPointerController->getDisplayId();
+    int32_t displayId = mPointerController->getDisplayId();
 
     float xCursorPosition;
     float yCursorPosition;
@@ -3560,7 +3563,11 @@
         if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
             action = AMOTION_EVENT_ACTION_DOWN;
         } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
-            action = AMOTION_EVENT_ACTION_UP;
+            if ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) {
+                action = AMOTION_EVENT_ACTION_CANCEL;
+            } else {
+                action = AMOTION_EVENT_ACTION_UP;
+            }
         } else {
             // Can't happen.
             ALOG_ASSERT(false);
@@ -3568,7 +3575,7 @@
     }
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
+    if (mDeviceMode == DeviceMode::POINTER) {
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
     }
     const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
@@ -3813,9 +3820,9 @@
 
 #if DEBUG_POINTER_ASSIGNMENT
                 ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
-                for (size_t i = 0; i < heapSize; i++) {
-                    ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
-                          heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+                for (size_t j = 0; j < heapSize; j++) {
+                    ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
+                          heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
                 }
 #endif
             }
@@ -3907,7 +3914,7 @@
 
 std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
+        if (mDeviceMode == DeviceMode::POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
         } else {
             return std::make_optional(mViewport.displayId);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 58bfc5c..df6581d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,8 @@
 #ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
 #define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
 
+#include <stdint.h>
+
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
 #include "EventHub.h"
@@ -24,8 +26,6 @@
 #include "InputReaderBase.h"
 #include "TouchButtonAccumulator.h"
 
-#include <stdint.h>
-
 namespace android {
 
 /* Raw axis information from the driver. */
@@ -71,7 +71,7 @@
 
     uint32_t pointerCount;
     Pointer pointers[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits;
+    BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
     uint32_t idToIndex[MAX_POINTER_ID + 1];
 
     RawPointerData();
@@ -90,6 +90,7 @@
     inline void clearIdBits() {
         hoveringIdBits.clear();
         touchingIdBits.clear();
+        canceledIdBits.clear();
     }
 
     inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; }
@@ -102,7 +103,7 @@
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits;
+    BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits;
     uint32_t idToIndex[MAX_POINTER_ID + 1];
 
     CookedPointerData();
@@ -128,30 +129,31 @@
     inline bool isTouching(uint32_t pointerIndex) const {
         return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
     }
+
+    inline bool hasPointerCoordsForId(uint32_t id) const { return validIdBits.hasBit(id); }
 };
 
 class TouchInputMapper : public InputMapper {
 public:
     explicit TouchInputMapper(InputDeviceContext& deviceContext);
-    virtual ~TouchInputMapper();
+    ~TouchInputMapper() override;
 
-    virtual uint32_t getSources() override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    uint32_t getSources() override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
+    void reset(nsecs_t when) override;
+    void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-                                       const int32_t* keyCodes, uint8_t* outFlags) override;
+    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+    bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags) override;
 
-    virtual void cancelTouch(nsecs_t when) override;
-    virtual void timeoutExpired(nsecs_t when) override;
-    virtual void updateExternalStylusState(const StylusState& state) override;
-    virtual std::optional<int32_t> getAssociatedDisplayId() override;
+    void cancelTouch(nsecs_t when) override;
+    void timeoutExpired(nsecs_t when) override;
+    void updateExternalStylusState(const StylusState& state) override;
+    std::optional<int32_t> getAssociatedDisplayId() override;
 
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
@@ -177,12 +179,12 @@
     // Input sources and device mode.
     uint32_t mSource;
 
-    enum DeviceMode {
-        DEVICE_MODE_DISABLED,   // input is disabled
-        DEVICE_MODE_DIRECT,     // direct mapping (touchscreen)
-        DEVICE_MODE_UNSCALED,   // unscaled mapping (touchpad)
-        DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
-        DEVICE_MODE_POINTER,    // pointer mapping (pointer)
+    enum class DeviceMode {
+        DISABLED,   // input is disabled
+        DIRECT,     // direct mapping (touchscreen)
+        UNSCALED,   // unscaled mapping (touchpad)
+        NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
+        POINTER,    // pointer mapping (pointer)
     };
     DeviceMode mDeviceMode;
 
@@ -191,11 +193,11 @@
 
     // Immutable configuration parameters.
     struct Parameters {
-        enum DeviceType {
-            DEVICE_TYPE_TOUCH_SCREEN,
-            DEVICE_TYPE_TOUCH_PAD,
-            DEVICE_TYPE_TOUCH_NAVIGATION,
-            DEVICE_TYPE_POINTER,
+        enum class DeviceType {
+            TOUCH_SCREEN,
+            TOUCH_PAD,
+            TOUCH_NAVIGATION,
+            POINTER,
         };
 
         DeviceType deviceType;
@@ -205,9 +207,9 @@
         bool hasButtonUnderPad;
         std::string uniqueDisplayId;
 
-        enum GestureMode {
-            GESTURE_MODE_SINGLE_TOUCH,
-            GESTURE_MODE_MULTI_TOUCH,
+        enum class GestureMode {
+            SINGLE_TOUCH,
+            MULTI_TOUCH,
         };
         GestureMode gestureMode;
 
@@ -217,13 +219,13 @@
     // Immutable calibration parameters in parsed form.
     struct Calibration {
         // Size
-        enum SizeCalibration {
-            SIZE_CALIBRATION_DEFAULT,
-            SIZE_CALIBRATION_NONE,
-            SIZE_CALIBRATION_GEOMETRIC,
-            SIZE_CALIBRATION_DIAMETER,
-            SIZE_CALIBRATION_BOX,
-            SIZE_CALIBRATION_AREA,
+        enum class SizeCalibration {
+            DEFAULT,
+            NONE,
+            GEOMETRIC,
+            DIAMETER,
+            BOX,
+            AREA,
         };
 
         SizeCalibration sizeCalibration;
@@ -236,11 +238,11 @@
         bool sizeIsSummed;
 
         // Pressure
-        enum PressureCalibration {
-            PRESSURE_CALIBRATION_DEFAULT,
-            PRESSURE_CALIBRATION_NONE,
-            PRESSURE_CALIBRATION_PHYSICAL,
-            PRESSURE_CALIBRATION_AMPLITUDE,
+        enum class PressureCalibration {
+            DEFAULT,
+            NONE,
+            PHYSICAL,
+            AMPLITUDE,
         };
 
         PressureCalibration pressureCalibration;
@@ -248,30 +250,30 @@
         float pressureScale;
 
         // Orientation
-        enum OrientationCalibration {
-            ORIENTATION_CALIBRATION_DEFAULT,
-            ORIENTATION_CALIBRATION_NONE,
-            ORIENTATION_CALIBRATION_INTERPOLATED,
-            ORIENTATION_CALIBRATION_VECTOR,
+        enum class OrientationCalibration {
+            DEFAULT,
+            NONE,
+            INTERPOLATED,
+            VECTOR,
         };
 
         OrientationCalibration orientationCalibration;
 
         // Distance
-        enum DistanceCalibration {
-            DISTANCE_CALIBRATION_DEFAULT,
-            DISTANCE_CALIBRATION_NONE,
-            DISTANCE_CALIBRATION_SCALED,
+        enum class DistanceCalibration {
+            DEFAULT,
+            NONE,
+            SCALED,
         };
 
         DistanceCalibration distanceCalibration;
         bool haveDistanceScale;
         float distanceScale;
 
-        enum CoverageCalibration {
-            COVERAGE_CALIBRATION_DEFAULT,
-            COVERAGE_CALIBRATION_NONE,
-            COVERAGE_CALIBRATION_BOX,
+        enum class CoverageCalibration {
+            DEFAULT,
+            NONE,
+            BOX,
         };
 
         CoverageCalibration coverageCalibration;
@@ -376,7 +378,7 @@
     nsecs_t mDownTime;
 
     // The pointer controller, or null if the device is not a pointer.
-    sp<PointerControllerInterface> mPointerController;
+    std::shared_ptr<PointerControllerInterface> mPointerController;
 
     std::vector<VirtualKey> mVirtualKeys;
 
@@ -524,16 +526,16 @@
         uint64_t distance : 48; // squared distance
     };
 
-    enum PointerUsage {
-        POINTER_USAGE_NONE,
-        POINTER_USAGE_GESTURES,
-        POINTER_USAGE_STYLUS,
-        POINTER_USAGE_MOUSE,
+    enum class PointerUsage {
+        NONE,
+        GESTURES,
+        STYLUS,
+        MOUSE,
     };
     PointerUsage mPointerUsage;
 
     struct PointerGesture {
-        enum Mode {
+        enum class Mode {
             // No fingers, button is not pressed.
             // Nothing happening.
             NEUTRAL,
@@ -646,9 +648,9 @@
             firstTouchTime = LLONG_MIN;
             activeTouchId = -1;
             activeGestureId = -1;
-            currentGestureMode = NEUTRAL;
+            currentGestureMode = Mode::NEUTRAL;
             currentGestureIdBits.clear();
-            lastGestureMode = NEUTRAL;
+            lastGestureMode = Mode::NEUTRAL;
             lastGestureIdBits.clear();
             downTime = 0;
             velocityTracker.clear();
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 7665680..ac7c266 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -39,23 +39,17 @@
     // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
 }
 
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
                                   int32_t token) {
 #if DEBUG_VIBRATOR
     std::string patternStr;
-    for (size_t i = 0; i < patternSize; i++) {
-        if (i != 0) {
-            patternStr += ", ";
-        }
-        patternStr += StringPrintf("%" PRId64, pattern[i]);
-    }
+    dumpPattern(patternStr);
     ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
           patternStr.c_str(), repeat, token);
 #endif
 
     mVibrating = true;
-    memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
-    mPatternSize = patternSize;
+    mPattern = pattern;
     mRepeat = repeat;
     mToken = token;
     mIndex = -1;
@@ -85,7 +79,7 @@
 
 void VibratorInputMapper::nextStep() {
     mIndex += 1;
-    if (size_t(mIndex) >= mPatternSize) {
+    if (size_t(mIndex) >= mPattern.size()) {
         if (mRepeat < 0) {
             // We are done.
             stopVibrating();
@@ -94,13 +88,14 @@
         mIndex = mRepeat;
     }
 
-    bool vibratorOn = mIndex & 1;
-    nsecs_t duration = mPattern[mIndex];
-    if (vibratorOn) {
+    const VibrationElement& element = mPattern[mIndex];
+    if (element.isOn()) {
 #if DEBUG_VIBRATOR
-        ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+        std::string description = element.toString();
+        ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+              description.c_str());
 #endif
-        getDeviceContext().vibrate(duration);
+        getDeviceContext().vibrate(element);
     } else {
 #if DEBUG_VIBRATOR
         ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -108,10 +103,12 @@
         getDeviceContext().cancelVibrate();
     }
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    mNextStepTime = now + duration;
+    std::chrono::nanoseconds duration =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
+    mNextStepTime = now + duration.count();
     getContext()->requestTimeoutAtTime(mNextStepTime);
 #if DEBUG_VIBRATOR
-    ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+    ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
 #endif
 }
 
@@ -126,6 +123,25 @@
 void VibratorInputMapper::dump(std::string& dump) {
     dump += INDENT2 "Vibrator Input Mapper:\n";
     dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+    if (mVibrating) {
+        dump += INDENT3 "Pattern: ";
+        dumpPattern(dump);
+        dump += "\n";
+        dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat);
+    }
+}
+
+void VibratorInputMapper::dumpPattern(std::string& dump) const {
+    dump += "[";
+
+    for (auto it = mPattern.begin(); it != mPattern.end(); ++it) {
+        dump += it->toString();
+        if (std::next(it) != mPattern.end()) {
+            dump += ", ";
+        }
+    }
+
+    dump += "]";
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index f69fdde..bfa5ec1 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+    virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
                          int32_t token) override;
     virtual void cancelVibrate(int32_t token) override;
     virtual void timeoutExpired(nsecs_t when) override;
@@ -38,13 +38,13 @@
 
 private:
     bool mVibrating;
-    nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
-    size_t mPatternSize;
+    std::vector<VibrationElement> mPattern;
     ssize_t mRepeat;
     int32_t mToken;
     ssize_t mIndex;
     nsecs_t mNextStepTime;
 
+    void dumpPattern(std::string& dump) const;
     void nextStep();
     void stopVibrating();
 };
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 73d2272..8cb7194 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -27,14 +27,26 @@
         "libinputflinger_defaults",
     ],
     srcs: [
+        "AnrTracker_test.cpp",
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
-        "TestInputListener.cpp",
+        "IInputFlingerQuery.aidl",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
+        "InputFlingerService_test.cpp",
+        "TestInputListener.cpp",
         "UinputDevice.cpp",
     ],
+    aidl: {
+        include_dirs: [
+            "frameworks/native/libs/input",
+        ],
+    },
+    static_libs: [
+        "libc++fs"
+    ],
     require_root: true,
+    test_suites: ["device-tests"],
 }
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
new file mode 100644
index 0000000..b561da1
--- /dev/null
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../AnrTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+// --- AnrTrackerTest ---
+
+/**
+ * Add a single entry and ensure it's returned as first, even if the token isn't valid
+ */
+TEST(AnrTrackerTest, SingleEntry_First) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(tracker.firstToken(), nullptr);
+}
+
+TEST(AnrTrackerTest, MultipleEntries_RemoveToken) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(1, token1);
+    tracker.insert(2, token2);
+    tracker.insert(3, token1);
+    tracker.insert(4, token2);
+    tracker.insert(5, token1);
+
+    tracker.eraseToken(token1);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+}
+
+TEST(AnrTrackerTest, AddAndRemove_Empty) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    tracker.insert(1, nullptr);
+    ASSERT_FALSE(tracker.empty());
+
+    tracker.erase(1, nullptr);
+    ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, Clear) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+    tracker.clear();
+    ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, SingleToken_MaintainsOrder) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    tracker.insert(2, nullptr);
+    tracker.insert(5, nullptr);
+    tracker.insert(0, nullptr);
+
+    ASSERT_EQ(0, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(5, token2);
+    tracker.insert(0, token2);
+
+    ASSERT_EQ(0, tracker.firstTimeout());
+    ASSERT_EQ(token2, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(2, token2);
+    tracker.insert(10, token2);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+    // Doesn't matter which token is returned - both are valid results
+    ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(2, token2);
+    tracker.insert(10, token2);
+
+    tracker.erase(2, token2);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+    ASSERT_EQ(token1, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, Empty_DoesntCrash) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+    // Can't call firstToken() if tracker.empty()
+}
+
+TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+
+    // Remove with non-matching timestamp
+    tracker.erase(2, nullptr);
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+
+    // Remove with non-matching token
+    tracker.erase(1, new BBinder());
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+
+    // Remove with both non-matching
+    tracker.erase(2, new BBinder());
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
index 0dea8d7..fd9d9d5 100644
--- a/services/inputflinger/tests/BlockingQueue_test.cpp
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -26,7 +26,7 @@
 // --- BlockingQueueTest ---
 
 /**
- * Sanity check of basic pop and push operation.
+ * Validate basic pop and push operation.
  */
 TEST(BlockingQueueTest, Queue_AddAndRemove) {
     constexpr size_t capacity = 10;
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 71731b0..ef68a84 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -199,3 +199,76 @@
         lastEventTime = event.when; // Ensure all returned events are monotonic
     }
 }
+
+// --- BitArrayTest ---
+class BitArrayTest : public testing::Test {
+protected:
+    static constexpr size_t SINGLE_ELE_BITS = 32UL;
+    static constexpr size_t MULTI_ELE_BITS = 256UL;
+
+    virtual void SetUp() override {
+        mBitmaskSingle.loadFromBuffer(mBufferSingle);
+        mBitmaskMulti.loadFromBuffer(mBufferMulti);
+    }
+
+    android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle;
+    android::BitArray<MULTI_ELE_BITS> mBitmaskMulti;
+
+private:
+    const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = {
+            0x800F0F0FUL // bit 0 - 31
+    };
+    const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = {
+            0xFFFFFFFFUL, // bit 0 - 31
+            0x01000001UL, // bit 32 - 63
+            0x00000000UL, // bit 64 - 95
+            0x80000000UL, // bit 96 - 127
+            0x00000000UL, // bit 128 - 159
+            0x00000000UL, // bit 160 - 191
+            0x80000008UL, // bit 192 - 223
+            0x00000000UL, // bit 224 - 255
+    };
+};
+
+TEST_F(BitArrayTest, SetBit) {
+    ASSERT_TRUE(mBitmaskSingle.test(0));
+    ASSERT_TRUE(mBitmaskSingle.test(31));
+    ASSERT_FALSE(mBitmaskSingle.test(7));
+
+    ASSERT_TRUE(mBitmaskMulti.test(32));
+    ASSERT_TRUE(mBitmaskMulti.test(56));
+    ASSERT_FALSE(mBitmaskMulti.test(192));
+    ASSERT_TRUE(mBitmaskMulti.test(223));
+    ASSERT_FALSE(mBitmaskMulti.test(255));
+}
+
+TEST_F(BitArrayTest, AnyBit) {
+    ASSERT_TRUE(mBitmaskSingle.any(31, 32));
+    ASSERT_FALSE(mBitmaskSingle.any(12, 16));
+
+    ASSERT_TRUE(mBitmaskMulti.any(31, 32));
+    ASSERT_FALSE(mBitmaskMulti.any(33, 33));
+    ASSERT_TRUE(mBitmaskMulti.any(32, 55));
+    ASSERT_TRUE(mBitmaskMulti.any(33, 57));
+    ASSERT_FALSE(mBitmaskMulti.any(33, 55));
+    ASSERT_FALSE(mBitmaskMulti.any(130, 190));
+
+    ASSERT_FALSE(mBitmaskMulti.any(128, 195));
+    ASSERT_TRUE(mBitmaskMulti.any(128, 196));
+    ASSERT_TRUE(mBitmaskMulti.any(128, 224));
+    ASSERT_FALSE(mBitmaskMulti.any(255, 256));
+}
+
+TEST_F(BitArrayTest, SetBit_InvalidBitIndex) {
+    ASSERT_FALSE(mBitmaskSingle.test(32));
+    ASSERT_FALSE(mBitmaskMulti.test(256));
+}
+
+TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) {
+    ASSERT_FALSE(mBitmaskSingle.any(32, 32));
+    ASSERT_FALSE(mBitmaskSingle.any(33, 34));
+
+    ASSERT_FALSE(mBitmaskMulti.any(256, 256));
+    ASSERT_FALSE(mBitmaskMulti.any(257, 258));
+    ASSERT_FALSE(mBitmaskMulti.any(0, 0));
+}
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
new file mode 100644
index 0000000..5c8a8da
--- /dev/null
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlingerQuery
+{
+    /* Test interfaces */
+    void getInputWindows(out InputWindowInfo[] inputHandles);
+    void getInputChannels(out InputChannel[] channels);
+    void getLastFocusRequest(out FocusRequest request);
+    void resetInputManager();
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 13e8354..40471b2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,6 +29,9 @@
 #include <vector>
 
 using android::base::StringPrintf;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+using namespace android::flag_operators;
 
 namespace android::inputdispatcher {
 
@@ -123,9 +126,18 @@
 
     // This function must be called soon after the expected ANR timer starts,
     // because we are also checking how much time has passed.
-    void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
-                                  const sp<InputApplicationHandle>& expectedApplication,
-                                  const sp<IBinder>& expectedToken) {
+    void assertNotifyAnrWasCalled(
+            std::chrono::nanoseconds timeout,
+            const std::shared_ptr<InputApplicationHandle>& expectedApplication,
+            const sp<IBinder>& expectedToken) {
+        std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData;
+        ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
+        ASSERT_EQ(expectedApplication, anrData.first);
+        ASSERT_EQ(expectedToken, anrData.second);
+    }
+
+    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+            std::chrono::nanoseconds timeout) {
         const std::chrono::time_point start = std::chrono::steady_clock::now();
         std::unique_lock lock(mLock);
         std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
@@ -136,16 +148,34 @@
         // before checking if ANR was called.
         // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
         // it some time to act. 100ms seems reasonable.
-        mNotifyAnr.wait_for(lock, timeToWait,
-                            [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; });
+        mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+            return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+        });
         const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
-        ASSERT_TRUE(mNotifyAnrWasCalled);
+        if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+            ADD_FAILURE() << "Did not receive ANR callback";
+            return {};
+        }
         // Ensure that the ANR didn't get raised too early. We can't be too strict here because
         // the dispatcher started counting before this function was called
-        ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for
-        mNotifyAnrWasCalled = false;
-        ASSERT_EQ(expectedApplication, mLastAnrApplication);
-        ASSERT_EQ(expectedToken, mLastAnrWindowToken);
+        if (std::chrono::abs(timeout - waited) > 100ms) {
+            ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+                          << "ms, but waited "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+                          << "ms instead";
+        }
+        std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> result =
+                std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
+        mAnrApplications.pop();
+        mAnrWindowTokens.pop();
+        return result;
+    }
+
+    void assertNotifyAnrWasNotCalled() {
+        std::scoped_lock lock(mLock);
+        ASSERT_TRUE(mAnrApplications.empty());
+        ASSERT_TRUE(mAnrWindowTokens.empty());
     }
 
     void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -153,6 +183,8 @@
         mConfig.keyRepeatDelay = delay;
     }
 
+    void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -161,36 +193,37 @@
     std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
     // ANR handling
-    bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false;
-    sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock);
-    sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock);
+    std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
     std::chrono::nanoseconds mAnrTimeout = 0ms;
 
-    virtual void notifyConfigurationChanged(nsecs_t when) override {
+    void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
-                              const sp<IBinder>& windowToken, const std::string&) override {
+    std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application,
+                                       const sp<IBinder>& windowToken,
+                                       const std::string&) override {
         std::scoped_lock lock(mLock);
-        mLastAnrApplication = application;
-        mLastAnrWindowToken = windowToken;
-        mNotifyAnrWasCalled = true;
+        mAnrApplications.push(application);
+        mAnrWindowTokens.push(windowToken);
         mNotifyAnr.notify_all();
-        return mAnrTimeout.count();
+        return mAnrTimeout;
     }
 
-    virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+    void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
-    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+    void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
-    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+    void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+
+    void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+    bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
         std::scoped_lock lock(mLock);
         switch (inputEvent->getType()) {
             case AINPUT_EVENT_TYPE_KEY: {
@@ -208,22 +241,20 @@
         return true;
     }
 
-    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+    void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
 
-    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+    void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
 
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
-                                                  uint32_t) override {
+    nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
         return 0;
     }
 
-    virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
-                                      KeyEvent*) override {
+    bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
         return false;
     }
 
-    virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
-                              uint32_t policyFlags) override {
+    void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+                      uint32_t policyFlags) override {
         std::scoped_lock lock(mLock);
         /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
          * essentially a passthrough for notifySwitch.
@@ -231,13 +262,11 @@
         mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
     }
 
-    virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+    void pokeUserActivity(nsecs_t, int32_t) override {}
 
-    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
-        return false;
-    }
+    bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
 
-    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+    void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
         std::scoped_lock lock(mLock);
         mOnPointerDownToken = newToken;
     }
@@ -266,70 +295,6 @@
     }
 };
 
-// --- HmacKeyManagerTest ---
-
-class HmacKeyManagerTest : public testing::Test {
-protected:
-    HmacKeyManager mHmacKeyManager;
-};
-
-/**
- * Ensure that separate calls to sign the same data are generating the same key.
- * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
- * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
- * tests.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
-    KeyEvent event = getTestKeyEvent();
-    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
-
-    std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
-    std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
-    ASSERT_EQ(hmac1, hmac2);
-}
-
-/**
- * Ensure that changes in VerifiedKeyEvent produce a different hmac.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
-    KeyEvent event = getTestKeyEvent();
-    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
-    std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
-
-    verifiedEvent.deviceId += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.source += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.eventTimeNanos += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.displayId += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.action += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.downTimeNanos += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.flags += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.keyCode += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.scanCode += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.metaState += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
-    verifiedEvent.repeatCount += 1;
-    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-}
-
 // --- InputDispatcherTest ---
 
 class InputDispatcherTest : public testing::Test {
@@ -364,6 +329,18 @@
             ALOGE("%s", to.c_str());
         }
     }
+
+    void setFocusedWindow(const sp<InputWindowHandle>& window,
+                          const sp<InputWindowHandle>& focusedWindow = nullptr) {
+        FocusRequest request;
+        request.token = window->getToken();
+        if (focusedWindow) {
+            request.focusedToken = focusedWindow->getToken();
+        }
+        request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+        request.displayId = window->getInfo()->displayId;
+        mDispatcher->setFocusedWindow(request);
+    }
 };
 
 
@@ -375,18 +352,18 @@
                      INVALID_HMAC,
                      /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
                      ARBITRARY_TIME);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
                      INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
                      ARBITRARY_TIME, ARBITRARY_TIME);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
 
@@ -406,113 +383,110 @@
     constexpr int32_t metaState = AMETA_NONE;
     constexpr MotionClassification classification = MotionClassification::NONE;
 
+    ui::Transform identityTransform;
     // Rejects undefined motion actions.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
-                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
-                     1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_DOWN |
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
-                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+                     pointerCoords);
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_DOWN |
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
-                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+                     pointerCoords);
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_UP |
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
-                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+                     pointerCoords);
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_UP |
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
-                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+                     0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+                     pointerCoords);
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
-                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with 0 pointers.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
-                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with more than MAX_POINTERS pointers.";
 
     // Rejects motion events with invalid pointer ids.
     pointerProperties[0].id = -1;
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
-                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerProperties[0].id = MAX_POINTER_ID + 1;
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
-                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
 
     // Rejects motion events with duplicate pointer ids.
@@ -520,13 +494,12 @@
     pointerProperties[1].id = 1;
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
-                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+                                            InputEventInjectionSync::NONE, 0ms, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
@@ -560,7 +533,8 @@
     FakeApplicationHandle() {
         mInfo.name = "Fake Application";
         mInfo.token = new BBinder();
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.dispatchingTimeoutMillis =
+                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
     }
     virtual ~FakeApplicationHandle() {}
 
@@ -568,16 +542,16 @@
         return true;
     }
 
-    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
-        mInfo.dispatchingTimeout = timeout.count();
+    void setDispatchingTimeout(std::chrono::milliseconds timeout) {
+        mInfo.dispatchingTimeoutMillis = timeout.count();
     }
 };
 
 class FakeInputReceiver {
 public:
-    explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+    explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
           : mName(name) {
-        mConsumer = std::make_unique<InputConsumer>(clientChannel);
+        mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
     }
 
     InputEvent* consume() {
@@ -643,7 +617,7 @@
         ASSERT_NE(nullptr, event) << mName.c_str()
                                   << ": consumer should have returned non-NULL event.";
         ASSERT_EQ(expectedEventType, event->getType())
-                << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+                << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
                 << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
         EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -688,9 +662,24 @@
 
     void assertNoEvents() {
         InputEvent* event = consume();
-        ASSERT_EQ(nullptr, event)
-                << mName.c_str()
-                << ": should not have received any events, so consume() should return NULL";
+        if (event == nullptr) {
+            return;
+        }
+        if (event->getType() == AINPUT_EVENT_TYPE_KEY) {
+            KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+            ADD_FAILURE() << "Received key event "
+                          << KeyEvent::actionToString(keyEvent.getAction());
+        } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) {
+            MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+            ADD_FAILURE() << "Received motion event "
+                          << MotionEvent::actionToString(motionEvent.getAction());
+        } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) {
+            FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+            ADD_FAILURE() << "Received focus event, hasFocus = "
+                          << (focusEvent.getHasFocus() ? "true" : "false");
+        }
+        FAIL() << mName.c_str()
+               << ": should not have received any events, so consume() should return NULL";
     }
 
     sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
@@ -707,74 +696,83 @@
     static const int32_t WIDTH = 600;
     static const int32_t HEIGHT = 800;
 
-    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+    FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
                      const sp<InputDispatcher>& dispatcher, const std::string name,
-                     int32_t displayId, sp<IBinder> token = nullptr)
+                     int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
           : mName(name) {
-        if (token == nullptr) {
-            sp<InputChannel> serverChannel, clientChannel;
-            InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-            mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
-            dispatcher->registerInputChannel(serverChannel);
-            token = serverChannel->getConnectionToken();
+        if (token == std::nullopt) {
+            base::Result<std::unique_ptr<InputChannel>> channel =
+                    dispatcher->createInputChannel(name);
+            token = (*channel)->getConnectionToken();
+            mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
         }
 
         inputApplicationHandle->updateInfo();
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
 
-        mInfo.token = token;
+        mInfo.token = *token;
         mInfo.id = sId++;
         mInfo.name = name;
-        mInfo.layoutParamsFlags = 0;
-        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.type = InputWindowInfo::Type::APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.frameLeft = 0;
         mInfo.frameTop = 0;
         mInfo.frameRight = WIDTH;
         mInfo.frameBottom = HEIGHT;
+        mInfo.transform.set(0, 0);
         mInfo.globalScaleFactor = 1.0;
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
         mInfo.visible = true;
-        mInfo.canReceiveKeys = true;
-        mInfo.hasFocus = false;
+        mInfo.focusable = false;
         mInfo.hasWallpaper = false;
         mInfo.paused = false;
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
-        mInfo.inputFeatures = 0;
         mInfo.displayId = displayId;
     }
 
     virtual bool updateInfo() { return true; }
 
-    void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
+    void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+
+    void setVisible(bool visible) { mInfo.visible = visible; }
 
     void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
-        mInfo.dispatchingTimeout = timeout.count();
+        mInfo.dispatchingTimeout = timeout;
     }
 
+    void setPaused(bool paused) { mInfo.paused = paused; }
+
     void setFrame(const Rect& frame) {
         mInfo.frameLeft = frame.left;
         mInfo.frameTop = frame.top;
         mInfo.frameRight = frame.right;
         mInfo.frameBottom = frame.bottom;
+        mInfo.transform.set(frame.left, frame.top);
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(frame);
     }
 
-    void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+    void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
 
-    void setWindowScale(float xScale, float yScale) {
-        mInfo.windowXScale = xScale;
-        mInfo.windowYScale = yScale;
+    void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
+
+    void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+        mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
     }
 
+    void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
     void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
                      expectedFlags);
     }
 
+    void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+    }
+
     void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
             int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
@@ -826,12 +824,12 @@
                                      expectedFlags);
     }
 
-    std::optional<uint32_t> receiveEvent() {
+    std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         if (mInputReceiver == nullptr) {
             ADD_FAILURE() << "Invalid receive event on window with no receiver";
             return std::nullopt;
         }
-        return mInputReceiver->receiveEvent();
+        return mInputReceiver->receiveEvent(outEvent);
     }
 
     void finishEvent(uint32_t sequenceNum) {
@@ -847,8 +845,12 @@
     }
 
     void assertNoEvents() {
-        ASSERT_NE(mInputReceiver, nullptr)
-                << "Call 'assertNoEvents' on a window with an InputReceiver";
+        if (mInputReceiver == nullptr &&
+            mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
+            return; // Can't receive events if the window does not have input channel
+        }
+        ASSERT_NE(nullptr, mInputReceiver)
+                << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL";
         mInputReceiver->assertNoEvents();
     }
 
@@ -864,8 +866,11 @@
 
 std::atomic<int32_t> FakeWindowHandle::sId{1};
 
-static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
-                         int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKey(
+        const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+        int32_t displayId = ADISPLAY_ID_NONE,
+        InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
@@ -875,59 +880,172 @@
                      repeatCount, currentTime, currentTime);
 
     // Inject event until dispatch out.
-    return dispatcher->injectInputEvent(
-            &event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
-            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
+                                        injectionTimeout,
+                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 }
 
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
-                             int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
+                                               int32_t displayId = ADISPLAY_ID_NONE) {
     return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
 }
 
-static int32_t injectMotionEvent(
+static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
+                                             int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
+
+class PointerBuilder {
+public:
+    PointerBuilder(int32_t id, int32_t toolType) {
+        mProperties.clear();
+        mProperties.id = id;
+        mProperties.toolType = toolType;
+        mCoords.clear();
+    }
+
+    PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+    PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+    PointerBuilder& axis(int32_t axis, float value) {
+        mCoords.setAxisValue(axis, value);
+        return *this;
+    }
+
+    PointerProperties buildProperties() const { return mProperties; }
+
+    PointerCoords buildCoords() const { return mCoords; }
+
+private:
+    PointerProperties mProperties;
+    PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+    MotionEventBuilder(int32_t action, int32_t source) {
+        mAction = action;
+        mSource = source;
+        mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+
+    MotionEventBuilder& eventTime(nsecs_t eventTime) {
+        mEventTime = eventTime;
+        return *this;
+    }
+
+    MotionEventBuilder& displayId(int32_t displayId) {
+        mDisplayId = displayId;
+        return *this;
+    }
+
+    MotionEventBuilder& actionButton(int32_t actionButton) {
+        mActionButton = actionButton;
+        return *this;
+    }
+
+    MotionEventBuilder& buttonState(int32_t actionButton) {
+        mActionButton = actionButton;
+        return *this;
+    }
+
+    MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+        mRawXCursorPosition = rawXCursorPosition;
+        return *this;
+    }
+
+    MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+        mRawYCursorPosition = rawYCursorPosition;
+        return *this;
+    }
+
+    MotionEventBuilder& pointer(PointerBuilder pointer) {
+        mPointers.push_back(pointer);
+        return *this;
+    }
+
+    MotionEvent build() {
+        std::vector<PointerProperties> pointerProperties;
+        std::vector<PointerCoords> pointerCoords;
+        for (const PointerBuilder& pointer : mPointers) {
+            pointerProperties.push_back(pointer.buildProperties());
+            pointerCoords.push_back(pointer.buildCoords());
+        }
+
+        // Set mouse cursor position for the most common cases to avoid boilerplate.
+        if (mSource == AINPUT_SOURCE_MOUSE &&
+            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+            mPointers.size() == 1) {
+            mRawXCursorPosition = pointerCoords[0].getX();
+            mRawYCursorPosition = pointerCoords[0].getY();
+        }
+
+        MotionEvent event;
+        ui::Transform identityTransform;
+        event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
+                         mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+                         mButtonState, MotionClassification::NONE, identityTransform,
+                         /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
+                         mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
+                         pointerProperties.data(), pointerCoords.data());
+
+        return event;
+    }
+
+private:
+    int32_t mAction;
+    int32_t mSource;
+    nsecs_t mEventTime;
+    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+    int32_t mActionButton{0};
+    int32_t mButtonState{0};
+    float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+    float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+    std::vector<PointerBuilder> mPointers;
+};
+
+static InputEventInjectionResult injectMotionEvent(
+        const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
+    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+                                        injectionTimeout,
+                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static InputEventInjectionResult injectMotionEvent(
         const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
         const PointF& position,
         const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                        AMOTION_EVENT_INVALID_CURSOR_POSITION}) {
-    MotionEvent event;
-    PointerProperties pointerProperties[1];
-    PointerCoords pointerCoords[1];
-
-    pointerProperties[0].clear();
-    pointerProperties[0].id = 0;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
-
-    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    // Define a valid motion down event.
-    event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
-                     /* actionButton */ 0,
-                     /* flags */ 0,
-                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
-                     /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
-                     /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
-                     currentTime, currentTime,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                                        AMOTION_EVENT_INVALID_CURSOR_POSITION},
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+        nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
+    MotionEvent event = MotionEventBuilder(action, source)
+                                .displayId(displayId)
+                                .eventTime(eventTime)
+                                .rawXCursorPosition(cursorPosition.x)
+                                .rawYCursorPosition(cursorPosition.y)
+                                .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                                 .x(position.x)
+                                                 .y(position.y))
+                                .build();
 
     // Inject event until dispatch out.
-    return dispatcher->injectInputEvent(
-            &event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
-            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+    return injectMotionEvent(dispatcher, event);
 }
 
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
-                                int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
+                                                  int32_t source, int32_t displayId,
+                                                  const PointF& location = {100, 200}) {
     return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
 }
 
-static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
-                              int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher,
+                                                int32_t source, int32_t displayId,
+                                                const PointF& location = {100, 200}) {
     return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
 }
 
@@ -979,14 +1097,14 @@
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
             ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
     // Window should receive motion event.
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1001,17 +1119,17 @@
  * called twice.
  */
 TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {50, 50}))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
     // Window should receive motion event.
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1024,18 +1142,18 @@
  * when finding touched windows.
  */
 TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {50, 50}))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
     // Window should receive motion event.
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1043,107 +1161,225 @@
 
 // The foreground window should receive the first touch down event.
 TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
             ADISPLAY_ID_DEFAULT);
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
     // Top window should receive the touch down event. Second window should not receive anything.
     windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowSecond->assertNoEvents();
 }
 
-TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
-            ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
-            ADISPLAY_ID_DEFAULT);
+TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> windowLeft =
+            new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    windowLeft->setFrame(Rect(0, 0, 600, 800));
+    windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> windowRight =
+            new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    windowRight->setFrame(Rect(600, 0, 1200, 800));
+    windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
-    // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    // Display should have only one focused window
-    windowSecond->setFocus(true);
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
 
-    windowSecond->consumeFocusEvent(true);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    // Start cursor position in right window so that we can move the cursor to left window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(900)
+                                                         .y(400))
+                                        .build()));
+    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 
-    // Focused window should receive event.
-    windowTop->assertNoEvents();
-    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+    // Move cursor into left window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+    // Inject a series of mouse events for a mouse click
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .buttonState(0)
+                                        .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(0)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    // Move mouse cursor back to right window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(900)
+                                                         .y(400))
+                                        .build()));
+    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 }
 
-TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
-            ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
-            ADISPLAY_ID_DEFAULT);
+// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
+// directly in this test.
+TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 1200, 800));
+    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
-    // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
-    windowTop->setFocus(true);
-    windowSecond->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
-    windowTop->consumeFocusEvent(true);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 
-    // Top focused window should receive event.
-    windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
-    windowSecond->assertNoEvents();
-}
+    // Inject a series of mouse events for a mouse click
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 
-TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
-            ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
-            ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .buttonState(0)
+                                        .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 
-    // Set focused application.
-    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(0)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
 
-    windowTop->setFocus(true);
-    windowSecond->setFocus(true);
-    // Release channel for window is no longer valid.
-    windowTop->releaseChannel();
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
-    windowSecond->consumeFocusEvent(true);
-
-    // Test inject a key down, should dispatch to a valid window.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-
-    // Top window is invalid, so it should not receive any input event.
-    windowTop->assertNoEvents();
-    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(400))
+                                        .build()));
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 }
 
 TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     sp<FakeWindowHandle> windowLeft =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     windowLeft->setFrame(Rect(0, 0, 600, 800));
-    windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> windowRight =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     windowRight->setFrame(Rect(600, 0, 1200, 800));
-    windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -1151,7 +1387,7 @@
 
     // Inject an event with coordinate in the area of right window, with mouse cursor in the area of
     // left window. This event should be dispatched to the left window.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
                                 ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
     windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1159,12 +1395,14 @@
 }
 
 TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
-    window->setFocus(true);
+    window->setFocusable(true);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
     window->consumeFocusEvent(true);
 
     NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
@@ -1182,7 +1420,7 @@
 }
 
 TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
@@ -1205,7 +1443,7 @@
 }
 
 TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a couple of windows
     sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
@@ -1240,7 +1478,7 @@
 }
 
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     PointF touchPoint = {10, 10};
 
@@ -1296,21 +1534,21 @@
 }
 
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
             "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
-            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                          InputWindowInfo::Flag::SPLIT_TOUCH);
 
     // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
             "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
-            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -1360,12 +1598,13 @@
 }
 
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
-    window->setFocus(true);
+    window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
 
     window->consumeFocusEvent(true);
 
@@ -1377,7 +1616,7 @@
 }
 
 TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
@@ -1392,7 +1631,7 @@
 
 // If a window is touchable, but does not have focus, it should receive motion events, but not keys
 TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
@@ -1416,10 +1655,9 @@
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
                         int32_t displayId, bool isGestureMonitor = false) {
-        sp<InputChannel> serverChannel, clientChannel;
-        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-        mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
-        dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+        base::Result<std::unique_ptr<InputChannel>> channel =
+                dispatcher->createInputMonitor(displayId, isGestureMonitor, name);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
     }
 
     sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1429,6 +1667,10 @@
                                      expectedDisplayId, expectedFlags);
     }
 
+    std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+
+    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
     void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
         mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
                                      expectedDisplayId, expectedFlags);
@@ -1447,7 +1689,7 @@
 
 // Tests for gesture monitors
 TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1455,35 +1697,37 @@
     FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
                                                       true /*isGestureMonitor*/);
 
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
 }
 
 TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    window->setFocus(true);
+    window->setFocusable(true);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
     window->consumeFocusEvent(true);
 
     FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
                                                       true /*isGestureMonitor*/);
 
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     monitor.assertNoEvents();
 }
 
 TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1491,9 +1735,9 @@
     FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
                                                       true /*isGestureMonitor*/);
 
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
 
@@ -1501,14 +1745,29 @@
 
     mDispatcher->pilferPointers(monitor.getToken());
 
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+    std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
+    ASSERT_TRUE(consumeSeq);
+
+    mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+    monitor.finishEvent(*consumeSeq);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
 TEST_F(InputDispatcherTest, TestMoveEvent) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
@@ -1539,52 +1798,58 @@
  * and the action of enabling / disabling.
  */
 TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
 
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    window->setFocus(true);
+    window->setFocusable(true);
 
     SCOPED_TRACE("Check default value of touch mode");
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
     window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
 
     SCOPED_TRACE("Remove the window to trigger focus loss");
-    window->setFocus(false);
+    window->setFocusable(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
 
     SCOPED_TRACE("Disable touch mode");
     mDispatcher->setInTouchMode(false);
-    window->setFocus(true);
+    window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
     window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
 
     SCOPED_TRACE("Remove the window to trigger focus loss");
-    window->setFocus(false);
+    window->setFocusable(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
 
     SCOPED_TRACE("Enable touch mode again");
     mDispatcher->setInTouchMode(true);
-    window->setFocus(true);
+    window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
     window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
 
     window->assertNoEvents();
 }
 
 TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    window->setFocus(true);
+    window->setFocusable(true);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
     window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
 
     NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
@@ -1614,7 +1879,7 @@
 }
 
 TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
 
@@ -1650,12 +1915,214 @@
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_IsConsistent) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+    std::array<uint8_t, 32> hmac1 = mDispatcher->sign(verifiedEvent);
+    std::array<uint8_t, 32> hmac2 = mDispatcher->sign(verifiedEvent);
+    ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+    std::array<uint8_t, 32> initialHmac = mDispatcher->sign(verifiedEvent);
+
+    verifiedEvent.deviceId += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.source += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.eventTimeNanos += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.displayId += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.action += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.downTimeNanos += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.flags += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.keyCode += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.scanCode += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.metaState += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+    verifiedEvent.repeatCount += 1;
+    ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> windowTop =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond =
+            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // Top window is also focusable but is not granted focus.
+    windowTop->setFocusable(true);
+    windowSecond->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    setFocusedWindow(windowSecond);
+
+    windowSecond->consumeFocusEvent(true);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Focused window should receive event.
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+    windowTop->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    window->setFocusable(true);
+    // Release channel for window is no longer valid.
+    window->releaseChannel();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
+    // Test inject a key down, should timeout.
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+    // window channel is invalid, so it should not receive any input event.
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // Window is not focusable.
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
+    // Test inject a key down, should timeout.
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+    // window is invalid, so it should not receive any input event.
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> windowTop =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond =
+            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    windowTop->setFocusable(true);
+    windowSecond->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    setFocusedWindow(windowTop);
+    windowTop->consumeFocusEvent(true);
+
+    setFocusedWindow(windowSecond, windowTop);
+    windowSecond->consumeFocusEvent(true);
+    windowTop->consumeFocusEvent(false);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Focused window should receive event.
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> windowTop =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond =
+            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    windowTop->setFocusable(true);
+    windowSecond->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    setFocusedWindow(windowSecond, windowTop);
+
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+    // Event should be dropped.
+    windowTop->assertNoEvents();
+    windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> previousFocusedWindow =
+            new FakeWindowHandle(application, mDispatcher, "previousFocusedWindow",
+                                 ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    window->setFocusable(true);
+    previousFocusedWindow->setFocusable(true);
+    window->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}});
+    setFocusedWindow(previousFocusedWindow);
+    previousFocusedWindow->consumeFocusEvent(true);
+
+    // Requesting focus on invisible window takes focus from currently focused window.
+    setFocusedWindow(window);
+    previousFocusedWindow->consumeFocusEvent(false);
+
+    // Injected key goes to pending queue.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+    // Window does not get focus event or key down.
+    window->assertNoEvents();
+
+    // Window becomes visible.
+    window->setVisible(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    // Window receives focus event.
+    window->consumeFocusEvent(true);
+    // Focused window receives key down.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
     static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000;   // 40 ms
 
-    sp<FakeApplicationHandle> mApp;
+    std::shared_ptr<FakeApplicationHandle> mApp;
     sp<FakeWindowHandle> mWindow;
 
     virtual void SetUp() override {
@@ -1669,17 +2136,18 @@
     }
 
     void setUpWindow() {
-        mApp = new FakeApplicationHandle();
+        mApp = std::make_shared<FakeApplicationHandle>();
         mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
-        mWindow->setFocus(true);
+        mWindow->setFocusable(true);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
-
+        setFocusedWindow(mWindow);
         mWindow->consumeFocusEvent(true);
     }
 
-    void sendAndConsumeKeyDown() {
+    void sendAndConsumeKeyDown(int32_t deviceId) {
         NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+        keyArgs.deviceId = deviceId;
         keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
         mDispatcher->notifyKey(&keyArgs);
 
@@ -1701,8 +2169,9 @@
         EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
     }
 
-    void sendAndConsumeKeyUp() {
+    void sendAndConsumeKeyUp(int32_t deviceId) {
         NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+        keyArgs.deviceId = deviceId;
         keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
         mDispatcher->notifyKey(&keyArgs);
 
@@ -1713,21 +2182,59 @@
 };
 
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
-    sendAndConsumeKeyDown();
+    sendAndConsumeKeyDown(1 /* deviceId */);
+    for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+        expectKeyRepeatOnce(repeatCount);
+    }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeatFromTwoDevices) {
+    sendAndConsumeKeyDown(1 /* deviceId */);
+    for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+        expectKeyRepeatOnce(repeatCount);
+    }
+    sendAndConsumeKeyDown(2 /* deviceId */);
+    /* repeatCount will start from 1 for deviceId 2 */
     for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
         expectKeyRepeatOnce(repeatCount);
     }
 }
 
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) {
-    sendAndConsumeKeyDown();
+    sendAndConsumeKeyDown(1 /* deviceId */);
     expectKeyRepeatOnce(1 /*repeatCount*/);
-    sendAndConsumeKeyUp();
+    sendAndConsumeKeyUp(1 /* deviceId */);
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
+    sendAndConsumeKeyDown(1 /* deviceId */);
+    expectKeyRepeatOnce(1 /*repeatCount*/);
+    sendAndConsumeKeyDown(2 /* deviceId */);
+    expectKeyRepeatOnce(1 /*repeatCount*/);
+    // Stale key up from device 1.
+    sendAndConsumeKeyUp(1 /* deviceId */);
+    // Device 2 is still down, keep repeating
+    expectKeyRepeatOnce(2 /*repeatCount*/);
+    expectKeyRepeatOnce(3 /*repeatCount*/);
+    // Device 2 key up
+    sendAndConsumeKeyUp(2 /* deviceId */);
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatStopsAfterRepeatingKeyUp) {
+    sendAndConsumeKeyDown(1 /* deviceId */);
+    expectKeyRepeatOnce(1 /*repeatCount*/);
+    sendAndConsumeKeyDown(2 /* deviceId */);
+    expectKeyRepeatOnce(1 /*repeatCount*/);
+    // Device 2 which holds the key repeating goes up, expect the repeating to stop.
+    sendAndConsumeKeyUp(2 /* deviceId */);
+    // Device 1 still holds key down, but the repeating was already stopped
     mWindow->assertNoEvents();
 }
 
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
-    sendAndConsumeKeyDown();
+    sendAndConsumeKeyDown(1 /* deviceId */);
     for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
         InputEvent* repeatEvent = mWindow->consume();
         ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
@@ -1737,7 +2244,7 @@
 }
 
 TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
-    sendAndConsumeKeyDown();
+    sendAndConsumeKeyDown(1 /* deviceId */);
 
     std::unordered_set<int32_t> idSet;
     for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
@@ -1756,17 +2263,18 @@
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
-        application1 = new FakeApplicationHandle();
+        application1 = std::make_shared<FakeApplicationHandle>();
         windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
                 ADISPLAY_ID_DEFAULT);
 
         // Set focus window for primary display, but focused display would be second one.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
-        windowInPrimary->setFocus(true);
+        windowInPrimary->setFocusable(true);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+        setFocusedWindow(windowInPrimary);
         windowInPrimary->consumeFocusEvent(true);
 
-        application2 = new FakeApplicationHandle();
+        application2 = std::make_shared<FakeApplicationHandle>();
         windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
                 SECOND_DISPLAY_ID);
         // Set focus to second display window.
@@ -1774,66 +2282,67 @@
         mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
         // Set focus window for second display.
         mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
-        windowInSecondary->setFocus(true);
+        windowInSecondary->setFocusable(true);
         mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+        setFocusedWindow(windowInSecondary);
         windowInSecondary->consumeFocusEvent(true);
     }
 
     virtual void TearDown() override {
         InputDispatcherTest::TearDown();
 
-        application1.clear();
+        application1.reset();
         windowInPrimary.clear();
-        application2.clear();
+        application2.reset();
         windowInSecondary.clear();
     }
 
 protected:
-    sp<FakeApplicationHandle> application1;
+    std::shared_ptr<FakeApplicationHandle> application1;
     sp<FakeWindowHandle> windowInPrimary;
-    sp<FakeApplicationHandle> application2;
+    std::shared_ptr<FakeApplicationHandle> application2;
     sp<FakeWindowHandle> windowInSecondary;
 };
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
     // Test touch down on primary display.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test touch down on second display.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
 }
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
     // Test inject a key down with display id specified.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test inject a key down without display id specified.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 
     // Remove all windows in secondary display.
     mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
 
-    // Expect old focus should receive a cancel event.
+    // Old focus should receive a cancel event.
     windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
                                     AKEY_EVENT_FLAG_CANCELED);
 
     // Test inject a key down, should timeout because of no target window.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeFocusEvent(false);
     windowInSecondary->assertNoEvents();
@@ -1847,18 +2356,18 @@
             FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test touch down on primary display.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
     monitorInSecondary.assertNoEvents();
 
     // Test touch down on second display.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -1867,9 +2376,9 @@
     // Test inject a non-pointer motion event.
     // If specific a display, it will dispatch to the focused window of particular display,
     // or it will dispatch to the focused window of focused display.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-        AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
@@ -1885,14 +2394,31 @@
             FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test inject a key down.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
     monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) {
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}});
+    setFocusedWindow(secondWindowInPrimary);
+    windowInPrimary->consumeFocusEvent(false);
+    secondWindowInPrimary->consumeFocusEvent(true);
+
+    // Test inject a key down.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->assertNoEvents();
+    secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
 class InputFilterTest : public InputDispatcherTest {
 protected:
     static constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -1970,25 +2496,27 @@
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
-        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
         mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
                 ADISPLAY_ID_DEFAULT);
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
-        mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mFocusedWindow =
                 new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
-        mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-        mFocusedWindow->setFocus(true);
+        mFocusedWindow->setFocusable(true);
 
         // Expect one focus window exist in display.
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+        setFocusedWindow(mFocusedWindow);
         mFocusedWindow->consumeFocusEvent(true);
     }
 
@@ -2009,10 +2537,10 @@
 // DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
 // the onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {20, 20}))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     mUnfocusedWindow->consumeMotionDown();
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2023,9 +2551,9 @@
 // DOWN on the window that doesn't have focus. Ensure no window received the
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     mFocusedWindow->consumeMotionDown();
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2035,8 +2563,8 @@
 // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
 // have focus. Ensure no window received the onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
-            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2048,10 +2576,10 @@
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus,
         OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                FOCUSED_WINDOW_TOUCH_POINT))
-            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     mFocusedWindow->consumeMotionDown();
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2064,19 +2592,20 @@
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
-        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
         mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
                                         ADISPLAY_ID_DEFAULT);
         // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
         // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
-        mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
-                                      InputWindowInfo::FLAG_SPLIT_TOUCH);
+        mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
         mWindow1->setFrame(Rect(0, 0, 100, 100));
 
         mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
                                         ADISPLAY_ID_DEFAULT, mWindow1->getToken());
-        mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
-                                      InputWindowInfo::FLAG_SPLIT_TOUCH);
+        mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
         mWindow2->setFrame(Rect(100, 100, 200, 200));
 
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -2088,9 +2617,8 @@
 
     // Helper function to convert the point from screen coordinates into the window's space
     static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
-        float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
-        float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
-        return {x, y};
+        vec2 vals = windowInfo->transform.transform(point.x, point.y);
+        return {vals.x, vals.y};
     }
 
     void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
@@ -2120,133 +2648,123 @@
                     << ", got " << motionEvent.getY(i);
         }
     }
+
+    void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+                                 std::vector<PointF> expectedPoints) {
+        NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
+                                                         ADISPLAY_ID_DEFAULT, touchedPoints);
+        mDispatcher->notifyMotion(&motionArgs);
+
+        // Always consume from window1 since it's the window that has the InputReceiver
+        consumeMotionEvent(mWindow1, action, expectedPoints);
+    }
 };
 
 TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
     // Touch Window 1
     PointF touchedPoint = {10, 10};
     PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
 
     // Release touch on Window 1
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-    // consume the UP event
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
 
     // Touch Window 2
     touchedPoint = {150, 150};
     expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
-
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-
-    // Consuming from window1 since it's the window that has the InputReceiver
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
 }
 
-TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
+    // Set scale value for window2
     mWindow2->setWindowScale(0.5f, 0.5f);
 
     // Touch Window 1
     PointF touchedPoint = {10, 10};
     PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
-
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
     // Release touch on Window 1
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-    // consume the UP event
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
 
     // Touch Window 2
     touchedPoint = {150, 150};
     expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
-    mDispatcher->notifyMotion(&motionArgs);
-
-    // Consuming from window1 since it's the window that has the InputReceiver
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+    // Update the transform so rotation is set
+    mWindow2->setWindowTransform(0, -1, 1, 0);
+    expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
 }
 
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
     mWindow2->setWindowScale(0.5f, 0.5f);
 
     // Touch Window 1
     std::vector<PointF> touchedPoints = {PointF{10, 10}};
     std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
     int32_t actionPointerDown =
             AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-    touchedPoints.emplace_back(PointF{150, 150});
-    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchedPoints.push_back(PointF{150, 150});
+    expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
 
-    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
+    // Release Window 2
+    int32_t actionPointerUp =
+            AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+    expectedPoints.pop_back();
 
-    // Consuming from window1 since it's the window that has the InputReceiver
-    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+    // Update the transform so rotation is set for Window 2
+    mWindow2->setWindowTransform(0, -1, 1, 0);
+    expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
 }
 
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
     mWindow2->setWindowScale(0.5f, 0.5f);
 
     // Touch Window 1
     std::vector<PointF> touchedPoints = {PointF{10, 10}};
     std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
     int32_t actionPointerDown =
             AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-    touchedPoints.emplace_back(PointF{150, 150});
-    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchedPoints.push_back(PointF{150, 150});
+    expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
 
-    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-
-    // Consuming from window1 since it's the window that has the InputReceiver
-    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
 
     // Move both windows
     touchedPoints = {{20, 20}, {175, 175}};
     expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
                       getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
 
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+    // Release Window 2
+    int32_t actionPointerUp =
+            AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+    expectedPoints.pop_back();
+
+    // Touch Window 2
+    mWindow2->setWindowTransform(0, -1, 1, 0);
+    expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+
+    // Move both windows
+    touchedPoints = {{20, 20}, {175, 175}};
+    expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+                      getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
 }
 
 TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
@@ -2255,57 +2773,44 @@
     // Touch Window 1
     std::vector<PointF> touchedPoints = {PointF{10, 10}};
     std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
-    NotifyMotionArgs motionArgs =
-            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
-                               ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
 
     // Touch Window 2
     int32_t actionPointerDown =
             AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-    touchedPoints.emplace_back(PointF{150, 150});
-    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+    touchedPoints.push_back(PointF{150, 150});
+    expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
 
-    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-
-    // Consuming from window1 since it's the window that has the InputReceiver
-    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+    touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
 
     // Move both windows
     touchedPoints = {{20, 20}, {175, 175}};
     expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
                       getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
 
-    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
-                                    ADISPLAY_ID_DEFAULT, touchedPoints);
-    mDispatcher->notifyMotion(&motionArgs);
-
-    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+    touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
 }
 
 class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
-        mApplication = new FakeApplicationHandle();
+        mApplication = std::make_shared<FakeApplicationHandle>();
         mApplication->setDispatchingTimeout(20ms);
         mWindow =
                 new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFrame(Rect(0, 0, 30, 30));
-        mWindow->setDispatchingTimeout(10ms);
-        mWindow->setFocus(true);
+        mWindow->setDispatchingTimeout(30ms);
+        mWindow->setFocusable(true);
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
-        mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
 
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+        setFocusedWindow(mWindow);
         mWindow->consumeFocusEvent(true);
     }
 
@@ -2315,54 +2820,949 @@
     }
 
 protected:
-    sp<FakeApplicationHandle> mApplication;
+    std::shared_ptr<FakeApplicationHandle> mApplication;
     sp<FakeWindowHandle> mWindow;
     static constexpr PointF WINDOW_LOCATION = {20, 20};
 
     void tapOnWindow() {
-        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                   injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                    WINDOW_LOCATION));
-        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                   injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                  WINDOW_LOCATION));
     }
 };
 
+// Send a tap and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
+    tapOnWindow();
+    mWindow->consumeMotionDown();
+    mWindow->consumeMotionUp();
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send a regular key and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+    // Key will not go to window because we have no focused window.
+    // The 'no focused window' ANR timer should start instead.
+
+    // Now, the focused application goes away.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, nullptr);
+    // The key should get dropped and there should be no ANR.
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
 // Send an event to the app and have the app not respond right away.
-// Make sure that ANR is raised
+// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
 TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                WINDOW_LOCATION));
 
-    // Also, overwhelm the socket to make sure ANR starts
-    for (size_t i = 0; i < 100; i++) {
-        injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
-                          ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i});
-    }
-
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
     mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+
+    // The remaining lines are not really needed for the test, but kept as a sanity check
+    mWindow->finishEvent(*sequenceNum);
+    mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+                          ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
 // Send a key to the app and have the app not respond right away.
 TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
     // Inject a key, and don't respond - expect that ANR is called.
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
     ASSERT_TRUE(sequenceNum);
-
-    // Start ANR process by sending a 2nd key, which would trigger the check for whether
-    // waitQueue is empty
-    injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1);
-
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken());
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    // taps on the window work as normal
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+    mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    // Once a focused event arrives, we get an ANR for this application
+    // We specify the injection timeout to be smaller than the application timeout, to ensure that
+    // injection times out (instead of failing).
+    const InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+    const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+// If the policy wants to keep waiting on the focused window to be added, make sure
+// that this timeout extension is honored and ANR is raised again.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+    const std::chrono::duration timeout = 5ms;
+    mFakePolicy->setAnrTimeout(timeout);
+
+    // Once a focused event arrives, we get an ANR for this application
+    // We specify the injection timeout to be smaller than the application timeout, to ensure that
+    // injection times out (instead of failing).
+    const InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+    const std::chrono::duration appTimeout =
+            mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+
+    // After the extended time has passed, ANR should be raised again
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+    // If we stop extending the timeout, dispatcher should go to idle.
+    // Another ANR may be raised during this time
+    mFakePolicy->setAnrTimeout(0ms);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    // Once a focused event arrives, we get an ANR for this application
+    const InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+
+    const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+    // Future focused events get dropped right away
+    ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mWindow->assertNoEvents();
+}
+
+/**
+ * Ensure that the implementation is valid. Since we are using multiset to keep track of the
+ * ANR timeouts, we are allowing entries with identical timestamps in the same connection.
+ * If we process 1 of the events, but ANR on the second event with the same timestamp,
+ * the ANR mechanism should still work.
+ *
+ * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the
+ * DOWN event, while not responding on the second one.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                      ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+                      {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                       AMOTION_EVENT_INVALID_CURSOR_POSITION},
+                      500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
+
+    // Now send ACTION_UP, with identical timestamp
+    injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                      ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+                      {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                       AMOTION_EVENT_INVALID_CURSOR_POSITION},
+                      500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
+
+    // We have now sent down and up. Let's consume first event and then ANR on the second.
+    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+}
+
+// If an app is not responding to a key event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+
+    // Stuck on the ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+    // New tap will go to the gesture monitor, but not to the window
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+    monitor.assertNoEvents();
+}
+
+// If an app is not responding to a motion event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeMotionDown();
+    // Stuck on the ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+    // New tap will go to the gesture monitor, but not to the window
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+    monitor.assertNoEvents();
+}
+
+// If a window is unresponsive, then you get anr. if the window later catches up and starts to
+// process events, you don't get an anr. When the window later becomes unresponsive again, you
+// get an ANR again.
+// 1. tap -> block on ACTION_UP -> receive ANR
+// 2. consume all pending events (= queue becomes healthy again)
+// 3. tap again -> block on ACTION_UP again -> receive ANR second time
+TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
+    tapOnWindow();
+
+    mWindow->consumeMotionDown();
+    // Block on ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mWindow->consumeMotionUp(); // Now the connection should be healthy again
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+
+    tapOnWindow();
+    mWindow->consumeMotionDown();
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mWindow->consumeMotionUp();
+
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+}
+
+// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
+// is honored
+TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
+    const std::chrono::duration timeout = 5ms;
+    mFakePolicy->setAnrTimeout(timeout);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+
+    const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
+                                          mWindow->getToken());
+
+    // Since the policy wanted to extend ANR, make sure it is called again after the extension
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->setAnrTimeout(0ms);
+    std::this_thread::sleep_for(windowTimeout);
+    // We are not checking if ANR has been called, because it may have been called again by the
+    // time we set the timeout to 0
+
+    // When the policy finally says stop, we should get ACTION_CANCEL
+    mWindow->consumeMotionDown();
+    mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+                          ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    mWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ *  KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+    mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+    tapOnWindow();
+    std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection will "succeed" because we will eventually give up and send the key to the focused
+    // window even if motions are still being processed. But because the injection timeout is short,
+    // we will receive INJECTION_TIMED_OUT as the result.
+
+    InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+    // Key will not be sent to the window, yet, because the window is still processing events
+    // and the key remains pending, waiting for the touch events to be processed
+    std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    std::this_thread::sleep_for(500ms);
+    // if we wait long enough though, dispatcher will give up, and still send the key
+    // to the focused window, even though we have not yet finished the motion event
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    mWindow->finishEvent(*downSequenceNum);
+    mWindow->finishEvent(*upSequenceNum);
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not go to the focused window until the motion is processed.
+ * If then a new motion comes in, then the pending key event should be going to the currently
+ * focused window right away.
+ */
+TEST_F(InputDispatcherSingleWindowAnr,
+       PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+    mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+    tapOnWindow();
+    std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection is async, so it will succeed
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+    // At this point, key is still pending, and should not be sent to the application yet.
+    std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    // Now tap down again. It should cause the pending key to go to the focused window right away.
+    tapOnWindow();
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
+    // the other events yet. We can finish events in any order.
+    mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
+    mWindow->finishEvent(*upSequenceNum);   // first tap's ACTION_UP
+    mWindow->consumeMotionDown();
+    mWindow->consumeMotionUp();
+    mWindow->assertNoEvents();
+}
+
+class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApplication = std::make_shared<FakeApplicationHandle>();
+        mApplication->setDispatchingTimeout(10ms);
+        mUnfocusedWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
+        mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
+        mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                                   InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
+                                   InputWindowInfo::Flag::SPLIT_TOUCH);
+
+        mFocusedWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->setDispatchingTimeout(30ms);
+        mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+        mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                                 InputWindowInfo::Flag::SPLIT_TOUCH);
+
+        // Set focused application.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+        mFocusedWindow->setFocusable(true);
+
+        // Expect one focus window exist in display.
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+        setFocusedWindow(mFocusedWindow);
+        mFocusedWindow->consumeFocusEvent(true);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+
+        mUnfocusedWindow.clear();
+        mFocusedWindow.clear();
+    }
+
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mUnfocusedWindow;
+    sp<FakeWindowHandle> mFocusedWindow;
+    static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
+    static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75};
+    static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40};
+
+    void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); }
+
+    void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); }
+
+private:
+    void tap(const PointF& location) {
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   location));
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                 location));
+    }
+};
+
+// If we have 2 windows that are both unresponsive, the one with the shortest timeout
+// should be ANR'd first.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mFocusedWindow->consumeMotionDown();
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    // We consumed all events, so no ANR
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+    std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(unfocusedSequenceNum);
+    std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
+    ASSERT_TRUE(focusedSequenceNum);
+
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    mFocusedWindow->finishEvent(*focusedSequenceNum);
+    mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// If we have 2 windows with identical timeouts that are both unresponsive,
+// it doesn't matter which order they should have ANR.
+// But we should receive ANR for both.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
+    // Set the timeout for unfocused window to match the focused window
+    mUnfocusedWindow->setDispatchingTimeout(10ms);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+    tapOnFocusedWindow();
+    // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
+    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData1 =
+            mFakePolicy->getNotifyAnrData(10ms);
+    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData2 =
+            mFakePolicy->getNotifyAnrData(0ms);
+
+    // We don't know which window will ANR first. But both of them should happen eventually.
+    ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
+                mFocusedWindow->getToken() == anrData2.second);
+    ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
+                mUnfocusedWindow->getToken() == anrData2.second);
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If a window is already not responding, the second tap on the same window should be ignored.
+// We should also log an error to account for the dropped event (not tested here).
+// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
+TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
+    tapOnFocusedWindow();
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    // Receive the events, but don't respond
+    std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+    ASSERT_TRUE(downEventSequenceNum);
+    std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+    ASSERT_TRUE(upEventSequenceNum);
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    // Tap once again
+    // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             FOCUSED_WINDOW_LOCATION));
+    // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
+    // valid touch target
+    mUnfocusedWindow->assertNoEvents();
+
+    // Consume the first tap
+    mFocusedWindow->finishEvent(*downEventSequenceNum);
+    mFocusedWindow->finishEvent(*upEventSequenceNum);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // The second tap did not go to the focused window
+    mFocusedWindow->assertNoEvents();
+    // should not have another ANR after the window just became healthy again
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If you tap outside of all windows, there will not be ANR
+TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               LOCATION_OUTSIDE_ALL_WINDOWS));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Since the focused window is paused, tapping on it should not produce any events
+TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
+    mFocusedWindow->setPaused(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+
+    std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // Should not ANR because the window is paused, and touches shouldn't go to it
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    mFocusedWindow->assertNoEvents();
+    mUnfocusedWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ * If a different window becomes focused at this time, the key should go to that window instead.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ *  KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
+    // Set a long ANR timeout to prevent it from triggering
+    mFocusedWindow->setDispatchingTimeout(2s);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+    tapOnUnfocusedWindow();
+    std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection will succeed because we will eventually give up and send the key to the focused
+    // window even if motions are still being processed.
+
+    InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+    // Key will not be sent to the window, yet, because the window is still processing events
+    // and the key remains pending, waiting for the touch events to be processed
+    std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
+    mFocusedWindow->setFocusable(false);
+    mUnfocusedWindow->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+    setFocusedWindow(mUnfocusedWindow);
+
+    // Focus events should precede the key events
+    mUnfocusedWindow->consumeFocusEvent(true);
+    mFocusedWindow->consumeFocusEvent(false);
+
+    // Finish the tap events, which should unblock dispatcher
+    mUnfocusedWindow->finishEvent(*downSequenceNum);
+    mUnfocusedWindow->finishEvent(*upSequenceNum);
+
+    // Now that all queues are cleared and no backlog in the connections, the key event
+    // can finally go to the newly focused "mUnfocusedWindow".
+    mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    mFocusedWindow->assertNoEvents();
+    mUnfocusedWindow->assertNoEvents();
+}
+
+// When the touch stream is split across 2 windows, and one of them does not respond,
+// then ANR should be raised and the touch should be canceled for the unresponsive window.
+// The other window should not be affected by that.
+TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
+    // Touch Window 1
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+    motionArgs =
+            generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    mUnfocusedWindow->consumeMotionDown();
+    mFocusedWindow->consumeMotionDown();
+    // Focused window may or may not receive ACTION_MOVE
+    // But it should definitely receive ACTION_CANCEL due to the ANR
+    InputEvent* event;
+    std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+    ASSERT_TRUE(moveOrCancelSequenceNum);
+    mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
+    ASSERT_NE(nullptr, event);
+    ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION);
+    MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+    if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) {
+        mFocusedWindow->consumeMotionCancel();
+    } else {
+        ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
+    }
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mUnfocusedWindow->assertNoEvents();
+    mFocusedWindow->assertNoEvents();
+}
+
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+    std::shared_ptr<FakeApplicationHandle> focusedApplication =
+            std::make_shared<FakeApplicationHandle>();
+    focusedApplication->setDispatchingTimeout(60ms);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+    // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+    mFocusedWindow->setFocusable(false);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+    mFocusedWindow->consumeFocusEvent(false);
+
+    // Send a key. The ANR timer should start because there is no focused window.
+    // 'focusedApplication' will get blamed if this timer completes.
+    // Key will not be sent anywhere because we have no focused window. It will remain pending.
+    InputEventInjectionResult result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+
+    // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+    // then the injected touches won't cause the focused event to get dropped.
+    // The dispatcher only checks for whether the queue should be pruned upon queueing.
+    // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+    // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+    // For this test, it means that the key would get delivered to the window once it becomes
+    // focused.
+    std::this_thread::sleep_for(10ms);
+
+    // Touch unfocused window. This should force the pending key to get dropped.
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // We do not consume the motion right away, because that would require dispatcher to first
+    // process (== drop) the key event, and by that time, ANR will be raised.
+    // Set the focused window first.
+    mFocusedWindow->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+    setFocusedWindow(mFocusedWindow);
+    mFocusedWindow->consumeFocusEvent(true);
+    // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+    // to another application. This could be a bug / behaviour in the policy.
+
+    mUnfocusedWindow->consumeMotionDown();
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // Should not ANR because we actually have a focused window. It was just added too slowly.
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
+// These tests ensure we cannot send touch events to a window that's positioned behind a window
+// that has feature NO_INPUT_CHANNEL.
+// Layout:
+//   Top (closest to user)
+//       mNoInputWindow (above all windows)
+//       mBottomWindow
+//   Bottom (furthest from user)
+class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApplication = std::make_shared<FakeApplicationHandle>();
+        mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+                                              "Window without input channel", ADISPLAY_ID_DEFAULT,
+                                              std::make_optional<sp<IBinder>>(nullptr) /*token*/);
+
+        mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+        mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+        // It's perfectly valid for this window to not have an associated input channel
+
+        mBottomWindow = new FakeWindowHandle(mApplication, mDispatcher, "Bottom window",
+                                             ADISPLAY_ID_DEFAULT);
+        mBottomWindow->setFrame(Rect(0, 0, 100, 100));
+
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+    }
+
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mNoInputWindow;
+    sp<FakeWindowHandle> mBottomWindow;
+};
+
+TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouches) {
+    PointF touchedPoint = {10, 10};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    mNoInputWindow->assertNoEvents();
+    // Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have
+    // an input channel, it is not marked as FLAG_NOT_TOUCHABLE,
+    // and therefore should prevent mBottomWindow from receiving touches
+    mBottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has feature NO_INPUT_CHANNEL, and somehow (by mistake) still has an input channel,
+ * ensure that this window does not receive any touches, and blocks touches to windows underneath.
+ */
+TEST_F(InputDispatcherMultiWindowOcclusionTests,
+       NoInputChannelFeature_DropsTouchesWithValidChannel) {
+    mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+                                          "Window with input channel and NO_INPUT_CHANNEL",
+                                          ADISPLAY_ID_DEFAULT);
+
+    mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+    mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+
+    PointF touchedPoint = {10, 10};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    mNoInputWindow->assertNoEvents();
+    mBottomWindow->assertNoEvents();
+}
+
+class InputDispatcherMirrorWindowFocusTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mMirror;
+
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mMirror = new FakeWindowHandle(mApp, mDispatcher, "TestWindowMirror", ADISPLAY_ID_DEFAULT,
+                                       mWindow->getToken());
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mWindow->setFocusable(true);
+        mMirror->setFocusable(true);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+    }
+};
+
+TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) {
+    // Request focus on a mirrored window
+    setFocusedWindow(mMirror);
+
+    // window gets focused
+    mWindow->consumeFocusEvent(true);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+// A focused & mirrored window remains focused only if the window and its mirror are both
+// focusable.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
+    setFocusedWindow(mMirror);
+
+    // window gets focused
+    mWindow->consumeFocusEvent(true);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    mMirror->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+    // window loses focus since one of the windows associated with the token in not focusable
+    mWindow->consumeFocusEvent(false);
+
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+    mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until the window and its mirror both become
+// invisible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) {
+    setFocusedWindow(mMirror);
+
+    // window gets focused
+    mWindow->consumeFocusEvent(true);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    mMirror->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    mWindow->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+    // window loses focus only after all windows associated with the token become invisible.
+    mWindow->consumeFocusEvent(false);
+
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+    mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until both windows are removed.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) {
+    setFocusedWindow(mMirror);
+
+    // window gets focused
+    mWindow->consumeFocusEvent(true);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    // single window is removed but the window token remains focused
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    // Both windows are removed
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+    mWindow->consumeFocusEvent(false);
+
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+    mWindow->assertNoEvents();
+}
+
+// Focus request can be pending until one window becomes visible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, DeferFocusWhenInvisible) {
+    // Request focus on an invisible mirror.
+    mWindow->setVisible(false);
+    mMirror->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+    setFocusedWindow(mMirror);
+
+    // Injected key goes to pending queue.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+    mMirror->setVisible(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+    // window gets focused
+    mWindow->consumeFocusEvent(true);
+    // window gets the pending key event
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
new file mode 100644
index 0000000..c368e79
--- /dev/null
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BnInputFlingerQuery.h>
+#include <IInputFlingerQuery.h>
+
+#include <android/os/BnInputFlinger.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+
+#include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <chrono>
+#include <thread>
+#include <unordered_map>
+
+#define TAG "InputFlingerServiceTest"
+
+using android::os::BnInputFlinger;
+using android::os::BnSetInputWindowsListener;
+using android::os::IInputFlinger;
+using android::os::ISetInputWindowsListener;
+
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+
+namespace android {
+
+static const sp<IBinder> TestInfoToken = new BBinder();
+static const sp<IBinder> FocusedTestInfoToken = new BBinder();
+static constexpr int32_t TestInfoId = 1;
+static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
+static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE;
+static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD;
+static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
+static constexpr int32_t TestInfoFrameLeft = 93;
+static constexpr int32_t TestInfoFrameTop = 34;
+static constexpr int32_t TestInfoFrameRight = 16;
+static constexpr int32_t TestInfoFrameBottom = 19;
+static constexpr int32_t TestInfoSurfaceInset = 17;
+static constexpr float TestInfoGlobalScaleFactor = 0.3;
+static constexpr float TestInfoWindowXScale = 0.4;
+static constexpr float TestInfoWindowYScale = 0.5;
+static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
+                                                 450 /* bottom */};
+static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
+static constexpr bool TestInfoVisible = false;
+static constexpr bool TestInfoTrustedOverlay = true;
+static constexpr bool TestInfoFocusable = false;
+static constexpr bool TestInfoHasWallpaper = false;
+static constexpr bool TestInfoPaused = false;
+static constexpr int32_t TestInfoOwnerPid = 19;
+static constexpr int32_t TestInfoOwnerUid = 24;
+static constexpr InputWindowInfo::Feature TestInfoInputFeatures =
+        InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+static constexpr int32_t TestInfoDisplayId = 34;
+static constexpr int32_t TestInfoPortalToDisplayId = 2;
+static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
+static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
+
+static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
+static const sp<IBinder> TestAppInfoToken = new BBinder();
+static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
+
+static const String16 kTestServiceName = String16("InputFlingerService");
+static const String16 kQueryServiceName = String16("InputFlingerQueryService");
+
+struct SetInputWindowsListener;
+// --- InputFlingerServiceTest ---
+class InputFlingerServiceTest : public testing::Test {
+public:
+    void SetUp() override;
+    void TearDown() override;
+
+protected:
+    void InitializeInputFlinger();
+    void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
+    void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
+                          nsecs_t timestampNanos);
+
+    void setInputWindowsFinished();
+    void verifyInputWindowInfo(const InputWindowInfo& info) const;
+    InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); }
+
+    sp<IInputFlinger> mService;
+    sp<IInputFlingerQuery> mQuery;
+
+private:
+    sp<SetInputWindowsListener> mSetInputWindowsListener;
+    std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
+    InputWindowInfo mInfo;
+    std::mutex mLock;
+    std::condition_variable mSetInputWindowsFinishedCondition;
+};
+
+struct SetInputWindowsListener : BnSetInputWindowsListener {
+    explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
+
+    binder::Status onSetInputWindowsFinished() override;
+
+    std::function<void()> mCbFunc;
+};
+
+class TestInputManager : public BnInputFlinger {
+protected:
+    virtual ~TestInputManager(){};
+
+public:
+    TestInputManager(){};
+
+    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
+    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
+    binder::Status getLastFocusRequest(FocusRequest*);
+
+    status_t dump(int fd, const Vector<String16>& args) override;
+
+    binder::Status setInputWindows(
+            const std::vector<InputWindowInfo>& handles,
+            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
+
+    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status setFocusedWindow(const FocusRequest&) override;
+
+    void reset();
+
+private:
+    mutable Mutex mLock;
+    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
+    std::vector<std::shared_ptr<InputChannel>> mInputChannels;
+    FocusRequest mFocusRequest;
+};
+
+class TestInputQuery : public BnInputFlingerQuery {
+public:
+    TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
+    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
+    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
+    binder::Status getLastFocusRequest(FocusRequest*) override;
+    binder::Status resetInputManager() override;
+
+private:
+    sp<android::TestInputManager> mManager;
+};
+
+binder::Status TestInputQuery::getInputWindows(
+        std::vector<::android::InputWindowInfo>* inputHandles) {
+    return mManager->getInputWindows(inputHandles);
+}
+
+binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
+    return mManager->getInputChannels(channels);
+}
+
+binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
+    return mManager->getLastFocusRequest(request);
+}
+
+binder::Status TestInputQuery::resetInputManager() {
+    mManager->reset();
+    return binder::Status::ok();
+}
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+    if (mCbFunc != nullptr) {
+        mCbFunc();
+    }
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setInputWindows(
+        const std::vector<InputWindowInfo>& infos,
+        const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+    AutoMutex _l(mLock);
+
+    for (const auto& info : infos) {
+        mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+        mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info));
+    }
+    if (setInputWindowsListener) {
+        setInputWindowsListener->onSetInputWindowsFinished();
+    }
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::createInputChannel(const std::string& name,
+                                                    InputChannel* outChannel) {
+    AutoMutex _l(mLock);
+    std::unique_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+
+    clientChannel->copyTo(*outChannel);
+
+    mInputChannels.emplace_back(std::move(serverChannel));
+
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+    AutoMutex _l(mLock);
+
+    auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
+                           [&](std::shared_ptr<InputChannel>& c) {
+                               return c->getConnectionToken() == connectionToken;
+                           });
+    if (it != mInputChannels.end()) {
+        mInputChannels.erase(it);
+    }
+
+    return binder::Status::ok();
+}
+
+status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
+    std::string dump;
+
+    dump += " InputFlinger dump\n";
+
+    ::write(fd, dump.c_str(), dump.size());
+    return NO_ERROR;
+}
+
+binder::Status TestInputManager::getInputWindows(
+        std::vector<::android::InputWindowInfo>* inputInfos) {
+    for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
+        for (auto& inputHandle : inputHandles) {
+            inputInfos->push_back(*inputHandle->getInfo());
+        }
+    }
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
+    channels->clear();
+    for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
+        channels->push_back(*channel);
+    }
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
+    *request = mFocusRequest;
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
+    mFocusRequest = request;
+    return binder::Status::ok();
+}
+
+void TestInputManager::reset() {
+    mHandlesPerDisplay.clear();
+    mInputChannels.clear();
+    mFocusRequest = FocusRequest();
+}
+
+void InputFlingerServiceTest::SetUp() {
+    mSetInputWindowsListener = new SetInputWindowsListener([&]() {
+        std::unique_lock<std::mutex> lock(mLock);
+        mSetInputWindowsFinishedCondition.notify_all();
+    });
+    InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+
+    mInfo.token = TestInfoToken;
+    mInfo.id = TestInfoId;
+    mInfo.name = TestInfoName;
+    mInfo.flags = TestInfoFlags;
+    mInfo.type = TestInfoType;
+    mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
+    mInfo.frameLeft = TestInfoFrameLeft;
+    mInfo.frameTop = TestInfoFrameTop;
+    mInfo.frameRight = TestInfoFrameRight;
+    mInfo.frameBottom = TestInfoFrameBottom;
+    mInfo.surfaceInset = TestInfoSurfaceInset;
+    mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
+    mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
+                         TestInfoFrameTop, 0, 0, 1});
+    mInfo.touchableRegion = TestInfoTouchableRegion;
+    mInfo.visible = TestInfoVisible;
+    mInfo.trustedOverlay = TestInfoTrustedOverlay;
+    mInfo.focusable = TestInfoFocusable;
+
+    mInfo.hasWallpaper = TestInfoHasWallpaper;
+    mInfo.paused = TestInfoPaused;
+    mInfo.ownerPid = TestInfoOwnerPid;
+    mInfo.ownerUid = TestInfoOwnerUid;
+    mInfo.inputFeatures = TestInfoInputFeatures;
+    mInfo.displayId = TestInfoDisplayId;
+    mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
+    mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
+    mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
+
+    mInfo.applicationInfo.name = TestAppInfoName;
+    mInfo.applicationInfo.token = TestAppInfoToken;
+    mInfo.applicationInfo.dispatchingTimeoutMillis =
+            std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout)
+                    .count();
+
+    InitializeInputFlinger();
+}
+
+void InputFlingerServiceTest::TearDown() {
+    mQuery->resetInputManager();
+}
+
+void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
+    EXPECT_EQ(mInfo, info);
+}
+
+void InputFlingerServiceTest::InitializeInputFlinger() {
+    sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
+    ASSERT_TRUE(input != nullptr);
+    mService = interface_cast<IInputFlinger>(input);
+
+    input = defaultServiceManager()->waitForService(kQueryServiceName);
+    ASSERT_TRUE(input != nullptr);
+    mQuery = interface_cast<IInputFlingerQuery>(input);
+}
+
+void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
+    std::unique_lock<std::mutex> lock(mLock);
+    mService->setInputWindows(infos, mSetInputWindowsListener);
+    // Verify listener call
+    EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
+                                               const sp<IBinder> focusedToken,
+                                               nsecs_t timestampNanos) {
+    FocusRequest request;
+    request.token = TestInfoToken;
+    request.focusedToken = focusedToken;
+    request.timestamp = timestampNanos;
+    mService->setFocusedWindow(request);
+    // call set input windows and wait for the callback to drain the queue.
+    setInputWindowsByInfos(std::vector<InputWindowInfo>());
+}
+
+/**
+ *  Test InputFlinger service interface SetInputWindows
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
+    std::vector<InputWindowInfo> infos = {getInfo()};
+    setInputWindowsByInfos(infos);
+
+    // Verify input windows from service
+    std::vector<::android::InputWindowInfo> windowInfos;
+    mQuery->getInputWindows(&windowInfos);
+    for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
+        verifyInputWindowInfo(windowInfo);
+    }
+}
+
+/**
+ *  Test InputFlinger service interface createInputChannel
+ */
+TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
+    // Test that the unblocked file descriptor flag is kept across processes over binder
+    // transactions.
+
+    InputChannel channel;
+    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+    const base::unique_fd& fd = channel.getFd();
+    ASSERT_TRUE(fd.ok());
+
+    const int result = fcntl(fd, F_GETFL);
+    EXPECT_NE(result, -1);
+    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+    InputChannel channel;
+    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+    std::vector<::android::InputChannel> channels;
+    mQuery->getInputChannels(&channels);
+    ASSERT_EQ(channels.size(), 1UL);
+    EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
+
+    mService->removeInputChannel(channel.getConnectionToken());
+    mQuery->getInputChannels(&channels);
+    EXPECT_EQ(channels.size(), 0UL);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
+
+    FocusRequest request;
+    mQuery->getLastFocusRequest(&request);
+
+    EXPECT_EQ(request.token, TestInfoToken);
+    EXPECT_EQ(request.focusedToken, nullptr);
+    EXPECT_EQ(request.timestamp, now);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
+
+    FocusRequest request;
+    mQuery->getLastFocusRequest(&request);
+
+    EXPECT_EQ(request.token, TestInfoToken);
+    EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
+    EXPECT_EQ(request.timestamp, now);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+    pid_t forkPid = fork();
+
+    if (forkPid == 0) {
+        // Server process
+        android::sp<android::TestInputManager> manager = new android::TestInputManager();
+        android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager);
+
+        android::defaultServiceManager()->addService(android::kTestServiceName, manager,
+                                                     false /*allowIsolated*/);
+        android::defaultServiceManager()->addService(android::kQueryServiceName, query,
+                                                     false /*allowIsolated*/);
+        android::ProcessState::self()->startThreadPool();
+        android::IPCThreadState::self()->joinThreadPool();
+    } else {
+        android::ProcessState::self()->startThreadPool();
+        ::testing::InitGoogleTest(&argc, argv);
+        int result = RUN_ALL_TESTS();
+        kill(forkPid, SIGKILL);
+        return result;
+    }
+    return 0;
+}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c457a15..a72d5c3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -27,15 +27,19 @@
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
 #include <UinputDevice.h>
-
 #include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
 #include <math.h>
 
+#include <memory>
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
+
 namespace android {
 
 using std::chrono_literals::operator""ms;
+using namespace android::flag_operators;
 
 // Timeout for waiting for an expected event
 static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
@@ -44,16 +48,24 @@
 static const nsecs_t ARBITRARY_TIME = 1234;
 
 // Arbitrary display properties.
-static const int32_t DISPLAY_ID = 0;
-static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
-static const int32_t DISPLAY_WIDTH = 480;
-static const int32_t DISPLAY_HEIGHT = 800;
-static const int32_t VIRTUAL_DISPLAY_ID = 1;
-static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
-static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
+static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
 static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
 static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
 
+static constexpr int32_t FIRST_SLOT = 0;
+static constexpr int32_t SECOND_SLOT = 1;
+static constexpr int32_t THIRD_SLOT = 2;
+static constexpr int32_t INVALID_TRACKING_ID = -1;
+static constexpr int32_t FIRST_TRACKING_ID = 0;
+static constexpr int32_t SECOND_TRACKING_ID = 1;
+static constexpr int32_t THIRD_TRACKING_ID = 2;
+
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
 
@@ -76,15 +88,14 @@
     int32_t mButtonState;
     int32_t mDisplayId;
 
-protected:
-    virtual ~FakePointerController() { }
-
 public:
     FakePointerController() :
         mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
         mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
     }
 
+    virtual ~FakePointerController() {}
+
     void setBounds(float minX, float minY, float maxX, float maxY) {
         mHaveBounds = true;
         mMinX = minX;
@@ -93,29 +104,23 @@
         mMaxY = maxY;
     }
 
-    virtual void setPosition(float x, float y) {
+    void setPosition(float x, float y) override {
         mX = x;
         mY = y;
     }
 
-    virtual void setButtonState(int32_t buttonState) {
-        mButtonState = buttonState;
-    }
+    void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
 
-    virtual int32_t getButtonState() const {
-        return mButtonState;
-    }
+    int32_t getButtonState() const override { return mButtonState; }
 
-    virtual void getPosition(float* outX, float* outY) const {
+    void getPosition(float* outX, float* outY) const override {
         *outX = mX;
         *outY = mY;
     }
 
-    virtual int32_t getDisplayId() const {
-        return mDisplayId;
-    }
+    int32_t getDisplayId() const override { return mDisplayId; }
 
-    virtual void setDisplayViewport(const DisplayViewport& viewport) {
+    void setDisplayViewport(const DisplayViewport& viewport) override {
         mDisplayId = viewport.displayId;
     }
 
@@ -124,7 +129,7 @@
     }
 
 private:
-    virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
         *outMinX = mMinX;
         *outMinY = mMinY;
         *outMaxX = mMaxX;
@@ -132,7 +137,7 @@
         return mHaveBounds;
     }
 
-    virtual void move(float deltaX, float deltaY) {
+    void move(float deltaX, float deltaY) override {
         mX += deltaX;
         if (mX < mMinX) mX = mMinX;
         if (mX > mMaxX) mX = mMaxX;
@@ -141,17 +146,14 @@
         if (mY > mMaxY) mY = mMaxY;
     }
 
-    virtual void fade(Transition) {
-    }
+    void fade(Transition) override {}
 
-    virtual void unfade(Transition) {
-    }
+    void unfade(Transition) override {}
 
-    virtual void setPresentation(Presentation) {
-    }
+    void setPresentation(Presentation) override {}
 
-    virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
-            int32_t displayId) {
+    void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+                  int32_t displayId) override {
         std::vector<int32_t> newSpots;
         // Add spots for fingers that are down.
         for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
@@ -162,8 +164,7 @@
         mSpotsByDisplay[displayId] = newSpots;
     }
 
-    virtual void clearSpots() {
-    }
+    void clearSpots() override {}
 
     std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
 };
@@ -176,14 +177,14 @@
     std::condition_variable mDevicesChangedCondition;
 
     InputReaderConfiguration mConfig;
-    KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
+    std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers;
     std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
     bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
 
 protected:
-    virtual ~FakeInputReaderPolicy() { }
+    virtual ~FakeInputReaderPolicy() {}
 
 public:
     FakeInputReaderPolicy() {
@@ -256,8 +257,8 @@
 
     void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
 
-    void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
-        mPointerControllers.add(deviceId, controller);
+    void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) {
+        mPointerControllers.insert_or_assign(deviceId, std::move(controller));
     }
 
     const InputReaderConfiguration* getReaderConfiguration() const {
@@ -289,6 +290,8 @@
         mConfig.defaultPointerDisplayId = pointerDisplayId;
     }
 
+    float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
+
 private:
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
             int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
@@ -314,28 +317,27 @@
         return v;
     }
 
-    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
-    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
-        return mPointerControllers.valueFor(deviceId);
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
+        return mPointerControllers[deviceId];
     }
 
-    virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mInputDevices = inputDevices;
         mInputDevicesChanged = true;
         mDevicesChangedCondition.notify_all();
     }
 
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier&) override {
         return nullptr;
     }
 
-    virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
-        return "";
-    }
+    std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
 
     void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
         std::unique_lock<std::mutex> lock(mLock);
@@ -360,7 +362,7 @@
 
     struct Device {
         InputDeviceIdentifier identifier;
-        uint32_t classes;
+        Flags<InputDeviceClass> classes;
         PropertyMap configuration;
         KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
         KeyedVector<int, bool> relativeAxes;
@@ -384,9 +386,7 @@
             return OK;
         }
 
-        explicit Device(uint32_t classes) :
-                classes(classes), enabled(true) {
-        }
+        explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
     };
 
     std::mutex mLock;
@@ -406,7 +406,7 @@
 
     FakeEventHub() { }
 
-    void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
+    void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) {
         Device* device = new Device(classes);
         device->identifier.name = name;
         mDevices.add(deviceId, device);
@@ -582,29 +582,27 @@
         return index >= 0 ? mDevices.valueAt(index) : nullptr;
     }
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const {
+    Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
         Device* device = getDevice(deviceId);
-        return device ? device->classes : 0;
+        return device ? device->classes : Flags<InputDeviceClass>(0);
     }
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
         Device* device = getDevice(deviceId);
         return device ? device->identifier : InputDeviceIdentifier();
     }
 
-    virtual int32_t getDeviceControllerNumber(int32_t) const {
-        return 0;
-    }
+    int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             *outConfiguration = device->configuration;
         }
     }
 
-    virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-            RawAbsoluteAxisInfo* outAxisInfo) const {
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
         Device* device = getDevice(deviceId);
         if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
@@ -617,7 +615,7 @@
         return -1;
     }
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             return device->relativeAxes.indexOfKey(axis) >= 0;
@@ -625,13 +623,10 @@
         return false;
     }
 
-    virtual bool hasInputProperty(int32_t, int) const {
-        return false;
-    }
+    bool hasInputProperty(int32_t, int) const override { return false; }
 
-    virtual status_t mapKey(int32_t deviceId,
-            int32_t scanCode, int32_t usageCode, int32_t metaState,
-            int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const {
+    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                    int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             const KeyInfo* key = getKey(device, scanCode, usageCode);
@@ -667,15 +662,13 @@
         return nullptr;
     }
 
-    virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
-        return NAME_NOT_FOUND;
-    }
+    status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices) {
+    void setExcludedDevices(const std::vector<std::string>& devices) override {
         mExcludedDevices = devices;
     }
 
-    virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+    size_t getEvents(int, RawEvent* buffer, size_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         if (mEvents.empty()) {
             return 0;
@@ -687,7 +680,7 @@
         return 1;
     }
 
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
         auto it = mVideoFrames.find(deviceId);
         if (it != mVideoFrames.end()) {
             std::vector<TouchVideoFrame> frames = std::move(it->second);
@@ -697,7 +690,7 @@
         return {};
     }
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
@@ -708,7 +701,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
@@ -719,7 +712,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
+    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->switchStates.indexOfKey(sw);
@@ -730,8 +723,8 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
-            int32_t* outValue) const {
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                  int32_t* outValue) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
@@ -744,22 +737,22 @@
         return -1;
     }
 
-    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-            uint8_t* outFlags) const {
+    // 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 {
         bool result = false;
         Device* device = getDevice(deviceId);
         if (device) {
+            result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
             for (size_t i = 0; i < numCodes; i++) {
                 for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
                     if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
                         outFlags[i] = 1;
-                        result = true;
                     }
                 }
                 for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
                     if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
                         outFlags[i] = 1;
-                        result = true;
                     }
                 }
             }
@@ -767,7 +760,7 @@
         return result;
     }
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const {
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
@@ -776,12 +769,12 @@
         return false;
     }
 
-    virtual bool hasLed(int32_t deviceId, int32_t led) const {
+    bool hasLed(int32_t deviceId, int32_t led) const override {
         Device* device = getDevice(deviceId);
         return device && device->leds.indexOfKey(led) >= 0;
     }
 
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
+    void setLedState(int32_t deviceId, int32_t led, bool on) override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->leds.indexOfKey(led);
@@ -795,8 +788,8 @@
         }
     }
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-            std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+    void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
         outVirtualKeys.clear();
 
         Device* device = getDevice(deviceId);
@@ -805,146 +798,31 @@
         }
     }
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
         return nullptr;
     }
 
-    virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
+    bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
         return false;
     }
 
-    virtual void vibrate(int32_t, nsecs_t) {
-    }
+    void vibrate(int32_t, const VibrationElement&) override {}
 
-    virtual void cancelVibrate(int32_t) {
-    }
+    void cancelVibrate(int32_t) override {}
 
     virtual bool isExternal(int32_t) const {
         return false;
     }
 
-    virtual void dump(std::string&) {
-    }
+    void dump(std::string&) override {}
 
-    virtual void monitor() {
-    }
+    void monitor() override {}
 
-    virtual void requestReopenDevices() {
-    }
+    void requestReopenDevices() override {}
 
-    virtual void wake() {
-    }
+    void wake() override {}
 };
 
-
-// --- FakeInputReaderContext ---
-
-class FakeInputReaderContext : public InputReaderContext {
-    std::shared_ptr<EventHubInterface> mEventHub;
-    sp<InputReaderPolicyInterface> mPolicy;
-    sp<InputListenerInterface> mListener;
-    int32_t mGlobalMetaState;
-    bool mUpdateGlobalMetaStateWasCalled;
-    int32_t mGeneration;
-    int32_t mNextId;
-    wp<PointerControllerInterface> mPointerController;
-
-public:
-    FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
-                           const sp<InputReaderPolicyInterface>& policy,
-                           const sp<InputListenerInterface>& listener)
-          : mEventHub(eventHub),
-            mPolicy(policy),
-            mListener(listener),
-            mGlobalMetaState(0),
-            mNextId(1) {}
-
-    virtual ~FakeInputReaderContext() { }
-
-    void assertUpdateGlobalMetaStateWasCalled() {
-        ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
-                << "Expected updateGlobalMetaState() to have been called.";
-        mUpdateGlobalMetaStateWasCalled = false;
-    }
-
-    void setGlobalMetaState(int32_t state) {
-        mGlobalMetaState = state;
-    }
-
-    uint32_t getGeneration() {
-        return mGeneration;
-    }
-
-    void updatePointerDisplay() {
-        sp<PointerControllerInterface> controller = mPointerController.promote();
-        if (controller != nullptr) {
-            InputReaderConfiguration config;
-            mPolicy->getReaderConfiguration(&config);
-            auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
-            if (viewport) {
-                controller->setDisplayViewport(*viewport);
-            }
-        }
-    }
-
-private:
-    virtual void updateGlobalMetaState() {
-        mUpdateGlobalMetaStateWasCalled = true;
-    }
-
-    virtual int32_t getGlobalMetaState() {
-        return mGlobalMetaState;
-    }
-
-    virtual EventHubInterface* getEventHub() {
-        return mEventHub.get();
-    }
-
-    virtual InputReaderPolicyInterface* getPolicy() {
-        return mPolicy.get();
-    }
-
-    virtual InputListenerInterface* getListener() {
-        return mListener.get();
-    }
-
-    virtual void disableVirtualKeysUntil(nsecs_t) {
-    }
-
-    virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
-
-    virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) {
-        sp<PointerControllerInterface> controller = mPointerController.promote();
-        if (controller == nullptr) {
-            controller = mPolicy->obtainPointerController(deviceId);
-            mPointerController = controller;
-            updatePointerDisplay();
-        }
-        return controller;
-    }
-
-    virtual void fadePointer() {
-    }
-
-    virtual void requestTimeoutAtTime(nsecs_t) {
-    }
-
-    virtual int32_t bumpGeneration() {
-        return ++mGeneration;
-    }
-
-    virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
-
-    }
-
-    virtual void dispatchExternalStylusState(const StylusState&) {
-
-    }
-
-    virtual int32_t getNextId() { return mNextId++; }
-};
-
-
 // --- FakeInputMapper ---
 
 class FakeInputMapper : public InputMapper {
@@ -974,7 +852,7 @@
             mResetWasCalled(false),
             mProcessWasCalled(false) {}
 
-    virtual ~FakeInputMapper() { }
+    virtual ~FakeInputMapper() {}
 
     void setKeyboardType(int32_t keyboardType) {
         mKeyboardType = keyboardType;
@@ -1043,11 +921,9 @@
     }
 
 private:
-    virtual uint32_t getSources() {
-        return mSources;
-    }
+    uint32_t getSources() override { return mSources; }
 
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
         InputMapper::populateDeviceInfo(deviceInfo);
 
         if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
@@ -1055,7 +931,7 @@
         }
     }
 
-    virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+    void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
@@ -1068,45 +944,45 @@
         mStateChangedCondition.notify_all();
     }
 
-    virtual void reset(nsecs_t) {
+    void reset(nsecs_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
         mStateChangedCondition.notify_all();
     }
 
-    virtual void process(const RawEvent* rawEvent) {
+    void process(const RawEvent* rawEvent) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
     }
 
-    virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
+    int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
         ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
         return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getScanCodeState(uint32_t, int32_t scanCode) {
+    int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
         ssize_t index = mScanCodeStates.indexOfKey(scanCode);
         return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getSwitchState(uint32_t, int32_t switchCode) {
+    int32_t getSwitchState(uint32_t, int32_t switchCode) override {
         ssize_t index = mSwitchStates.indexOfKey(switchCode);
         return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual bool markSupportedKeyCodes(uint32_t, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags) {
-        bool result = false;
+    // Return true if the device has non-empty key layout.
+    bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags) override {
         for (size_t i = 0; i < numCodes; i++) {
             for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
                 if (keyCodes[i] == mSupportedKeyCodes[j]) {
                     outFlags[i] = 1;
-                    result = true;
                 }
             }
         }
+        bool result = mSupportedKeyCodes.size() > 0;
         return result;
     }
 
@@ -1129,17 +1005,17 @@
 // --- InstrumentedInputReader ---
 
 class InstrumentedInputReader : public InputReader {
-    std::shared_ptr<InputDevice> mNextDevice;
+    std::queue<std::shared_ptr<InputDevice>> mNextDevices;
 
 public:
     InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
                             const sp<InputReaderPolicyInterface>& policy,
                             const sp<InputListenerInterface>& listener)
-          : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
+          : InputReader(eventHub, policy, listener), mFakeContext(this) {}
 
     virtual ~InstrumentedInputReader() {}
 
-    void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
+    void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
                                            const std::string& location = "") {
@@ -1147,7 +1023,7 @@
         identifier.name = name;
         identifier.location = location;
         int32_t generation = deviceId + 1;
-        return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
+        return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
     }
 
     // Make the protected loopOnce method accessible to tests.
@@ -1156,15 +1032,58 @@
 protected:
     virtual std::shared_ptr<InputDevice> createDeviceLocked(
             int32_t eventHubId, const InputDeviceIdentifier& identifier) {
-        if (mNextDevice) {
-            std::shared_ptr<InputDevice> device(mNextDevice);
-            mNextDevice = nullptr;
+        if (!mNextDevices.empty()) {
+            std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+            mNextDevices.pop();
             return device;
         }
         return InputReader::createDeviceLocked(eventHubId, identifier);
     }
 
+    // --- FakeInputReaderContext ---
+    class FakeInputReaderContext : public ContextImpl {
+        int32_t mGlobalMetaState;
+        bool mUpdateGlobalMetaStateWasCalled;
+        int32_t mGeneration;
+
+    public:
+        FakeInputReaderContext(InputReader* reader)
+              : ContextImpl(reader),
+                mGlobalMetaState(0),
+                mUpdateGlobalMetaStateWasCalled(false),
+                mGeneration(1) {}
+
+        virtual ~FakeInputReaderContext() {}
+
+        void assertUpdateGlobalMetaStateWasCalled() {
+            ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+                    << "Expected updateGlobalMetaState() to have been called.";
+            mUpdateGlobalMetaStateWasCalled = false;
+        }
+
+        void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+        uint32_t getGeneration() { return mGeneration; }
+
+        void updateGlobalMetaState() override {
+            mUpdateGlobalMetaStateWasCalled = true;
+            ContextImpl::updateGlobalMetaState();
+        }
+
+        int32_t getGlobalMetaState() override {
+            return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+        }
+
+        int32_t bumpGeneration() override {
+            mGeneration = ContextImpl::bumpGeneration();
+            return mGeneration;
+        }
+    } mFakeContext;
+
     friend class InputReaderTest;
+
+public:
+    FakeInputReaderContext* getContext() { return &mFakeContext; }
 };
 
 // --- InputReaderPolicyTest ---
@@ -1172,8 +1091,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
-    virtual void TearDown() override { mFakePolicy.clear(); }
+    void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1188,20 +1107,21 @@
 
     // We didn't add any viewports yet, so there shouldn't be any.
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+            mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     ASSERT_FALSE(internalViewport);
 
     // Add an internal viewport, then clear it
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+                                    DISPLAY_ORIENTATION_0, uniqueId, NO_PORT,
+                                    ViewportType::INTERNAL);
 
     // Check matching by uniqueId
     internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_TRUE(internalViewport);
-    ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+    ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type);
 
     // Check matching by viewport type
-    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     ASSERT_TRUE(internalViewport);
     ASSERT_EQ(uniqueId, internalViewport->uniqueId);
 
@@ -1209,7 +1129,7 @@
     // Make sure nothing is found after clear
     internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_FALSE(internalViewport);
-    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     ASSERT_FALSE(internalViewport);
 }
 
@@ -1223,26 +1143,30 @@
 
     // Add an internal viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+                                    DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT,
+                                    ViewportType::INTERNAL);
     // Add an external viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+                                    DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT,
+                                    ViewportType::EXTERNAL);
     // Add an virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+                                    DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT,
+                                    ViewportType::VIRTUAL);
     // Add another virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+                                    DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT,
+                                    ViewportType::VIRTUAL);
 
     // Check matching by type for internal
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+            mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
     ASSERT_TRUE(internalViewport);
     ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
 
     // Check matching by type for external
     std::optional<DisplayViewport> externalViewport =
-            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+            mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL);
     ASSERT_TRUE(externalViewport);
     ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
 
@@ -1250,7 +1174,7 @@
     std::optional<DisplayViewport> virtualViewport1 =
             mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
     ASSERT_TRUE(virtualViewport1);
-    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+    ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type);
     ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
     ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
 
@@ -1258,7 +1182,7 @@
     std::optional<DisplayViewport> virtualViewport2 =
             mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
     ASSERT_TRUE(virtualViewport2);
-    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+    ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type);
     ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
     ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
 }
@@ -1275,8 +1199,8 @@
     constexpr int32_t displayId1 = 2;
     constexpr int32_t displayId2 = 3;
 
-    std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
-            ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+    std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL,
+                                       ViewportType::VIRTUAL};
     for (const ViewportType& type : types) {
         mFakePolicy->clearViewports();
         // Add a viewport
@@ -1311,10 +1235,51 @@
 }
 
 /**
+ * When we have multiple internal displays make sure we always return the default display when
+ * querying by type.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) {
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t nonDefaultDisplayId = 2;
+    static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT,
+                  "Test display ID should not be ADISPLAY_ID_DEFAULT");
+
+    // Add the default display first and ensure it gets returned.
+    mFakePolicy->clearViewports();
+    mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+                                    ViewportType::INTERNAL);
+
+    std::optional<DisplayViewport> viewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+    ASSERT_TRUE(viewport);
+    ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+    ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+
+    // Add the default display second to make sure order doesn't matter.
+    mFakePolicy->clearViewports();
+    mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+                                    ViewportType::INTERNAL);
+
+    viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+    ASSERT_TRUE(viewport);
+    ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+    ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+}
+
+/**
  * Check getDisplayViewportByPort
  */
 TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
-    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    constexpr ViewportType type = ViewportType::EXTERNAL;
     const std::string uniqueId1 = "uniqueId1";
     const std::string uniqueId2 = "uniqueId2";
     constexpr int32_t displayId1 = 1;
@@ -1358,7 +1323,7 @@
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1367,12 +1332,12 @@
                                                             mFakeListener);
     }
 
-    virtual void TearDown() override {
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
 
-    void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes,
+    void addDevice(int32_t eventHubId, const std::string& name, Flags<InputDeviceClass> classes,
                    const PropertyMap* configuration) {
         mFakeEventHub->addDevice(eventHubId, name, classes);
 
@@ -1397,34 +1362,23 @@
     }
 
     FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
-                                                  const std::string& name, uint32_t classes,
-                                                  uint32_t sources,
+                                                  const std::string& name,
+                                                  Flags<InputDeviceClass> classes, uint32_t sources,
                                                   const PropertyMap* configuration) {
         std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
         FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
-        mReader->setNextDevice(device);
+        mReader->pushNextDevice(device);
         addDevice(eventHubId, name, classes, configuration);
         return mapper;
     }
 };
 
-TEST_F(InputReaderTest, GetInputDevices) {
-    ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard",
-            INPUT_DEVICE_CLASS_KEYBOARD, nullptr));
-    ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
-            0, nullptr)); // no classes so device will be ignored
+TEST_F(InputReaderTest, ReaderGetInputDevices) {
+    ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+                                      nullptr)); // no classes so device will be ignored
 
-    std::vector<InputDeviceInfo> inputDevices;
-    mReader->getInputDevices(inputDevices);
-    ASSERT_EQ(1U, inputDevices.size());
-    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
-    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
-    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
-    ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-
-    // Should also have received a notification describing the new input devices.
-    inputDevices = mFakePolicy->getInputDevices();
+    const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1433,14 +1387,50 @@
     ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
 }
 
+TEST_F(InputReaderTest, PolicyGetInputDevices) {
+    ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+                                      nullptr)); // no classes so device will be ignored
+
+    // Should also have received a notification describing the new input devices.
+    const std::vector<InputDeviceInfo>& inputDevices = mFakePolicy->getInputDevices();
+    ASSERT_EQ(1U, inputDevices.size());
+    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
+    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
+    ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
+}
+
+TEST_F(InputReaderTest, GetMergedInputDevices) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+    // Add two subdevices to device
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    // Must add at least one mapper or the device will be ignored!
+    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+    // Push same device instance for next device to be added, so they'll have same identifier.
+    mReader->pushNextDevice(device);
+    mReader->pushNextDevice(device);
+    ASSERT_NO_FATAL_FAILURE(
+            addDevice(eventHubIds[0], "fake1", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(
+            addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+
+    // Two devices will be merged to one input device as they have same identifier
+    ASSERT_EQ(1U, mReader->getInputDevices().size());
+}
+
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
     device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->setNextDevice(device);
+    mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
@@ -1472,7 +1462,7 @@
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1505,7 +1495,7 @@
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1538,7 +1528,7 @@
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1571,7 +1561,7 @@
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1613,7 +1603,7 @@
 
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
     constexpr int32_t eventHubId = 1;
-    addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
+    addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
 
@@ -1623,7 +1613,7 @@
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1644,12 +1634,12 @@
 
 TEST_F(InputReaderTest, DeviceReset_RandomId) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
     device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->setNextDevice(device);
+    mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     NotifyDeviceResetArgs resetArgs;
@@ -1677,12 +1667,12 @@
 
 TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) {
     constexpr int32_t deviceId = 1;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
     device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->setNextDevice(device);
+    mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
     NotifyDeviceResetArgs resetArgs;
@@ -1692,13 +1682,13 @@
 
 TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "USB1";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
     FakeInputMapper& mapper =
             device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
-    mReader->setNextDevice(device);
+    mReader->pushNextDevice(device);
 
     const uint8_t hdmi1 = 1;
 
@@ -1708,9 +1698,11 @@
     // Add default and second display.
     mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+                                    DISPLAY_ORIENTATION_0, "local:0", NO_PORT,
+                                    ViewportType::INTERNAL);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+                                    DISPLAY_ORIENTATION_0, "local:1", hdmi1,
+                                    ViewportType::EXTERNAL);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
 
@@ -1733,6 +1725,73 @@
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
+TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+    constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    // Must add at least one mapper or the device will be ignored!
+    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+    mReader->pushNextDevice(device);
+    mReader->pushNextDevice(device);
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(deviceId, resetArgs.deviceId);
+    ASSERT_TRUE(device->isEnabled());
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+    disableDevice(deviceId);
+    mReader->loopOnce();
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(deviceId, resetArgs.deviceId);
+    ASSERT_FALSE(device->isEnabled());
+    ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+    ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+    enableDevice(deviceId);
+    mReader->loopOnce();
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(deviceId, resetArgs.deviceId);
+    ASSERT_TRUE(device->isEnabled());
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+}
+
+TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+    constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+    // Add two subdevices to device
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    FakeInputMapper& mapperDevice1 =
+            device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+    FakeInputMapper& mapperDevice2 =
+            device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+    mReader->pushNextDevice(device);
+    mReader->pushNextDevice(device);
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+    mapperDevice1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    mapperDevice2.setKeyCodeState(AKEYCODE_B, AKEY_STATE_DOWN);
+
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_A));
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_B));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C));
+}
+
 // --- InputReaderIntegrationTest ---
 
 // These tests create and interact with the InputReader only through its interface.
@@ -1746,7 +1805,7 @@
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<InputReaderInterface> mReader;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakePolicy = new FakeInputReaderPolicy();
         mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
                                               30ms /*eventDidNotHappenTimeout*/);
@@ -1761,7 +1820,7 @@
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
     }
 
-    virtual void TearDown() override {
+    void TearDown() override {
         ASSERT_EQ(mReader->stop(), OK);
         mTestListener.clear();
         mFakePolicy.clear();
@@ -1802,20 +1861,17 @@
     ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
 
     // Find the test device by its name.
-    std::vector<InputDeviceInfo> inputDevices;
-    mReader->getInputDevices(inputDevices);
-    InputDeviceInfo* keyboardInfo = nullptr;
-    const char* keyboardName = keyboard->getName();
-    for (unsigned int i = 0; i < initialNumDevices + 1; i++) {
-        if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) {
-            keyboardInfo = &inputDevices[i];
-            break;
-        }
-    }
-    ASSERT_NE(keyboardInfo, nullptr);
-    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType());
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources());
-    ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size());
+    const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
+    const auto& it =
+            std::find_if(inputDevices.begin(), inputDevices.end(),
+                         [&keyboard](const InputDeviceInfo& info) {
+                             return info.getIdentifier().name == keyboard->getName();
+                         });
+
+    ASSERT_NE(it, inputDevices.end());
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
+    ASSERT_EQ(0U, it->getMotionRanges().size());
 
     keyboard.reset();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1873,18 +1929,14 @@
 // --- TouchProcessTest ---
 class TouchIntegrationTest : public InputReaderIntegrationTest {
 protected:
-    static const int32_t FIRST_SLOT = 0;
-    static const int32_t SECOND_SLOT = 1;
-    static const int32_t FIRST_TRACKING_ID = 0;
-    static const int32_t SECOND_TRACKING_ID = 1;
     const std::string UNIQUE_ID = "local:0";
 
-    virtual void SetUp() override {
+    void SetUp() override {
         InputReaderIntegrationTest::SetUp();
         // At least add an internal display.
         setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                      DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
-                                     ViewportType::VIEWPORT_INTERNAL);
+                                     ViewportType::INTERNAL);
 
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1947,9 +1999,9 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_POINTER_UP (Second slot)
-    mDevice->sendUp();
+    mDevice->sendPointerUp();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               args.action);
 
     // ACTION_UP
@@ -1964,11 +2016,13 @@
     const Point centerPoint = mDevice->getCenterPoint();
 
     // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
-    // ACTION_POINTER_DOWN (Second slot)
+    // ACTION_POINTER_DOWN (second slot)
     const Point secondPoint = centerPoint + Point(100, 100);
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
@@ -1977,26 +2031,31 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               args.action);
 
-    // ACTION_MOVE (Second slot)
+    // ACTION_MOVE (second slot)
     mDevice->sendMove(secondPoint + Point(1, 1));
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
-    // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
-    // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+    // Send MT_TOOL_PALM (second slot), which indicates that the touch IC has determined this to be
+    // a palm event.
+    // Expect to receive the ACTION_POINTER_UP with cancel flag.
     mDevice->sendToolType(MT_TOOL_PALM);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
 
-    // ACTION_POINTER_UP (Second slot)
-    mDevice->sendUp();
+    // Send up to second slot, expect first slot send moving.
+    mDevice->sendPointerUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
-    // ACTION_UP
+    // Send ACTION_UP (first slot)
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
 
-    // Expect no event received after abort the entire gesture.
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
 // --- InputDeviceTest ---
@@ -2007,33 +2066,32 @@
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
-    static const uint32_t DEVICE_CLASSES;
+    static const Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
-    FakeInputReaderContext* mFakeContext;
-
+    std::unique_ptr<InstrumentedInputReader> mReader;
     std::shared_ptr<InputDevice> mDevice;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-
-        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
-        mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+        mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
                                                 identifier);
+        mReader->pushNextDevice(mDevice);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+        mReader->loopOnce();
     }
 
-    virtual void TearDown() override {
-        mDevice = nullptr;
-        delete mFakeContext;
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -2044,14 +2102,14 @@
 const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
 const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
-        | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
+const Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
+        InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
 const int32_t InputDeviceTest::EVENTHUB_ID = 1;
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
     ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
-    ASSERT_EQ(0U, mDevice->getClasses());
+    ASSERT_EQ(Flags<InputDeviceClass>(0), mDevice->getClasses());
 }
 
 TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
@@ -2220,8 +2278,7 @@
 
     // Prepare displays.
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
-                                    ViewportType::VIEWPORT_INTERNAL);
+                                    DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, ViewportType::INTERNAL);
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                        InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
@@ -2247,33 +2304,27 @@
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
-    static const uint32_t DEVICE_CLASSES;
+    static const Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
-    FakeInputReaderContext* mFakeContext;
-    InputDevice* mDevice;
+    std::unique_ptr<InstrumentedInputReader> mReader;
+    std::shared_ptr<InputDevice> mDevice;
 
-    virtual void SetUp(uint32_t classes) {
+    virtual void SetUp(Flags<InputDeviceClass> classes) {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-        InputDeviceIdentifier identifier;
-        identifier.name = DEVICE_NAME;
-        identifier.location = DEVICE_LOCATION;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
-
-        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
+        mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
-    virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+    void SetUp() override { SetUp(DEVICE_CLASSES); }
 
-    virtual void TearDown() override {
-        delete mDevice;
-        delete mFakeContext;
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -2284,16 +2335,33 @@
 
     void configureDevice(uint32_t changes) {
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-            mFakeContext->updatePointerDisplay();
+            mReader->requestRefreshConfiguration(changes);
+            mReader->loopOnce();
         }
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location, int32_t eventHubId,
+                                           Flags<InputDeviceClass> classes) {
+        InputDeviceIdentifier identifier;
+        identifier.name = name;
+        identifier.location = location;
+        std::shared_ptr<InputDevice> device =
+                std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+                                              identifier);
+        mReader->pushNextDevice(device);
+        mFakeEventHub->addDevice(eventHubId, name, classes);
+        mReader->loopOnce();
+        return device;
+    }
+
     template <class T, typename... Args>
     T& addMapperAndConfigure(Args... args) {
         T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
+        mapper.reset(ARBITRARY_TIME);
         return mapper;
     }
 
@@ -2309,8 +2377,7 @@
         mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
-                        int32_t value) {
+    void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) {
         RawEvent event;
         event.when = when;
         event.deviceId = mapper.getDeviceContext().getEventHubId();
@@ -2318,6 +2385,7 @@
         event.code = code;
         event.value = value;
         mapper.process(&event);
+        mReader->loopOnce();
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -2348,9 +2416,9 @@
         ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
     }
 
-    static void assertPosition(const sp<FakePointerController>& controller, float x, float y) {
+    static void assertPosition(const FakePointerController& controller, float x, float y) {
         float actualX, actualY;
-        controller->getPosition(&actualX, &actualY);
+        controller.getPosition(&actualX, &actualY);
         ASSERT_NEAR(x, actualX, 1);
         ASSERT_NEAR(y, actualY, 1);
     }
@@ -2361,7 +2429,8 @@
 const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
+const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+        Flags<InputDeviceClass>(0); // not needed for current tests
 const int32_t InputMapperTest::EVENTHUB_ID = 1;
 
 // --- SwitchInputMapperTest ---
@@ -2421,8 +2490,8 @@
  * orientation.
  */
 void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+                                 NO_PORT, ViewportType::INTERNAL);
 }
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
@@ -2458,10 +2527,16 @@
     const int32_t USAGE_UNKNOWN = 0x07ffff;
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2556,13 +2631,17 @@
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    // Initial metastate.
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Metakey down.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
@@ -2570,7 +2649,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-    ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
@@ -2589,7 +2668,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-    ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
@@ -2727,7 +2806,7 @@
     // ^--- already checked by the previous test
 
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+                                 UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2737,7 +2816,7 @@
     constexpr int32_t newDisplayId = 2;
     clearViewports();
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+                                 UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2794,6 +2873,9 @@
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initialize metastate to AMETA_NUM_LOCK_ON.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Initialization should have turned all of the lights off.
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
@@ -2849,6 +2931,43 @@
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+    // Initial metastate should be AMETA_NONE as no meta keys added.
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Meta state should be AMETA_NONE after reset
+    mapper.reset(ARBITRARY_TIME);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+    NotifyKeyArgs args;
+    // Press button "A"
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AMETA_NONE, args.metaState);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+
+    // Button up.
+    process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AMETA_NONE, args.metaState);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+}
+
 TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
     // keyboard 1.
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
@@ -2858,15 +2977,13 @@
 
     // keyboard 2.
     const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "KEYBOARD2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
-    InputDeviceIdentifier identifier;
-    identifier.name = "KEYBOARD2";
-    identifier.location = USB2;
-    std::unique_ptr<InputDevice> device2 =
-            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          identifier);
-    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
@@ -2898,9 +3015,9 @@
     // Prepare second display.
     constexpr int32_t newDisplayId = 2;
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-                                 UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+                                 UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-                                 SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+                                 SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
     // Default device will reconfigure above, need additional reconfiguration for another device.
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                        InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2929,13 +3046,80 @@
                                                 AKEYCODE_DPAD_LEFT, newDisplayId));
 }
 
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+    // Initialization should have turned all of the lights off.
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+    // Toggle caps lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+    // Toggle num lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+    // Toggle scroll lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+    mFakeEventHub->removeDevice(EVENTHUB_ID);
+    mReader->loopOnce();
+
+    // keyboard 2 should default toggle keys.
+    const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "KEYBOARD2";
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+    KeyboardInputMapper& mapper2 =
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+                                                    AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+    device2->reset(ARBITRARY_TIME);
+
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+              mapper2.getMetaState());
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
 protected:
-    virtual void SetUp() override {
-        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
-    }
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
 };
 
 TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
@@ -3021,12 +3205,12 @@
 protected:
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
 
-    sp<FakePointerController> mFakePointerController;
+    std::shared_ptr<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         InputMapperTest::SetUp();
 
-        mFakePointerController = new FakePointerController();
+        mFakePointerController = std::make_shared<FakePointerController>();
         mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
@@ -3035,7 +3219,7 @@
 
     void prepareDisplay(int32_t orientation) {
         const std::string uniqueId = "local:0";
-        const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+        const ViewportType viewportType = ViewportType::INTERNAL;
         setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                 orientation, uniqueId, NO_PORT, viewportType);
     }
@@ -3125,7 +3309,7 @@
     addConfigurationProperty("cursor.mode", "navigation");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs args;
 
@@ -3682,7 +3866,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_PointerCapture) {
@@ -3710,7 +3894,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
 
     // Button press.
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
@@ -3749,14 +3933,14 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
 
     // Disable pointer capture and check that the device generation got bumped
     // and events are generated the usual way.
-    const uint32_t generation = mFakeContext->getGeneration();
+    const uint32_t generation = mReader->getContext()->getGeneration();
     mFakePolicy->setPointerCapture(false);
     configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
-    ASSERT_TRUE(mFakeContext->getGeneration() != generation);
+    ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
@@ -3770,7 +3954,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
@@ -3780,8 +3964,7 @@
     constexpr int32_t SECOND_DISPLAY_ID = 1;
     const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
     mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
-                                    SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
-                                    ViewportType::VIEWPORT_EXTERNAL);
+                                    SECOND_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::EXTERNAL);
     mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
@@ -3798,7 +3981,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
     ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
 }
 
@@ -3908,8 +4091,8 @@
 };
 
 void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
-            UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+                                 port, ViewportType::INTERNAL);
 }
 
 void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
@@ -3918,9 +4101,9 @@
 }
 
 void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
-    setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
-        VIRTUAL_DISPLAY_HEIGHT, orientation,
-        VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+    setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
+                                 orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
+                                 ViewportType::VIRTUAL);
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
@@ -4172,7 +4355,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyKeyArgs args;
 
@@ -4222,7 +4405,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyKeyArgs keyArgs;
 
@@ -4343,7 +4526,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -4418,7 +4601,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -4514,7 +4697,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5409,7 +5592,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5685,7 +5868,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5860,7 +6043,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -6773,7 +6956,7 @@
     const uint8_t hdmi1 = 0;
     const uint8_t hdmi2 = 1;
     const std::string secondaryUniqueId = "uniqueId2";
-    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    constexpr ViewportType type = ViewportType::EXTERNAL;
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
@@ -6806,14 +6989,15 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
     // Setup for second display.
-    sp<FakePointerController> fakePointerController = new FakePointerController();
+    std::shared_ptr<FakePointerController> fakePointerController =
+            std::make_shared<FakePointerController>();
     fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(100, 200);
     fakePointerController->setButtonState(0);
     mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
 
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
-    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+    prepareSecondaryDisplay(ViewportType::EXTERNAL);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
@@ -6839,15 +7023,13 @@
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "TOUCHSCREEN2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
-    InputDeviceIdentifier identifier;
-    identifier.name = "TOUCHSCREEN2";
-    identifier.location = USB2;
-    std::unique_ptr<InputDevice> device2 =
-            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          identifier);
-    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+
     mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
                                    0 /*flat*/, 0 /*fuzz*/);
     mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
@@ -6866,7 +7048,8 @@
     device2->reset(ARBITRARY_TIME);
 
     // Setup PointerController.
-    sp<FakePointerController> fakePointerController = new FakePointerController();
+    std::shared_ptr<FakePointerController> fakePointerController =
+            std::make_shared<FakePointerController>();
     mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
     mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController);
 
@@ -6879,11 +7062,11 @@
 
     // Create displays.
     prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
-    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2);
+    prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
 
     // Default device will reconfigure above, need additional reconfiguration for another device.
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-            InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
     // Two fingers down at default display.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -6990,7 +7173,7 @@
 TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
     constexpr uint8_t hdmi2 = 1;
     const std::string secondaryUniqueId = "uniqueId2";
-    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    constexpr ViewportType type = ViewportType::EXTERNAL;
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
 
@@ -7057,10 +7240,10 @@
 }
 
 /**
- * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
- * UP events should be ignored.
+ * Test single touch should be canceled when received the MT_TOOL_PALM event, and the following
+ * MOVE and UP events should be ignored.
  */
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7070,7 +7253,7 @@
 
     // default tool type is finger
     constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
-    processId(mapper, 1);
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x1, y1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7084,19 +7267,19 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
 
     // Ignore the following MOVE and UP events if had detect a palm event.
-    processId(mapper, 1);
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // finger up.
-    processId(mapper, -1);
+    processId(mapper, INVALID_TRACKING_ID);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // new finger down
+    processId(mapper, FIRST_TRACKING_ID);
     processToolType(mapper, MT_TOOL_FINGER);
-    processId(mapper, 1);
     processPosition(mapper, x3, y3);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7105,11 +7288,10 @@
 }
 
 /**
- * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger,
- * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is
- * MT_TOOL_FINGER.
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the rest active fingers could still be allowed to receive the events
  */
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7118,8 +7300,8 @@
     NotifyMotionArgs motionArgs;
 
     // default tool type is finger
-    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
-    processId(mapper, 1);
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x1, y1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7127,60 +7309,239 @@
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
     // Second finger down.
-    processSlot(mapper, 1);
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
     processPosition(mapper, x2, y2);
-    processId(mapper, 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+
+    // If the tool type of the first finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // The following MOVE events of second finger should be processed.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 1, y2 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
+    // it. Second finger receive move.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // Second finger keeps moving.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 2, y2 + 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // Second finger up.
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event, if only 1 finger
+ * is active, it should send CANCEL after receiving the MT_TOOL_PALM event.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    // First finger down.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
-    // If the tool type of the first pointer changes to MT_TOOL_PALM,
-    // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL.
-    processSlot(mapper, 0);
-    processId(mapper, 1);
+    // If the tool type of the first finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // Second finger keeps moving.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 1, y2 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // second finger becomes palm, receive cancel due to only 1 finger is active.
+    processId(mapper, SECOND_TRACKING_ID);
     processToolType(mapper, MT_TOOL_PALM);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
 
-    // Ignore the following MOVE and UP events if had detect a palm event.
-    processSlot(mapper, 1);
-    processId(mapper, 2);
+    // third finger down.
+    processSlot(mapper, THIRD_SLOT);
+    processId(mapper, THIRD_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_FINGER);
     processPosition(mapper, x3, y3);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // second finger up.
-    processId(mapper, -1);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // first finger move, but still in palm
-    processSlot(mapper, 0);
-    processId(mapper, 1);
-    processPosition(mapper, x1 - 1, y1 - 1);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // second finger down, expect as new finger down.
-    processSlot(mapper, 1);
-    processId(mapper, 2);
-    processPosition(mapper, x2, y2);
-    processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // third finger move
+    processId(mapper, THIRD_TRACKING_ID);
+    processPosition(mapper, x3 + 1, y3 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger up, third finger receive move.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // second finger up, third finger receive move.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // third finger up.
+    processSlot(mapper, THIRD_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the active finger could still be allowed to receive the events
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // If the tool type of the second finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processId(mapper, SECOND_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // The following MOVE event should be processed.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1 + 1, y1 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // second finger up.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger keep moving
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1 + 2, y1 + 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger up.
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 }
 
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
 protected:
-    virtual void SetUp() override {
-        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
-    }
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
 };
 
 /**
@@ -7204,7 +7565,7 @@
     ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
 
     // Expect the event to be sent to the external viewport if it is present.
-    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+    prepareSecondaryDisplay(ViewportType::EXTERNAL);
     processPosition(mapper, 100, 100);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7218,7 +7579,7 @@
 protected:
     void halfDisplayToCenterHorizontal(int32_t orientation) {
         std::optional<DisplayViewport> internalViewport =
-                mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+                mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
 
         // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
         internalViewport->orientation = orientation;
@@ -7336,4 +7697,223 @@
     constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
     processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
 }
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
+    // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
+    std::shared_ptr<FakePointerController> fakePointerController =
+            std::make_shared<FakePointerController>();
+    fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    fakePointerController->setPosition(0, 0);
+    fakePointerController->setButtonState(0);
+
+    // prepare device and capture
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    mFakePolicy->setPointerCapture(true);
+    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // captured touchpad should be a touchpad source
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+
+    InputDeviceInfo deviceInfo;
+    mDevice->getDeviceInfo(&deviceInfo);
+
+    const InputDeviceInfo::MotionRange* relRangeX =
+            deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
+    ASSERT_NE(relRangeX, nullptr);
+    ASSERT_EQ(relRangeX->min, -(RAW_X_MAX - RAW_X_MIN));
+    ASSERT_EQ(relRangeX->max, RAW_X_MAX - RAW_X_MIN);
+    const InputDeviceInfo::MotionRange* relRangeY =
+            deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
+    ASSERT_NE(relRangeY, nullptr);
+    ASSERT_EQ(relRangeY->min, -(RAW_Y_MAX - RAW_Y_MIN));
+    ASSERT_EQ(relRangeY->max, RAW_Y_MAX - RAW_Y_MIN);
+
+    // run captured pointer tests - note that this is unscaled, so input listener events should be
+    //                              identical to what the hardware sends (accounting for any
+    //                              calibration).
+    // FINGER 0 DOWN
+    processSlot(mapper, 0);
+    processId(mapper, 1);
+    processPosition(mapper, 100 + RAW_X_MIN, 100 + RAW_Y_MIN);
+    processKey(mapper, BTN_TOUCH, 1);
+    processSync(mapper);
+
+    // expect coord[0] to contain initial location of touch 0
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+    ASSERT_EQ(1U, args.pointerCount);
+    ASSERT_EQ(0, args.pointerProperties[0].id);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // FINGER 1 DOWN
+    processSlot(mapper, 1);
+    processId(mapper, 2);
+    processPosition(mapper, 560 + RAW_X_MIN, 154 + RAW_Y_MIN);
+    processSync(mapper);
+
+    // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            args.action);
+    ASSERT_EQ(2U, args.pointerCount);
+    ASSERT_EQ(0, args.pointerProperties[0].id);
+    ASSERT_EQ(1, args.pointerProperties[1].id);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[1], 560, 154, 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // FINGER 1 MOVE
+    processPosition(mapper, 540 + RAW_X_MIN, 690 + RAW_Y_MIN);
+    processSync(mapper);
+
+    // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+    // from move
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // FINGER 0 MOVE
+    processSlot(mapper, 0);
+    processPosition(mapper, 50 + RAW_X_MIN, 800 + RAW_Y_MIN);
+    processSync(mapper);
+
+    // expect coord[0] to contain new touch 0 location, coord[1] to contain previous location
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 50, 800, 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // BUTTON DOWN
+    processKey(mapper, BTN_LEFT, 1);
+    processSync(mapper);
+
+    // touchinputmapper design sends a move before button press
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+
+    // BUTTON UP
+    processKey(mapper, BTN_LEFT, 0);
+    processSync(mapper);
+
+    // touchinputmapper design sends a move after button release
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // FINGER 0 UP
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | 0x0000, args.action);
+
+    // FINGER 1 MOVE
+    processSlot(mapper, 1);
+    processPosition(mapper, 320 + RAW_X_MIN, 900 + RAW_Y_MIN);
+    processSync(mapper);
+
+    // expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_EQ(1U, args.pointerCount);
+    ASSERT_EQ(1, args.pointerProperties[0].id);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // FINGER 1 UP
+    processId(mapper, -1);
+    processKey(mapper, BTN_TOUCH, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+
+    // non captured touchpad should be a mouse source
+    mFakePolicy->setPointerCapture(false);
+    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
+    std::shared_ptr<FakePointerController> fakePointerController =
+            std::make_shared<FakePointerController>();
+    fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    fakePointerController->setPosition(0, 0);
+    fakePointerController->setButtonState(0);
+
+    // prepare device and capture
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    // run uncaptured pointer tests - pushes out generic events
+    // FINGER 0 DOWN
+    processId(mapper, 3);
+    processPosition(mapper, 100, 100);
+    processKey(mapper, BTN_TOUCH, 1);
+    processSync(mapper);
+
+    // start at (100,100), cursor should be at (0,0) * scale
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(
+            assertPointerCoords(args.pointerCoords[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // FINGER 0 MOVE
+    processPosition(mapper, 200, 200);
+    processSync(mapper);
+
+    // compute scaling to help with touch position checking
+    float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN);
+    float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+    float scale =
+            mFakePolicy->getPointerGestureMovementSpeedRatio() * displayDiagonal / rawDiagonal;
+
+    // translate from (100,100) -> (200,200), cursor should have changed to (100,100) * scale)
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 100 * scale, 100 * scale, 0,
+                                                0, 0, 0, 0, 0, 0, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) {
+    std::shared_ptr<FakePointerController> fakePointerController =
+            std::make_shared<FakePointerController>();
+
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    mFakePolicy->setPointerCapture(false);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // uncaptured touchpad should be a pointer device
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+
+    // captured touchpad should be a touchpad device
+    mFakePolicy->setPointerCapture(true);
+    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 0659511..7fec2c8 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -179,6 +179,11 @@
     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);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 22d1f63..01a557c 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -139,6 +139,7 @@
     void sendTrackingId(int32_t trackingId);
     void sendDown(const Point& point);
     void sendMove(const Point& point);
+    void sendPointerUp();
     void sendUp();
     void sendToolType(int32_t toolType);
 
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index ec3dfc8..b5e6ae9 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -4,6 +4,7 @@
     srcs: [
         "BatterySaverPolicyConfig.cpp",
         "CoolingDevice.cpp",
+        "ParcelDuration.cpp",
         "PowerHalController.cpp",
         "PowerHalLoader.cpp",
         "PowerHalWrapper.cpp",
diff --git a/services/powermanager/ParcelDuration.cpp b/services/powermanager/ParcelDuration.cpp
new file mode 100644
index 0000000..c0ab380
--- /dev/null
+++ b/services/powermanager/ParcelDuration.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ParcelDuration"
+
+#include <android/ParcelDuration.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t ParcelDuration::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->readInt64(&mSeconds) ?: parcel->readInt32(&mNanos);
+}
+
+status_t ParcelDuration::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->writeInt64(mSeconds) ?: parcel->writeInt32(mNanos);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index d26d582..178f545 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -15,40 +15,39 @@
  */
 
 #define LOG_TAG "PowerHalController"
-#include <utils/Log.h>
-
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
-
 #include <powermanager/PowerHalController.h>
 #include <powermanager/PowerHalLoader.h>
+#include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using namespace android::hardware::power;
 
 namespace android {
 
+namespace power {
+
 // -------------------------------------------------------------------------------------------------
 
-std::unique_ptr<PowerHalWrapper> PowerHalConnector::connect() {
-    sp<IPowerAidl> halAidl = PowerHalLoader::loadAidl();
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+    sp<IPower> halAidl = PowerHalLoader::loadAidl();
     if (halAidl) {
-        return std::make_unique<AidlPowerHalWrapper>(halAidl);
+        return std::make_unique<AidlHalWrapper>(halAidl);
     }
-    sp<IPowerV1_0> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
-    sp<IPowerV1_1> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+    sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+    sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
     if (halHidlV1_1) {
-        return std::make_unique<HidlPowerHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+        return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
     }
     if (halHidlV1_0) {
-        return std::make_unique<HidlPowerHalWrapperV1_0>(halHidlV1_0);
+        return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
     }
     return nullptr;
 }
 
-void PowerHalConnector::reset() {
+void HalConnector::reset() {
     PowerHalLoader::unloadAll();
 }
 
@@ -58,8 +57,9 @@
     initHal();
 }
 
-// Check validity of current handle to the power HAL service, and create a new one if necessary.
-std::shared_ptr<PowerHalWrapper> PowerHalController::initHal() {
+// Check validity of current handle to the power HAL service, and create a new
+// one if necessary.
+std::shared_ptr<HalWrapper> PowerHalController::initHal() {
     std::lock_guard<std::mutex> lock(mConnectedHalMutex);
     if (mConnectedHal == nullptr) {
         mConnectedHal = mHalConnector->connect();
@@ -71,10 +71,10 @@
     return mConnectedHal;
 }
 
-// Check if a call to Power HAL function failed; if so, log the failure and invalidate the
-// current Power HAL handle.
-PowerHalResult PowerHalController::processHalResult(PowerHalResult result, const char* fnName) {
-    if (result == PowerHalResult::FAILED) {
+// Check if a call to Power HAL function failed; if so, log the failure and
+// invalidate the current Power HAL handle.
+HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
+    if (result == HalResult::FAILED) {
         ALOGE("%s() failed: power HAL service not available.", fnName);
         std::lock_guard<std::mutex> lock(mConnectedHalMutex);
         // Drop Power HAL handle. This will force future api calls to reconnect.
@@ -84,16 +84,18 @@
     return result;
 }
 
-PowerHalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
-    std::shared_ptr<PowerHalWrapper> handle = initHal();
+HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+    std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setBoost(boost, durationMs);
     return processHalResult(result, "setBoost");
 }
 
-PowerHalResult PowerHalController::setMode(Mode mode, bool enabled) {
-    std::shared_ptr<PowerHalWrapper> handle = initHal();
+HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+    std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setMode(mode, enabled);
     return processHalResult(result, "setMode");
 }
 
-}; // namespace android
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 3ae5384..1f1b43a 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -21,15 +21,14 @@
 #include <binder/IServiceManager.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
-
 #include <powermanager/PowerHalLoader.h>
 
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
-using IPowerAidl = android::hardware::power::IPower;
+using namespace android::hardware::power;
 
 namespace android {
 
+namespace power {
+
 // -------------------------------------------------------------------------------------------------
 
 template <typename T, typename F>
@@ -53,9 +52,9 @@
 // -------------------------------------------------------------------------------------------------
 
 std::mutex PowerHalLoader::gHalMutex;
-sp<IPowerAidl> PowerHalLoader::gHalAidl = nullptr;
-sp<IPowerV1_0> PowerHalLoader::gHalHidlV1_0 = nullptr;
-sp<IPowerV1_1> PowerHalLoader::gHalHidlV1_1 = nullptr;
+sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
 
 void PowerHalLoader::unloadAll() {
     std::lock_guard<std::mutex> lock(gHalMutex);
@@ -64,31 +63,33 @@
     gHalHidlV1_1 = nullptr;
 }
 
-sp<IPowerAidl> PowerHalLoader::loadAidl() {
+sp<IPower> PowerHalLoader::loadAidl() {
     std::lock_guard<std::mutex> lock(gHalMutex);
     static bool gHalExists = true;
-    static auto loadFn = []() { return waitForVintfService<IPowerAidl>(); };
-    return loadHal<IPowerAidl>(gHalExists, gHalAidl, loadFn, "AIDL");
+    static auto loadFn = []() { return waitForVintfService<IPower>(); };
+    return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
 }
 
-sp<IPowerV1_0> PowerHalLoader::loadHidlV1_0() {
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
     std::lock_guard<std::mutex> lock(gHalMutex);
     return loadHidlV1_0Locked();
 }
 
-sp<IPowerV1_1> PowerHalLoader::loadHidlV1_1() {
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
     std::lock_guard<std::mutex> lock(gHalMutex);
     static bool gHalExists = true;
-    static auto loadFn = []() { return IPowerV1_1::castFrom(loadHidlV1_0Locked()); };
-    return loadHal<IPowerV1_1>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+    static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+    return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
 }
 
-sp<IPowerV1_0> PowerHalLoader::loadHidlV1_0Locked() {
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
     static bool gHalExists = true;
-    static auto loadFn = []() { return IPowerV1_0::getService(); };
-    return loadHal<IPowerV1_0>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+    static auto loadFn = []() { return V1_0::IPower::getService(); };
+    return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
 }
 
 // -------------------------------------------------------------------------------------------------
 
+} // namespace power
+
 } // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index d959a2c..4a711ca 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -14,159 +14,168 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "PowerHalWrapper"
-#include <utils/Log.h>
-
+#define LOG_TAG "HalWrapper"
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/Mode.h>
-
 #include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
-using android::hardware::power::V1_0::Feature;
-using android::hardware::power::V1_0::PowerHint;
+using namespace android::hardware::power;
 
 namespace android {
 
+namespace power {
+
 // -------------------------------------------------------------------------------------------------
 
-PowerHalResult EmptyPowerHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+inline HalResult toHalResult(const binder::Status& result) {
+    if (result.isOk()) {
+        return HalResult::SUCCESSFUL;
+    }
+    ALOGE("Power HAL request failed: %s", result.toString8().c_str());
+    return HalResult::FAILED;
+}
+
+template <typename T>
+inline HalResult toHalResult(const hardware::Return<T>& result) {
+    if (result.isOk()) {
+        return HalResult::SUCCESSFUL;
+    }
+    ALOGE("Power HAL request failed: %s", result.description().c_str());
+    return HalResult::FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
     ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
-        toString(boost).c_str(), durationMs);
-    return PowerHalResult::UNSUPPORTED;
+          toString(boost).c_str(), durationMs);
+    return HalResult::UNSUPPORTED;
 }
 
-PowerHalResult EmptyPowerHalWrapper::setMode(Mode mode, bool enabled) {
-    ALOGV("Skipped setMode %s to %s because Power HAL not available",
-        toString(mode).c_str(), enabled ? "true" : "false");
-    return PowerHalResult::UNSUPPORTED;
+HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+    ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
+          enabled ? "true" : "false");
+    return HalResult::UNSUPPORTED;
 }
 
 // -------------------------------------------------------------------------------------------------
 
-PowerHalResult HidlPowerHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
     if (boost == Boost::INTERACTION) {
-        return sendPowerHint(PowerHint::INTERACTION, durationMs);
+        return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
     } else {
-        ALOGV("Skipped setBoost %s because Power HAL AIDL not available",
-            toString(boost).c_str());
-        return PowerHalResult::UNSUPPORTED;
+        ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+        return HalResult::UNSUPPORTED;
     }
 }
 
-PowerHalResult HidlPowerHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
     uint32_t data = enabled ? 1 : 0;
     switch (mode) {
         case Mode::LAUNCH:
-            return sendPowerHint(PowerHint::LAUNCH, data);
+            return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
         case Mode::LOW_POWER:
-            return sendPowerHint(PowerHint::LOW_POWER, data);
+            return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
         case Mode::SUSTAINED_PERFORMANCE:
-            return sendPowerHint(PowerHint::SUSTAINED_PERFORMANCE, data);
+            return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
         case Mode::VR:
-            return sendPowerHint(PowerHint::VR_MODE, data);
+            return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
         case Mode::INTERACTIVE:
             return setInteractive(enabled);
         case Mode::DOUBLE_TAP_TO_WAKE:
-            return setFeature(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
+            return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
         default:
             ALOGV("Skipped setMode %s because Power HAL AIDL not available",
-                toString(mode).c_str());
-            return PowerHalResult::UNSUPPORTED;
+                  toString(mode).c_str());
+            return HalResult::UNSUPPORTED;
     }
 }
 
-PowerHalResult HidlPowerHalWrapperV1_0::sendPowerHint(PowerHint hintId, uint32_t data) {
-    auto ret = handleV1_0->powerHint(hintId, data);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    return toHalResult(mHandleV1_0->powerHint(hintId, data));
 }
 
-PowerHalResult HidlPowerHalWrapperV1_0::setInteractive(bool enabled) {
-    auto ret = handleV1_0->setInteractive(enabled);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
+    return toHalResult(mHandleV1_0->setInteractive(enabled));
 }
 
-PowerHalResult HidlPowerHalWrapperV1_0::setFeature(Feature feature, bool enabled) {
-    auto ret = handleV1_0->setFeature(feature, enabled);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+    return toHalResult(mHandleV1_0->setFeature(feature, enabled));
 }
 
 // -------------------------------------------------------------------------------------------------
 
-PowerHalResult HidlPowerHalWrapperV1_1::sendPowerHint(PowerHint hintId, uint32_t data) {
-    auto ret = handleV1_1->powerHintAsync(hintId, data);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
 }
 
 // -------------------------------------------------------------------------------------------------
 
-PowerHalResult AidlPowerHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
     std::unique_lock<std::mutex> lock(mBoostMutex);
     // Quick return if boost is not supported by HAL
     if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
-        boostSupportedArray[static_cast<int32_t>(boost)] == PowerHalSupport::OFF) {
-        ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
-            toString(boost).c_str());
-        return PowerHalResult::UNSUPPORTED;
+        mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+        ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+        return HalResult::UNSUPPORTED;
     }
 
-    if (boostSupportedArray[static_cast<int32_t>(boost)] == PowerHalSupport::UNKNOWN) {
+    if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
         bool isSupported = false;
-        auto isSupportedRet = handle->isBoostSupported(boost, &isSupported);
+        auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
         if (!isSupportedRet.isOk()) {
-            ALOGV("Skipped setBoost %s because Power HAL is not available to check support",
-                toString(boost).c_str());
-            return PowerHalResult::FAILED;
+            ALOGE("Skipped setBoost %s because check support failed with: %s",
+                  toString(boost).c_str(), isSupportedRet.toString8().c_str());
+            return HalResult::FAILED;
         }
 
-        boostSupportedArray[static_cast<int32_t>(boost)] =
-            isSupported ? PowerHalSupport::ON : PowerHalSupport::OFF;
+        mBoostSupportedArray[static_cast<int32_t>(boost)] =
+                isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
             ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
-                toString(boost).c_str());
-            return PowerHalResult::UNSUPPORTED;
+                  toString(boost).c_str());
+            return HalResult::UNSUPPORTED;
         }
     }
     lock.unlock();
 
-    auto ret = handle->setBoost(boost, durationMs);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+    return toHalResult(mHandle->setBoost(boost, durationMs));
 }
 
-PowerHalResult AidlPowerHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
     std::unique_lock<std::mutex> lock(mModeMutex);
     // Quick return if mode is not supported by HAL
     if (mode > Mode::DISPLAY_INACTIVE ||
-        modeSupportedArray[static_cast<int32_t>(mode)] == PowerHalSupport::OFF) {
-        ALOGV("Skipped setMode %s because Power HAL doesn't support it",
-            toString(mode).c_str());
-        return PowerHalResult::UNSUPPORTED;
+        mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+        ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+        return HalResult::UNSUPPORTED;
     }
 
-    if (modeSupportedArray[static_cast<int32_t>(mode)] == PowerHalSupport::UNKNOWN) {
+    if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
         bool isSupported = false;
-        auto isSupportedRet = handle->isModeSupported(mode, &isSupported);
+        auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
         if (!isSupportedRet.isOk()) {
-            ALOGV("Skipped setMode %s because Power HAL is not available to check support",
-                toString(mode).c_str());
-            return PowerHalResult::FAILED;
+            ALOGE("Skipped setMode %s because check support failed with: %s",
+                  toString(mode).c_str(), isSupportedRet.toString8().c_str());
+            return HalResult::FAILED;
         }
 
-        modeSupportedArray[static_cast<int32_t>(mode)] =
-            isSupported ? PowerHalSupport::ON : PowerHalSupport::OFF;
+        mModeSupportedArray[static_cast<int32_t>(mode)] =
+                isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
-                ALOGV("Skipped setMode %s because Power HAL doesn't support it",
-                    toString(mode).c_str());
-                return PowerHalResult::UNSUPPORTED;
+            ALOGV("Skipped setMode %s because Power HAL doesn't support it",
+                  toString(mode).c_str());
+            return HalResult::UNSUPPORTED;
         }
     }
     lock.unlock();
 
-    auto ret = handle->setMode(mode, enabled);
-    return ret.isOk() ? PowerHalResult::SUCCESSFUL : PowerHalResult::FAILED;
+    return toHalResult(mHandle->setMode(mode, enabled));
 }
 
 // -------------------------------------------------------------------------------------------------
 
-}; // namespace android
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
index 9a67901..caaec55 100644
--- a/services/powermanager/TEST_MAPPING
+++ b/services/powermanager/TEST_MAPPING
@@ -1,10 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "powermanager_test"
-    },
-    {
-      "name": "thermalmanager_test"
+      "name": "libpowermanager_test"
     }
   ]
 }
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
new file mode 100644
index 0000000..4c5d508
--- /dev/null
+++ b/services/powermanager/benchmarks/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_benchmark {
+    name: "libpowermanager_benchmarks",
+    srcs: [
+        "main.cpp",
+        "PowerHalAidlBenchmarks.cpp",
+        "PowerHalControllerBenchmarks.cpp",
+        "PowerHalHidlBenchmarks.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libpowermanager",
+        "libutils",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.power-cpp",
+    ],
+    static_libs: [
+        "libtestUtil",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
new file mode 100644
index 0000000..1004828
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalAidlBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <binder/IServiceManager.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+using std::chrono::microseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
+                         Args1&&... args1) {
+    sp<IPower> hal = waitForVintfService<IPower>();
+
+    if (hal == nullptr) {
+        ALOGI("Power HAL not available, skipping test...");
+        return;
+    }
+
+    binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+        ALOGI("Power HAL does not support this operation, skipping test...");
+        return;
+    }
+
+    while (state.KeepRunning()) {
+        ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+        state.PauseTiming();
+        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        if (delay > 0us) {
+            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+        }
+        state.ResumeTiming();
+    }
+}
+
+static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
+    bool isSupported;
+    Boost boost = static_cast<Boost>(state.range(0));
+    runBenchmark(state, 0us, &IPower::isBoostSupported, boost, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_isModeSupported(benchmark::State& state) {
+    bool isSupported;
+    Mode mode = static_cast<Mode>(state.range(0));
+    runBenchmark(state, 0us, &IPower::isModeSupported, mode, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
+    Boost boost = static_cast<Boost>(state.range(0));
+    runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1);
+}
+
+static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
+    Mode mode = static_cast<Mode>(state.range(0));
+    runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
+}
+
+BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..598080b
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <powermanager/PowerHalController.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::power::HalResult;
+using android::power::PowerHalController;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
+
+template <class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, HalResult (PowerHalController::*fn)(Args0...),
+                         Args1&&... args1) {
+    while (state.KeepRunning()) {
+        PowerHalController controller;
+        HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+        state.PauseTiming();
+        if (ret == HalResult::FAILED) state.SkipWithError("Power HAL request failed");
+        state.ResumeTiming();
+    }
+}
+
+template <class... Args0, class... Args1>
+static void runCachedBenchmark(benchmark::State& state,
+                               HalResult (PowerHalController::*fn)(Args0...), Args1&&... args1) {
+    PowerHalController controller;
+    // First call out of test, to cache HAL service and isSupported result.
+    (controller.*fn)(std::forward<Args1>(args1)...);
+
+    while (state.KeepRunning()) {
+        HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+        state.PauseTiming();
+        if (ret == HalResult::FAILED) {
+            state.SkipWithError("Power HAL request failed");
+        }
+        testDelaySpin(
+                std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
+        state.ResumeTiming();
+    }
+}
+
+static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        PowerHalController controller;
+        controller.init();
+    }
+}
+
+static void BM_PowerHalControllerBenchmarks_initCached(benchmark::State& state) {
+    PowerHalController controller;
+    // First connection out of test.
+    controller.init();
+
+    while (state.KeepRunning()) {
+        controller.init();
+    }
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
+    Boost boost = static_cast<Boost>(state.range(0));
+    runBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
+    Boost boost = static_cast<Boost>(state.range(0));
+    runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
+    Mode mode = static_cast<Mode>(state.range(0));
+    runBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+static void BM_PowerHalControllerBenchmarks_setModeCached(benchmark::State& state) {
+    Mode mode = static_cast<Mode>(state.range(0));
+    runCachedBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+BENCHMARK(BM_PowerHalControllerBenchmarks_init);
+BENCHMARK(BM_PowerHalControllerBenchmarks_initCached);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoostCached)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setModeCached)->DenseRange(FIRST_MODE, LAST_MODE, 1);
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
new file mode 100644
index 0000000..97e026b
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalHidlBenchmarks"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::Return;
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using std::chrono::microseconds;
+using IPower1_0 = android::hardware::power::V1_0::IPower;
+using IPower1_1 = android::hardware::power::V1_1::IPower;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from types.hal from versions 1.0 to 1.3.
+static constexpr int64_t FIRST_POWER_HINT = static_cast<int64_t>(PowerHint::VSYNC);
+static constexpr int64_t LAST_POWER_HINT = static_cast<int64_t>(PowerHint::LAUNCH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class I, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> (I::*fn)(Args0...),
+                         Args1&&... args1) {
+    sp<I> hal = I::getService();
+
+    if (hal == nullptr) {
+        ALOGI("Power HAL HIDL not available, skipping test...");
+        return;
+    }
+
+    while (state.KeepRunning()) {
+        Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+        state.PauseTiming();
+        if (!ret.isOk()) state.SkipWithError(ret.description().c_str());
+        if (delay > 0us) {
+            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+        }
+        state.ResumeTiming();
+    }
+}
+
+static void BM_PowerHalHidlBenchmarks_setFeature(benchmark::State& state) {
+    runBenchmark(state, 0us, &IPower1_0::setFeature, Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE,
+                 false);
+}
+
+static void BM_PowerHalHidlBenchmarks_setInteractive(benchmark::State& state) {
+    runBenchmark(state, 0us, &IPower1_0::setInteractive, false);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHint(benchmark::State& state) {
+    PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+    runBenchmark(state, 0us, &IPower1_0::powerHint, powerHint, 0);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHintAsync(benchmark::State& state) {
+    PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+    runBenchmark(state, ONEWAY_API_DELAY, &IPower1_1::powerHintAsync, powerHint, 0);
+}
+
+BENCHMARK(BM_PowerHalHidlBenchmarks_setFeature);
+BENCHMARK(BM_PowerHalHidlBenchmarks_setInteractive);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHint)->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHintAsync)
+        ->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/services/powermanager/benchmarks/main.cpp
similarity index 72%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to services/powermanager/benchmarks/main.cpp
index 10cf17c..15c57bf 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/services/powermanager/benchmarks/main.cpp
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *            http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,6 @@
  * limitations under the License.
  */
 
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+#include <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/services/powermanager/include/android/ParcelDuration.h b/services/powermanager/include/android/ParcelDuration.h
new file mode 100644
index 0000000..117d173
--- /dev/null
+++ b/services/powermanager/include/android/ParcelDuration.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_PARCELDURATION_H
+#define ANDROID_OS_PARCELDURATION_H
+
+#include <binder/Parcelable.h>
+#include <math.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * Parcelable version of {@link java.time.Duration} that can be used in binder calls.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/ParcelDuration.java
+ */
+struct ParcelDuration : public android::Parcelable {
+    ParcelDuration(int64_t seconds = 0, int32_t nanos = 0) : mSeconds(seconds), mNanos(nanos) {}
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
+    status_t writeToParcel(android::Parcel* parcel) const override;
+    bool operator==(const ParcelDuration& pd) const {
+        return mSeconds == pd.mSeconds && mNanos == pd.mNanos;
+    }
+
+private:
+    int64_t mSeconds;
+    int32_t mNanos;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_PARCELDURATION_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 70837cf..49abc11 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -13,9 +13,10 @@
 // limitations under the License.
 
 cc_test {
-    name: "powermanager_test",
+    name: "libpowermanager_test",
     test_suites: ["device-tests"],
     srcs: [
+        "IThermalManagerTest.cpp",
         "PowerHalControllerTest.cpp",
         "PowerHalLoaderTest.cpp",
         "PowerHalWrapperAidlTest.cpp",
@@ -42,22 +43,3 @@
         "libgmock",
     ],
 }
-
-cc_test {
-    name: "thermalmanager_test",
-    test_suites: ["device-tests"],
-    srcs: ["IThermalManagerTest.cpp",],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
-    shared_libs: [
-        "libbase",
-        "libhidlbase",
-        "liblog",
-        "libpowermanager",
-        "libbinder",
-        "libutils",
-    ],
-}
diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
index 575b9ee..b62be5f 100644
--- a/services/powermanager/tests/IThermalManagerTest.cpp
+++ b/services/powermanager/tests/IThermalManagerTest.cpp
@@ -86,12 +86,14 @@
     EXPECT_NE(binder, nullptr);
     mThermalSvc = interface_cast<IThermalService>(binder);
     EXPECT_NE(mThermalSvc, nullptr);
+    // Lock mutex for operation, so listener will only be processed after wait_for is called
+    std::unique_lock<std::mutex> lock(mMutex);
     bool success = false;
     binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+    // Check the result
     ASSERT_TRUE(success);
     ASSERT_TRUE(ret.isOk());
     // Wait for listener called after registration, shouldn't timeout
-    std::unique_lock<std::mutex> lock(mMutex);
     EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
 }
 
@@ -111,6 +113,7 @@
 
 TEST_P(IThermalListenerTest, TestListener) {
     int level = GetParam();
+    // Lock mutex for operation, so listener will only be processed after wait_for is called
     std::unique_lock<std::mutex> lock(mMutex);
     // Set the override thermal status
     setThermalOverride(level);
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index ac1e19a..141b244 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -19,15 +19,12 @@
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
-#include <powermanager/PowerHalWrapper.h>
 #include <powermanager/PowerHalController.h>
+#include <utils/Log.h>
 
 #include <thread>
-#include <utils/Log.h>
 
 using android::hardware::power::Boost;
 using android::hardware::power::Mode;
@@ -36,6 +33,7 @@
 using android::hardware::power::V1_0::PowerHint;
 
 using namespace android;
+using namespace android::power;
 using namespace std::chrono_literals;
 using namespace testing;
 
@@ -45,23 +43,21 @@
 public:
     MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
     MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, getPlatformLowPowerStats,
-        (getPlatformLowPowerStats_cb _hidl_cb), (override));
+    MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+    MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+                (getPlatformLowPowerStats_cb _hidl_cb), (override));
 };
 
-class TestPowerHalConnector : public PowerHalConnector {
+class TestPowerHalConnector : public HalConnector {
 public:
     TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
     virtual ~TestPowerHalConnector() = default;
 
-    virtual std::unique_ptr<PowerHalWrapper> connect() override {
+    virtual std::unique_ptr<HalWrapper> connect() override {
         mCountMutex.lock();
         ++mConnectedCount;
         mCountMutex.unlock();
-        return std::make_unique<HidlPowerHalWrapperV1_0>(mHal);
+        return std::make_unique<HidlHalWrapperV1_0>(mHal);
     }
 
     void reset() override {
@@ -70,13 +66,9 @@
         mCountMutex.unlock();
     }
 
-    int getConnectCount() {
-        return mConnectedCount;
-    }
+    int getConnectCount() { return mConnectedCount; }
 
-    int getResetCount() {
-        return mResetCount;
-    }
+    int getResetCount() { return mResetCount; }
 
 private:
     sp<IPower> mHal = nullptr;
@@ -89,8 +81,8 @@
 public:
     AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
 
-    std::unique_ptr<PowerHalWrapper> connect() override {
-        // Call parent to update counter, but ignore connected PowerHalWrapper.
+    std::unique_ptr<HalWrapper> connect() override {
+        // Call parent to update counter, but ignore connected HalWrapper.
         TestPowerHalConnector::connect();
         return nullptr;
     }
@@ -103,7 +95,7 @@
     void SetUp() override {
         mMockHal = new StrictMock<MockIPowerV1_0>();
         std::unique_ptr<TestPowerHalConnector> halConnector =
-            std::make_unique<TestPowerHalConnector>(mMockHal);
+                std::make_unique<TestPowerHalConnector>(mMockHal);
         mHalConnector = halConnector.get();
         mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
     }
@@ -132,20 +124,22 @@
 
 TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
     std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
-        std::make_unique<AlwaysFailingTestPowerHalConnector>();
+            std::make_unique<AlwaysFailingTestPowerHalConnector>();
     AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
     PowerHalController halController(std::move(halConnector));
 
     int powerHalConnectCount = failingHalConnector->getConnectCount();
     EXPECT_EQ(powerHalConnectCount, 0);
 
-    // Still works with EmptyPowerHalWrapper as fallback ignoring every api call and logging.
+    // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
+    // and logging.
     auto result = halController.setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
     result = halController.setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 
-    // PowerHalConnector was called every time to attempt to reconnect with underlying service.
+    // PowerHalConnector was called every time to attempt to reconnect with
+    // underlying service.
     powerHalConnectCount = failingHalConnector->getConnectCount();
     EXPECT_EQ(powerHalConnectCount, 2);
     // PowerHalConnector was never reset.
@@ -160,15 +154,14 @@
     {
         InSequence seg;
         EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
     }
 
     auto result = mHalController->setBoost(Boost::INTERACTION, 100);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mHalController->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 
     // PowerHalConnector was called only once and never reset.
     powerHalConnectCount = mHalConnector->getConnectCount();
@@ -182,23 +175,23 @@
     EXPECT_EQ(powerHalConnectCount, 0);
 
     ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
-        .WillByDefault([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .WillByDefault([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
-    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
-        .Times(Exactly(4));
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
 
     auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mHalController->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
     result = mHalController->setMode(Mode::VR, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mHalController->setMode(Mode::LOW_POWER, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 
-    // PowerHalConnector was called only twice: on first api call and after failed call.
+    // PowerHalConnector was called only twice: on first api call and after failed
+    // call.
     powerHalConnectCount = mHalConnector->getConnectCount();
     EXPECT_EQ(powerHalConnectCount, 2);
     // PowerHalConnector was reset once after failed call.
@@ -211,9 +204,9 @@
     EXPECT_EQ(powerHalConnectCount, 0);
 
     auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
     result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 
     // PowerHalConnector was called only once and never reset.
     powerHalConnectCount = mHalConnector->getConnectCount();
@@ -226,19 +219,19 @@
     int powerHalConnectCount = mHalConnector->getConnectCount();
     EXPECT_EQ(powerHalConnectCount, 0);
 
-    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
-        .Times(Exactly(10));
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10));
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    // PowerHalConnector was called only by the first thread to use the api and never reset.
+    // PowerHalConnector was called only by the first thread to use the api and
+    // never reset.
     powerHalConnectCount = mHalConnector->getConnectCount();
     EXPECT_EQ(powerHalConnectCount, 1);
     int powerHalResetCount = mHalConnector->getResetCount();
@@ -250,36 +243,36 @@
     EXPECT_EQ(powerHalConnectCount, 0);
 
     ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
-        .WillByDefault([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .WillByDefault([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
-    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
-        .Times(Exactly(40));
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40));
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::LAUNCH, true);
-            ASSERT_EQ(PowerHalResult::FAILED, result);
+            ASSERT_EQ(HalResult::FAILED, result);
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::LOW_POWER, false);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::VR, true);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
     // PowerHalConnector was called at least once by the first thread.
-    // Reset and reconnect calls were made at most 10 times, once after each failure.
+    // Reset and reconnect calls were made at most 10 times, once after each
+    // failure.
     powerHalConnectCount = mHalConnector->getConnectCount();
     EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
     int powerHalResetCount = mHalConnector->getResetCount();
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index 2310a72..058e1b5 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -19,17 +19,18 @@
 #include <android-base/logging.h>
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/IPower.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalLoader.h>
 
 #include <future>
-#include <gtest/gtest.h>
-
-#include <powermanager/PowerHalLoader.h>
 
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerAidl = android::hardware::power::IPower;
 
 using namespace android;
+using namespace android::power;
+using namespace testing;
 
 // -------------------------------------------------------------------------------------------------
 
@@ -54,14 +55,10 @@
 // -------------------------------------------------------------------------------------------------
 
 template <typename T>
-class PowerHalLoaderTest : public testing::Test {
+class PowerHalLoaderTest : public Test {
 public:
-    sp<T> load() {
-        return ::loadHal<T>();
-    }
-    void unload() {
-        PowerHalLoader::unloadAll();
-    }
+    sp<T> load() { return ::loadHal<T>(); }
+    void unload() { PowerHalLoader::unloadAll(); }
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -95,7 +92,7 @@
     std::vector<std::future<sp<TypeParam>>> futures;
     for (int i = 0; i < 10; i++) {
         futures.push_back(
-            std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+                std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
     }
 
     futures[0].wait();
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 73b7466..a765659 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -19,14 +19,12 @@
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
 
 #include <thread>
-#include <utils/Log.h>
 
 using android::binder::Status;
 using android::hardware::power::Boost;
@@ -34,6 +32,7 @@
 using android::hardware::power::Mode;
 
 using namespace android;
+using namespace android::power;
 using namespace std::chrono_literals;
 using namespace testing;
 
@@ -57,7 +56,7 @@
     void SetUp() override;
 
 protected:
-    std::unique_ptr<PowerHalWrapper> mWrapper = nullptr;
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIPower>> mMockHal = nullptr;
 };
 
@@ -65,7 +64,7 @@
 
 void PowerHalWrapperAidlTest::SetUp() {
     mMockHal = new StrictMock<MockIPower>();
-    mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+    mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
     ASSERT_NE(mWrapper, nullptr);
 }
 
@@ -75,62 +74,61 @@
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
     }
 
     auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
-            .Times(Exactly(1))
-            .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
     }
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
     result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
     EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
-        .Times(Exactly(1))
-        .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
     result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
-            .Times(Exactly(10));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
     }
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
@@ -140,62 +138,61 @@
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
     }
 
     auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
-            .Times(Exactly(1))
-            .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
     result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
     EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
-        .Times(Exactly(1))
-        .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
     result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false)))
-            .Times(Exactly(10));
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
     }
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mWrapper->setMode(Mode::LAUNCH, false);
-            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 5379054..6693d0b 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -20,12 +20,9 @@
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <powermanager/PowerHalWrapper.h>
-
 #include <utils/Log.h>
 
 using android::hardware::power::Boost;
@@ -35,6 +32,7 @@
 using android::hardware::power::V1_0::PowerHint;
 
 using namespace android;
+using namespace android::power;
 using namespace std::chrono_literals;
 using namespace testing;
 
@@ -44,11 +42,9 @@
 public:
     MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
     MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, getPlatformLowPowerStats,
-        (getPlatformLowPowerStats_cb _hidl_cb), (override));
+    MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+    MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+                (getPlatformLowPowerStats_cb _hidl_cb), (override));
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -58,7 +54,7 @@
     void SetUp() override;
 
 protected:
-    std::unique_ptr<PowerHalWrapper> mWrapper = nullptr;
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
 };
 
@@ -66,80 +62,75 @@
 
 void PowerHalWrapperHidlV1_0Test::SetUp() {
     mMockHal = new StrictMock<MockIPowerV1_0>();
-    mWrapper = std::make_unique<HidlPowerHalWrapperV1_0>(mMockHal);
+    mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal);
     ASSERT_NE(mWrapper, nullptr);
 }
 
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) {
-    EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
-        .Times(Exactly(1));
+    EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
     EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
-        .Times(Exactly(1))
-        .WillRepeatedly([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .Times(Exactly(1))
+            .WillRepeatedly([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
     auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0)))
-            .Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(),
                     setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::LOW_POWER, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::VR, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::INTERACTIVE, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
     EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
-        .Times(Exactly(1))
-        .WillRepeatedly([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .Times(Exactly(1))
+            .WillRepeatedly([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
     auto result = mWrapper->setMode(Mode::LAUNCH, 1);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
     auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 931c0d5..55bbd6d 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -21,12 +21,9 @@
 #include <android/hardware/power/IPower.h>
 #include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <powermanager/PowerHalWrapper.h>
-
 #include <utils/Log.h>
 
 using android::hardware::power::Boost;
@@ -37,6 +34,7 @@
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
 using namespace android;
+using namespace android::power;
 using namespace std::chrono_literals;
 using namespace testing;
 
@@ -46,27 +44,21 @@
 public:
     MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
     MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, getPlatformLowPowerStats,
-        (getPlatformLowPowerStats_cb _hidl_cb), (override));
+    MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+    MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+                (getPlatformLowPowerStats_cb _hidl_cb), (override));
 };
 
 class MockIPowerV1_1 : public IPowerV1_1 {
 public:
     MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
     MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, getPlatformLowPowerStats,
-        (getPlatformLowPowerStats_cb _hidl_cb), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
-    MOCK_METHOD(
-        hardware::Return<void>, getSubsystemLowPowerStats,
-        (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+    MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+    MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+                (getPlatformLowPowerStats_cb _hidl_cb), (override));
+    MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
+    MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+                (getSubsystemLowPowerStats_cb _hidl_cb), (override));
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -76,7 +68,7 @@
     void SetUp() override;
 
 protected:
-    std::unique_ptr<PowerHalWrapper> mWrapper = nullptr;
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
     sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
 };
@@ -86,7 +78,7 @@
 void PowerHalWrapperHidlV1_1Test::SetUp() {
     mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
     mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
-    mWrapper = std::make_unique<HidlPowerHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+    mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
     ASSERT_NE(mWrapper, nullptr);
 }
 
@@ -94,72 +86,72 @@
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
     EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
-        .Times(Exactly(1));
+            .Times(Exactly(1));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
     EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
-        .Times(Exactly(1))
-        .WillRepeatedly([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .Times(Exactly(1))
+            .WillRepeatedly([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
     auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
         EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
+        EXPECT_CALL(*mMockHalV1_1.get(),
+                    powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+                .Times(Exactly(1));
         EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
-            .Times(Exactly(1));
-        EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
+        EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
         EXPECT_CALL(*mMockHalV1_0.get(),
                     setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
-            .Times(Exactly(1));
+                .Times(Exactly(1));
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::LOW_POWER, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::VR, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::INTERACTIVE, true);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
     result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
-    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
     EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
-        .Times(Exactly(1))
-        .WillRepeatedly([](PowerHint, int32_t) {
-            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
-        });
+            .Times(Exactly(1))
+            .WillRepeatedly([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
 
     auto result = mWrapper->setMode(Mode::LAUNCH, 1);
-    ASSERT_EQ(PowerHalResult::FAILED, result);
+    ASSERT_EQ(HalResult::FAILED, result);
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
     auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
 }
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 8a282e2..2810bff 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -145,20 +145,23 @@
                     convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
 
                     if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
-                        if(sensor.resolution == 0) {
-                            // Don't crash here or the device will go into a crashloop.
-                            ALOGW("%s must have a non-zero resolution", sensor.name);
-                            // For simple algos, map their resolution to 1 if it's not specified
-                            sensor.resolution =
-                                    SensorDeviceUtils::defaultResolutionForType(sensor.type);
-                        }
+                        sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
 
-                        double promotedResolution = sensor.resolution;
-                        double promotedMaxRange = sensor.maxRange;
-                        if (fmod(promotedMaxRange, promotedResolution) != 0) {
-                            ALOGW("%s's max range %f is not a multiple of the resolution %f",
-                                    sensor.name, sensor.maxRange, sensor.resolution);
-                            SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution);
+                        // Some sensors don't have a default resolution and will be left at 0.
+                        // Don't crash in this case since CTS will verify that devices don't go to
+                        // production with a resolution of 0.
+                        if (sensor.resolution != 0) {
+                            double promotedResolution = sensor.resolution;
+                            double promotedMaxRange = sensor.maxRange;
+                            if (fmod(promotedMaxRange, promotedResolution) != 0) {
+                                ALOGW("%s's max range %f is not a multiple of the resolution %f",
+                                        sensor.name, sensor.maxRange, sensor.resolution);
+                                SensorDeviceUtils::quantizeValue(
+                                        &sensor.maxRange, promotedResolution);
+                            }
+                        } else {
+                            // Don't crash here or the device will go into a crashloop.
+                            ALOGW("%s should have a non-zero resolution", sensor.name);
                         }
                     }
 
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 52213cf..5aa283e 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -31,7 +31,6 @@
 namespace SensorDeviceUtils {
 
 void quantizeSensorEventValues(sensors_event_t *event, float resolution) {
-    LOG_FATAL_IF(resolution == 0, "Resolution must be specified for all sensors!");
     if (resolution == 0) {
         return;
     }
@@ -79,8 +78,26 @@
     }
 }
 
-float defaultResolutionForType(int type) {
-    switch ((SensorTypeV2_1)type) {
+float resolutionForSensor(const sensor_t &sensor) {
+    switch ((SensorTypeV2_1)sensor.type) {
+        case SensorTypeV2_1::ACCELEROMETER:
+        case SensorTypeV2_1::MAGNETIC_FIELD:
+        case SensorTypeV2_1::GYROSCOPE:
+        case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
+        case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
+        case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED: {
+            if (sensor.maxRange == 0) {
+                ALOGE("No max range for sensor type %d, can't determine appropriate resolution",
+                        sensor.type);
+                return sensor.resolution;
+            }
+            // Accel, gyro, and mag shouldn't have more than 24 bits of resolution on the most
+            // advanced devices.
+            double lowerBound = 2.0 * sensor.maxRange / std::pow(2, 24);
+
+            // No need to check the upper bound as that's already enforced through CTS.
+            return std::max(sensor.resolution, static_cast<float>(lowerBound));
+        }
         case SensorTypeV2_1::SIGNIFICANT_MOTION:
         case SensorTypeV2_1::STEP_DETECTOR:
         case SensorTypeV2_1::STEP_COUNTER:
@@ -91,12 +108,14 @@
         case SensorTypeV2_1::WRIST_TILT_GESTURE:
         case SensorTypeV2_1::STATIONARY_DETECT:
         case SensorTypeV2_1::MOTION_DETECT:
+            // Ignore input resolution as all of these sensors are required to have a resolution of
+            // 1.
             return 1.0f;
         default:
-            // fall through and return 0 for all other types
+            // fall through and return the current resolution for all other types
             break;
     }
-    return 0.0f;
+    return sensor.resolution;
 }
 
 HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() {
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index d7e621c..1309971 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -19,6 +19,7 @@
 
 #include <android/hidl/manager/1.0/IServiceNotification.h>
 #include <hardware/sensors.h>
+#include <utils/Log.h>
 
 #include <cmath>
 #include <condition_variable>
@@ -33,18 +34,22 @@
 
 // Quantizes a single value using a sensor's resolution.
 inline void quantizeValue(float *value, double resolution) {
+    if (resolution == 0) {
+        return;
+    }
+
     // Increase the value of the sensor's nominal resolution to ensure that
     // sensor accuracy improvements, like runtime calibration, are not masked
     // during requantization.
-    double incRes = 0.25 * resolution;
+    double incRes = 0.125 * resolution;
     *value = round(static_cast<double>(*value) / incRes) * incRes;
 }
 
 // Ensures a sensor event doesn't provide values finer grained than its sensor resolution allows.
 void quantizeSensorEventValues(sensors_event_t *event, float resolution);
 
-// Provides a default resolution for simple sensor types if one wasn't provided by the HAL.
-float defaultResolutionForType(int type);
+// Returns the expected resolution value for the given sensor
+float resolutionForSensor(const sensor_t &sensor);
 
 class HidlServiceRegistrationWaiter : public IServiceNotification {
 public:
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index ccf05d9..6810c1b7 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <log/log.h>
 #include <sys/socket.h>
 #include <utils/threads.h>
 
@@ -28,6 +29,12 @@
 #define UNUSED(x) (void)(x)
 
 namespace android {
+namespace {
+
+// Used as the default value for the target SDK until it's obtained via getTargetSdkVersion.
+constexpr int kTargetSdkUnknown = 0;
+
+}  // namespace
 
 SensorService::SensorEventConnection::SensorEventConnection(
         const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
@@ -35,7 +42,8 @@
     : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
       mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
       mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
-      mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
+      mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown),
+      mDestroyed(false) {
     mChannel = new BitTube(mService->mSocketBufferSize);
 #if DEBUG_CONNECTIONS
     mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -46,20 +54,13 @@
 SensorService::SensorEventConnection::~SensorEventConnection() {
     ALOGD_IF(DEBUG_CONNECTIONS, "~SensorEventConnection(%p)", this);
     destroy();
-}
-
-void SensorService::SensorEventConnection::destroy() {
-    Mutex::Autolock _l(mDestroyLock);
-
-    // destroy once only
-    if (mDestroyed) {
-        return;
-    }
-
     mService->cleanupConnection(this);
     if (mEventCache != nullptr) {
         delete[] mEventCache;
     }
+}
+
+void SensorService::SensorEventConnection::destroy() {
     mDestroyed = true;
 }
 
@@ -160,7 +161,7 @@
     Mutex::Autolock _l(mConnectionLock);
     sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
     if (si == nullptr ||
-        !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
+        !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) ||
         mSensorInfo.count(handle) > 0) {
         return false;
     }
@@ -271,11 +272,16 @@
     }
 }
 
-void SensorService::SensorEventConnection::incrementPendingFlushCount(int32_t handle) {
-    Mutex::Autolock _l(mConnectionLock);
-    if (mSensorInfo.count(handle) > 0) {
-        FlushInfo& flushInfo = mSensorInfo[handle];
-        flushInfo.mPendingFlushEventsToSend++;
+bool SensorService::SensorEventConnection::incrementPendingFlushCountIfHasAccess(int32_t handle) {
+    if (hasSensorAccess()) {
+        Mutex::Autolock _l(mConnectionLock);
+        if (mSensorInfo.count(handle) > 0) {
+            FlushInfo& flushInfo = mSensorInfo[handle];
+            flushInfo.mPendingFlushEventsToSend++;
+        }
+        return true;
+    } else {
+        return false;
     }
 }
 
@@ -439,8 +445,29 @@
     bool success = true;
     const auto iter = mHandleToAppOp.find(event.sensor);
     if (iter != mHandleToAppOp.end()) {
-        int32_t appOpMode = mService->sAppOpsManager.noteOp((*iter).second, mUid, mOpPackageName);
-        success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        if (mTargetSdk == kTargetSdkUnknown) {
+            // getTargetSdkVersion returns -1 if it fails so this operation should only be run once
+            // per connection and then cached. Perform this here as opposed to in the constructor to
+            // avoid log spam for NDK/VNDK clients that don't use sensors guarded with permissions
+            // and pass in invalid op package names.
+            mTargetSdk = SensorService::getTargetSdkVersion(mOpPackageName);
+        }
+
+        // Special handling for step count/detect backwards compatibility: if the app's target SDK
+        // is pre-Q, still permit delivering events to the app even if permission isn't granted
+        // (since this permission was only introduced in Q)
+        if ((event.type == SENSOR_TYPE_STEP_COUNTER || event.type == SENSOR_TYPE_STEP_DETECTOR) &&
+                mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+            success = true;
+        } else {
+            int32_t sensorHandle = event.sensor;
+            String16 noteMsg("Sensor event (");
+            noteMsg.append(String16(mService->getSensorStringType(sensorHandle)));
+            noteMsg.append(String16(")"));
+            int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
+                                                                mOpPackageName, {}, noteMsg);
+            success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        }
     }
     return success;
 }
@@ -650,6 +677,11 @@
         int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
         int reservedFlags)
 {
+    if (mDestroyed) {
+        android_errorWriteLog(0x534e4554, "168211968");
+        return DEAD_OBJECT;
+    }
+
     status_t err;
     if (enabled) {
         err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
@@ -664,10 +696,19 @@
 status_t SensorService::SensorEventConnection::setEventRate(
         int handle, nsecs_t samplingPeriodNs)
 {
+    if (mDestroyed) {
+        android_errorWriteLog(0x534e4554, "168211968");
+        return DEAD_OBJECT;
+    }
+
     return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
 }
 
 status_t  SensorService::SensorEventConnection::flush() {
+    if (mDestroyed) {
+        return DEAD_OBJECT;
+    }
+
     return  mService->flushSensor(this, mOpPackageName);
 }
 
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 13cee6f..9487a39 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SENSOR_EVENT_CONNECTION_H
 #define ANDROID_SENSOR_EVENT_CONNECTION_H
 
+#include <atomic>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unordered_map>
@@ -116,8 +117,9 @@
     // for writing send the data from the cache.
     virtual int handleEvent(int fd, int events, void* data);
 
-    // Increment mPendingFlushEventsToSend for the given sensor handle.
-    void incrementPendingFlushCount(int32_t handle);
+    // Increment mPendingFlushEventsToSend for the given handle if the connection has sensor access.
+    // Returns true if this connection does have sensor access.
+    bool incrementPendingFlushCountIfHasAccess(int32_t handle);
 
     // Add or remove the file descriptor associated with the BitTube to the looper. If mDead is set
     // to true or there are no more sensors for this connection, the file descriptor is removed if
@@ -175,13 +177,14 @@
     int mEventsDropped;
     String8 mPackageName;
     const String16 mOpPackageName;
+    int mTargetSdk;
 #if DEBUG_CONNECTIONS
     int mEventsReceived, mEventsSent, mEventsSentFromCache;
     int mTotalAcksNeeded, mTotalAcksReceived;
 #endif
 
-    mutable Mutex mDestroyLock;
-    bool mDestroyed;
+    // Used to track if this object was inappropriately used after destroy().
+    std::atomic_bool mDestroyed;
 
     // Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
     // valid mapping for sensors that require a permission in order to reduce the lookup time.
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 0ce32cc..85ce0f0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -57,6 +57,12 @@
             mNonSensor.getName());
 }
 
+String8 SensorList::getStringType(int handle) const {
+    return getOne<String8>(
+            handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();},
+            mNonSensor.getStringType());
+}
+
 sp<SensorInterface> SensorList::getInterface(int handle) const {
     return getOne<sp<SensorInterface>>(
             handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8424b22..617ceef 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -53,6 +53,8 @@
     const Vector<Sensor> getVirtualSensors() const;
 
     String8 getName(int handle) const;
+    String8 getStringType(int handle) const;
+
     sp<SensorInterface> getInterface(int handle) const;
     bool isNewHandle(int handle) const;
 
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2b4fd7f..8f25bdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -79,6 +79,8 @@
 bool SensorService::sHmacGlobalKeyIsValid = false;
 std::map<String16, int> SensorService::sPackageTargetVersion;
 Mutex SensorService::sPackageTargetVersionLock;
+String16 SensorService::sSensorInterfaceDescriptorPrefix =
+        String16("android.frameworks.sensorservice@");
 AppOpsManager SensorService::sAppOpsManager;
 
 #define SENSOR_SERVICE_DIR "/data/system/sensor_service"
@@ -1110,6 +1112,10 @@
     return mSensors.getName(handle);
 }
 
+String8 SensorService::getSensorStringType(int handle) const {
+    return mSensors.getStringType(handle);
+}
+
 bool SensorService::isVirtualSensor(int handle) const {
     sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
     return sensor != nullptr && sensor->isVirtual();
@@ -1777,7 +1783,10 @@
         if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0 || isVirtualSensor(handle)) {
             // For older devices just increment pending flush count which will send a trivial
             // flush complete event.
-            connection->incrementPendingFlushCount(handle);
+            if (!connection->incrementPendingFlushCountIfHasAccess(handle)) {
+                ALOGE("flush called on an inaccessible sensor");
+                err = INVALID_OPERATION;
+            }
         } else {
             if (!canAccessSensor(sensor->getSensor(), "Tried flushing", opPackageName)) {
                 err = INVALID_OPERATION;
@@ -1802,39 +1811,30 @@
     }
 
     const int32_t opCode = sensor.getRequiredAppOp();
-    const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
-            IPCThreadState::self()->getCallingUid(), opPackageName);
-    bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
+    int targetSdkVersion = getTargetSdkVersion(opPackageName);
 
     bool canAccess = false;
-    if (hasPermissionForSensor(sensor)) {
-        // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
-        if (opCode < 0 || appOpAllowed) {
-            canAccess = true;
-        }
-    } else if (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
-            sensor.getType() == SENSOR_TYPE_STEP_DETECTOR) {
-        int targetSdkVersion = getTargetSdkVersion(opPackageName);
-        // Allow access to the sensor if the application targets pre-Q, which is before the
+    if (targetSdkVersion > 0 && targetSdkVersion <= __ANDROID_API_P__ &&
+            (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
+             sensor.getType() == SENSOR_TYPE_STEP_DETECTOR)) {
+        // Allow access to step sensors if the application targets pre-Q, which is before the
         // requirement to hold the AR permission to access Step Counter and Step Detector events
-        // was introduced, and the user hasn't revoked the app op.
-        //
-        // Verifying the app op is required to ensure that the user hasn't revoked the necessary
-        // permissions to access the Step Detector and Step Counter when the application targets
-        // pre-Q. Without this check, if the user revokes the pre-Q install-time GMS Core AR
-        // permission, the app would still be able to receive Step Counter and Step Detector events.
-        if (appOpAllowed &&
-                targetSdkVersion > 0 &&
-                targetSdkVersion <= __ANDROID_API_P__) {
+        // was introduced.
+        canAccess = true;
+    } else if (hasPermissionForSensor(sensor)) {
+        // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
+        if (opCode >= 0) {
+            const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
+                    IPCThreadState::self()->getCallingUid(), opPackageName);
+            canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        } else {
             canAccess = true;
         }
     }
 
-    if (canAccess) {
-        sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
-    } else {
-        ALOGE("%s a sensor (%s) without holding its required permission: %s",
-                operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+    if (!canAccess) {
+        ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
+              operation, sensor.getName().string(), sensor.getRequiredPermission().string());
     }
 
     return canAccess;
@@ -1855,6 +1855,13 @@
 }
 
 int SensorService::getTargetSdkVersion(const String16& opPackageName) {
+    // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This
+    // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't
+    // give it elevated privileges.
+    if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) {
+        return -1;
+    }
+
     Mutex::Autolock packageLock(sPackageTargetVersionLock);
     int targetSdkVersion = -1;
     auto entry = sPackageTargetVersion.find(opPackageName);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3bb8421..50c7c2f 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -295,6 +295,7 @@
     virtual status_t dump(int fd, const Vector<String16>& args);
     status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
     String8 getSensorName(int handle) const;
+    String8 getSensorStringType(int handle) const;
     bool isVirtualSensor(int handle) const;
     sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
     bool isWakeUpSensor(int type) const;
@@ -424,6 +425,7 @@
     static AppOpsManager sAppOpsManager;
     static std::map<String16, int> sPackageTargetVersion;
     static Mutex sPackageTargetVersionLock;
+    static String16 sSensorInterfaceDescriptorPrefix;
 };
 
 } // namespace android
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index 80c3b65..ae0a984 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -58,7 +58,7 @@
     std::vector<int32_t> buckets = chargeCycles.cycleBucket;
     int initialSize = buckets.size();
     for (int i = 0; i < 10 - initialSize; i++) {
-        buckets.push_back(-1); // Push -1 for buckets that do not exist.
+        buckets.push_back(0); // Push 0 for buckets that do not exist.
     }
     android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
             buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index a790d0b..ed0d75b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -13,14 +13,16 @@
 
 cc_defaults {
     name: "libsurfaceflinger_defaults",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "surfaceflinger_defaults",
+        "skia_deps",
+    ],
     cflags: [
         "-DLOG_TAG=\"SurfaceFlinger\"",
         "-DGL_GLEXT_PROTOTYPES",
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@2.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
@@ -36,7 +38,6 @@
         "android.hardware.power-cpp",
         "libbase",
         "libbinder",
-        "libbufferhubqueue",
         "libcutils",
         "libEGL",
         "libfmq",
@@ -47,7 +48,6 @@
         "liblayers_proto",
         "liblog",
         "libnativewindow",
-        "libpdx_default_transport",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libstatslog",
@@ -58,21 +58,13 @@
         "libutils",
         "libSurfaceFlingerProp",
     ],
-    // VrComposer is not used when building surfaceflinger for vendors
-    target: {
-        vendor: {
-            exclude_shared_libs: [
-                "android.frameworks.vr.composer@2.0",
-            ],
-        },
-    },
     static_libs: [
         "libcompositionengine",
+        "libframetimeline",
         "libperfetto_client_experimental",
         "librenderengine",
         "libserviceutils",
         "libtrace_proto",
-        "libvrflinger",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -108,11 +100,11 @@
     defaults: ["libsurfaceflinger_defaults"],
     cflags: [
         "-fvisibility=hidden",
-        "-fwhole-program-vtables", // requires ThinLTO
     ],
     lto: {
         thin: true,
     },
+    whole_program_vtables: true, // Requires ThinLTO
     // TODO(b/131771163): Fix broken fuzzer support with LTO.
     sanitize: {
         fuzzer: false,
@@ -145,6 +137,7 @@
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
+        "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
         "FrameTracer/FrameTracer.cpp",
@@ -152,15 +145,14 @@
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
+        "LayerRenderArea.cpp",
         "LayerVector.cpp",
         "MonitoredProducer.cpp",
         "NativeWindowSurface.cpp",
         "RefreshRateOverlay.cpp",
         "RegionSamplingThread.cpp",
         "RenderArea.cpp",
-        "Scheduler/DispSync.cpp",
         "Scheduler/DispSyncSource.cpp",
-        "Scheduler/EventControlThread.cpp",
         "Scheduler/EventThread.cpp",
         "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
@@ -168,15 +160,15 @@
         "Scheduler/LayerInfo.cpp",
         "Scheduler/LayerInfoV2.cpp",
         "Scheduler/MessageQueue.cpp",
-        "Scheduler/PhaseOffsets.cpp",
         "Scheduler/RefreshRateConfigs.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
         "Scheduler/Timer.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
-        "Scheduler/VSyncModulator.cpp",
+        "Scheduler/VsyncModulator.cpp",
         "Scheduler/VSyncReactor.cpp",
+        "Scheduler/VsyncConfiguration.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
@@ -186,35 +178,12 @@
     ],
 }
 
-cc_library_shared {
-    // Please use libsurfaceflinger_defaults to configure how the sources are
-    // built, so the same settings can be used elsewhere.
-    name: "libsurfaceflinger",
-    defaults: ["libsurfaceflinger_production_defaults"],
-    srcs: [
-        ":libsurfaceflinger_sources",
-
-        // Note: SurfaceFlingerFactory is not in the default sources so that it
-        // can be easily replaced.
-        "SurfaceFlingerFactory.cpp",
-    ],
-    cflags: [
-        "-DUSE_VR_COMPOSER=1",
-    ],
-    // VrComposer is not used when building surfaceflinger for vendors
-    target: {
-        vendor: {
-            cflags: [
-                "-DUSE_VR_COMPOSER=0",
-            ],
-        },
-    },
-    logtags: ["EventLog/EventLogTags.logtags"],
-}
-
 cc_defaults {
     name: "libsurfaceflinger_binary",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "surfaceflinger_defaults",
+        "libsurfaceflinger_production_defaults",
+    ],
     cflags: [
         "-DLOG_TAG=\"SurfaceFlinger\"",
     ],
@@ -239,23 +208,31 @@
         "libserviceutils",
         "libtrace_proto",
     ],
-    ldflags: ["-Wl,--export-dynamic"],
 }
 
 filegroup {
     name: "surfaceflinger_binary_sources",
-    srcs: ["main_surfaceflinger.cpp"],
+    srcs: [
+        ":libsurfaceflinger_sources",
+        "main_surfaceflinger.cpp",
+    ],
 }
 
 cc_binary {
     name: "surfaceflinger",
     defaults: ["libsurfaceflinger_binary"],
     init_rc: ["surfaceflinger.rc"],
-    srcs: [":surfaceflinger_binary_sources"],
+    srcs: [
+        ":surfaceflinger_binary_sources",
+        // Note: SurfaceFlingerFactory is not in the filegroup so that it
+        // can be easily replaced.
+        "SurfaceFlingerFactory.cpp",
+    ],
     shared_libs: [
-        "libsurfaceflinger",
         "libSurfaceFlingerProp",
     ],
+
+     logtags: ["EventLog/EventLogTags.logtags"],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f0b0200..fa75ffa 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -204,8 +204,8 @@
     layer.frameNumber = mCurrentFrameNumber;
     layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
 
-    // TODO: we could be more subtle with isFixedSize()
-    const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
+    const bool useFiltering =
+            targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
 
     // Query the texture matrix given our current filtering mode.
     float textureMatrix[16];
@@ -366,7 +366,7 @@
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } else if (!display) {
         // Do nothing.
-    } else if (const auto displayId = display->getId();
+    } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
@@ -393,6 +393,15 @@
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
 
+    // If this is not a valid vsync for the layer's uid, return and try again later
+    const bool isVsyncValidForUid =
+            mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+    if (!isVsyncValidForUid) {
+        ATRACE_NAME("!isVsyncValidForUid");
+        mFlinger->setTransactionFlags(eTraversalNeeded);
+        return false;
+    }
+
     bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
 
     if (refreshRequired) {
@@ -527,10 +536,6 @@
 }
 
 uint32_t BufferLayer::getEffectiveScalingMode() const {
-    if (mOverrideScalingMode >= 0) {
-        return mOverrideScalingMode;
-    }
-
     return mBufferInfo.mScaleMode;
 }
 
@@ -539,20 +544,6 @@
     return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
-bool BufferLayer::latchUnsignaledBuffers() {
-    static bool propertyLoaded = false;
-    static bool latch = false;
-    static std::mutex mutex;
-    std::lock_guard<std::mutex> lock(mutex);
-    if (!propertyLoaded) {
-        char value[PROPERTY_VALUE_MAX] = {};
-        property_get("debug.sf.latch_unsignaled", value, "0");
-        latch = atoi(value);
-        propertyLoaded = true;
-    }
-    return latch;
-}
-
 // h/w composer set-up
 bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
     const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
@@ -840,6 +831,10 @@
     }
 }
 
+bool BufferLayer::bufferNeedsFiltering() const {
+    return isFixedSize();
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 97ffe6f..deaf846 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -37,6 +37,7 @@
 #include "BufferLayerConsumer.h"
 #include "Client.h"
 #include "DisplayHardware/HWComposer.h"
+#include "FrameTimeline.h"
 #include "FrameTracker.h"
 #include "Layer.h"
 #include "LayerVector.h"
@@ -50,10 +51,7 @@
     explicit BufferLayer(const LayerCreationArgs& args);
     virtual ~BufferLayer() override;
 
-    // -----------------------------------------------------------------------
-    // Overriden from Layer
-    // -----------------------------------------------------------------------
-public:
+    // Implements Layer.
     sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
     compositionengine::LayerFECompositionState* editCompositionState() override;
 
@@ -96,8 +94,7 @@
 
     bool hasReadyFrame() const override;
 
-    // Returns the current scaling mode, unless mOverrideScalingMode
-    // is set, in which case, it returns mOverrideScalingMode
+    // Returns the current scaling mode
     uint32_t getEffectiveScalingMode() const override;
 
     // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
@@ -118,35 +115,9 @@
 
     ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
-    // -----------------------------------------------------------------------
-    // Functions that must be implemented by derived classes
-    // -----------------------------------------------------------------------
-private:
-    virtual bool fenceHasSignaled() const = 0;
-    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-
-    PixelFormat getPixelFormat() const;
-
-    // Computes the transform matrix using the setFilteringEnabled to determine whether the
-    // transform matrix should be computed for use with bilinear filtering.
-    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
-
-    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
-
-    virtual bool getAutoRefresh() const = 0;
-    virtual bool getSidebandStreamChanged() const = 0;
-
-    // Latch sideband stream and returns true if the dirty region should be updated.
-    virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
-
-    virtual bool hasFrameUpdate() const = 0;
-
-    virtual status_t bindTextureImage() = 0;
-    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                                    nsecs_t expectedPresentTime) = 0;
-
-    virtual status_t updateActiveBuffer() = 0;
-    virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+    // Returns true if the transformed buffer size does not match the layer size and we need
+    // to apply filtering.
+    virtual bool bufferNeedsFiltering() const;
 
 protected:
     struct BufferInfo {
@@ -182,9 +153,6 @@
     bool onPreComposition(nsecs_t) override;
     void preparePerFrameCompositionState() override;
 
-    // Loads the corresponding system property once per process
-    static bool latchUnsignaledBuffers();
-
     // Check all of the local sync points to ensure that all transactions
     // which need to have been applied prior to the frame which is about to
     // be latched have signaled
@@ -202,7 +170,7 @@
     void updateCloneBufferInfo() override;
     uint64_t mPreviousFrameNumber = 0;
 
-    virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
 
     void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
 
@@ -211,6 +179,29 @@
     ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
 
 private:
+    virtual bool fenceHasSignaled() const = 0;
+    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
+    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
+
+    virtual bool getAutoRefresh() const = 0;
+    virtual bool getSidebandStreamChanged() const = 0;
+
+    // Latch sideband stream and returns true if the dirty region should be updated.
+    virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+
+    virtual bool hasFrameUpdate() const = 0;
+
+    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                    nsecs_t expectedPresentTime) = 0;
+
+    virtual status_t updateActiveBuffer() = 0;
+    virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+
+    // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+    // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+    // detection.
+    bool needsInputInfo() const override { return !mPotentialCursor; }
+
     // Returns true if this layer requires filtering
     bool needsFiltering(const DisplayDevice*) const override;
     bool needsFilteringForScreenshots(const DisplayDevice*,
@@ -220,6 +211,12 @@
     // and its parent layer is not bounded
     Rect getBufferSize(const State& s) const override;
 
+    PixelFormat getPixelFormat() const;
+
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
+
     std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8722952..69d2d11 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -25,7 +25,7 @@
 
 #include "BufferLayerConsumer.h"
 #include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
 
 #include <inttypes.h>
 
@@ -153,25 +153,9 @@
     if (err != NO_ERROR) {
         return err;
     }
-
-    if (!mRE.useNativeFenceSync()) {
-        // Bind the new buffer to the GL texture.
-        //
-        // Older devices require the "implicit" synchronization provided
-        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
-        // devices will either call this in Layer::onDraw, or (if it's not
-        // a GL-composited layer) not at all.
-        err = bindTextureImageLocked();
-    }
-
     return err;
 }
 
-status_t BufferLayerConsumer::bindTextureImage() {
-    Mutex::Autolock lock(mMutex);
-    return bindTextureImageLocked();
-}
-
 void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
     if (!fence->isValid()) {
         return;
@@ -292,17 +276,6 @@
     return err;
 }
 
-status_t BufferLayerConsumer::bindTextureImageLocked() {
-    ATRACE_CALL();
-
-    if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
-        return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
-                                             mCurrentFence);
-    }
-
-    return NO_INIT;
-}
-
 void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
     Mutex::Autolock lock(mMutex);
     memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
@@ -530,6 +503,9 @@
 BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
                                   renderengine::RenderEngine& engine)
       : mGraphicBuffer(graphicBuffer), mRE(engine) {
+    if (graphicBuffer != nullptr && (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED)) {
+        return;
+    }
     mRE.cacheExternalTextureBuffer(mGraphicBuffer);
 }
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 5e3044f..dd39214 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -34,7 +34,6 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class DispSync;
 class Layer;
 class String8;
 
@@ -95,9 +94,6 @@
     status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
                             bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
 
-    // See BufferLayerConsumer::bindTextureImageLocked().
-    status_t bindTextureImage();
-
     // setReleaseFence stores a fence that will signal when the current buffer
     // is no longer being read. This fence will be returned to the producer
     // when the current buffer is released by updateTexImage(). Multiple
@@ -215,10 +211,6 @@
                                     PendingRelease* pendingRelease = nullptr)
             EXCLUDES(mImagesMutex);
 
-    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
-    // If the bind succeeds, this calls doFenceWait.
-    status_t bindTextureImageLocked();
-
 private:
     // Utility class for managing GraphicBuffer references into renderengine
     class Image {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 07be791..71b05fd 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
 #include "TimeStats/TimeStats.h"
 
 namespace android {
+using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
 
@@ -109,7 +110,7 @@
 
     Mutex::Autolock lock(mQueueItemLock);
 
-    const int64_t addedTime = mQueueItems[0].mTimestamp;
+    const int64_t addedTime = mQueueItems[0].item.mTimestamp;
 
     // Ignore timestamps more than a second in the future
     const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
@@ -131,16 +132,12 @@
 // -----------------------------------------------------------------------
 
 bool BufferQueueLayer::fenceHasSignaled() const {
-    if (latchUnsignaledBuffers()) {
-        return true;
-    }
-
     if (!hasFrameUpdate()) {
         return true;
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    if (mQueueItems[0].mIsDroppable) {
+    if (mQueueItems[0].item.mIsDroppable) {
         // Even though this buffer's fence may not have signaled yet, it could
         // be replaced by another buffer before it has a chance to, which means
         // that it's possible to get into a situation where a buffer is never
@@ -148,7 +145,7 @@
         return true;
     }
     const bool fenceSignaled =
-            mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+            mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
     if (!fenceSignaled) {
         mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
                                                     TimeStats::LatchSkipReason::LateAcquire);
@@ -163,12 +160,12 @@
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    return mQueueItems[0].mTimestamp <= expectedPresentTime;
+    return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
 }
 
 uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
-    uint64_t frameNumber = mQueueItems[0].mFrameNumber;
+    uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
 
     // The head of the queue will be dropped if there are signaled and timely frames behind it
     if (isRemovedFromCurrentState()) {
@@ -177,23 +174,23 @@
 
     for (int i = 1; i < mQueueItems.size(); i++) {
         const bool fenceSignaled =
-                mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+                mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
         if (!fenceSignaled) {
             break;
         }
 
         // We don't drop frames without explicit timestamps
-        if (mQueueItems[i].mIsAutoTimestamp) {
+        if (mQueueItems[i].item.mIsAutoTimestamp) {
             break;
         }
 
-        const nsecs_t desiredPresent = mQueueItems[i].mTimestamp;
+        const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
         if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
             desiredPresent > expectedPresentTime) {
             break;
         }
 
-        frameNumber = mQueueItems[i].mFrameNumber;
+        frameNumber = mQueueItems[i].item.mFrameNumber;
     }
 
     return frameNumber;
@@ -208,8 +205,12 @@
 }
 
 bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+    const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
     bool sidebandStreamChanged = true;
-    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
+        updateSidebandStream) {
         // mSidebandStreamChanged was changed to false
         mSidebandStream = mConsumer->getSidebandStream();
         auto* layerCompositionState = editCompositionState();
@@ -229,10 +230,6 @@
     return mQueuedFrames > 0;
 }
 
-status_t BufferQueueLayer::bindTextureImage() {
-    return mConsumer->bindTextureImage();
-}
-
 status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                           nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -242,7 +239,7 @@
     bool queuedBuffer = false;
     const int32_t layerId = getSequence();
     LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                    getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
+                    getProducerStickyTransform() != 0, mName,
                     getTransformToDisplayInverse());
 
     if (isRemovedFromCurrentState()) {
@@ -258,11 +255,11 @@
         Mutex::Autolock lock(mQueueItemLock);
         for (int i = 0; i < mQueueItems.size(); i++) {
             bool fenceSignaled =
-                    mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+                    mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
             if (!fenceSignaled) {
                 break;
             }
-            lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+            lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber;
         }
     }
     const uint64_t maxFrameNumberToAcquire =
@@ -280,9 +277,13 @@
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
+            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+            if (mQueueItems[0].surfaceFrame) {
+                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                          PresentState::Dropped);
+            }
+            mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
         }
         return BAD_VALUE;
@@ -293,6 +294,12 @@
         // early.
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
+            for (auto& [item, surfaceFrame] : mQueueItems) {
+                if (surfaceFrame) {
+                    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                                              PresentState::Dropped);
+                }
+            }
             mQueueItems.clear();
             mQueuedFrames = 0;
             mFlinger->mTimeStats->onDestroy(layerId);
@@ -316,22 +323,29 @@
 
         // Remove any stale buffers that have been dropped during
         // updateTexImage
-        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
+        while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+            if (mQueueItems[0].surfaceFrame) {
+                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                          PresentState::Dropped);
+            }
+            mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
         }
 
-        uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
-        mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
-                                           mQueueItems[0].mFenceTime,
-                                           FrameTracer::FrameEvent::ACQUIRE_FENCE);
+        uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId();
         mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
                                                FrameTracer::FrameEvent::LATCH);
 
-        mQueueItems.removeAt(0);
+        if (mQueueItems[0].surfaceFrame) {
+            mQueueItems[0].surfaceFrame->setAcquireFenceTime(
+                    mQueueItems[0].item.mFenceTime->getSignalTime());
+            mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                      PresentState::Presented);
+        }
+        mQueueItems.erase(mQueueItems.begin());
     }
 
     // Decrement the queued-frames count.  Signal another event if we
@@ -367,6 +381,10 @@
     return NO_ERROR;
 }
 
+void BufferQueueLayer::setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) {
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
+}
+
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayerConsumer::ContentsChangedListener
 // -----------------------------------------------------------------------
@@ -393,8 +411,12 @@
 
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
     const int32_t layerId = getSequence();
-    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
-                                           systemTime(), FrameTracer::FrameEvent::QUEUE);
+    const uint64_t bufferId = item.mGraphicBuffer->getId();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+                                           FrameTracer::FrameEvent::QUEUE);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+                                       std::make_shared<FenceTime>(item.mFence),
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
 
     ATRACE_CALL();
     // Add this buffer from our internal queue tracker
@@ -419,7 +441,12 @@
             }
         }
 
-        mQueueItems.push_back(item);
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+                                                                     mName, mFrameTimelineVsyncId);
+        surfaceFrame->setActualQueueTime(systemTime());
+
+        mQueueItems.push_back({item, std::move(surfaceFrame)});
         mQueuedFrames++;
 
         // Wake up any pending callbacks
@@ -452,7 +479,13 @@
             ALOGE("Can't replace a frame on an empty queue");
             return;
         }
-        mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+                                                                     mName, mFrameTimelineVsyncId);
+        surfaceFrame->setActualQueueTime(systemTime());
+        mQueueItems[mQueueItems.size() - 1].item = item;
+        mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
 
         // Wake up any pending callbacks
         mLastFrameNumberReceived = item.mFrameNumber;
@@ -460,8 +493,12 @@
     }
 
     const int32_t layerId = getSequence();
-    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
-                                           systemTime(), FrameTracer::FrameEvent::QUEUE);
+    const uint64_t bufferId = item.mGraphicBuffer->getId();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+                                           FrameTracer::FrameEvent::QUEUE);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+                                       std::make_shared<FenceTime>(item.mFence),
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
     mConsumer->onBufferAvailable(item);
 }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5ebc22d..fb8a0c2 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -22,6 +22,10 @@
 
 namespace android {
 
+namespace frametimeline {
+class SurfaceFrame;
+}
+
 /*
  * A new BufferQueue and a new BufferLayerConsumer are created when the
  * BufferLayer is first referenced.
@@ -35,10 +39,7 @@
     explicit BufferQueueLayer(const LayerCreationArgs&);
     ~BufferQueueLayer() override;
 
-    // -----------------------------------------------------------------------
-    // Interface implementation for Layer
-    // -----------------------------------------------------------------------
-public:
+    // Implements Layer.
     const char* getType() const override { return "BufferQueueLayer"; }
 
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -54,41 +55,12 @@
 
     bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
 
-    // -----------------------------------------------------------------------
-
-    // -----------------------------------------------------------------------
-    // Interface implementation for BufferLayer
-    // -----------------------------------------------------------------------
-public:
+    // Implements BufferLayer.
     bool fenceHasSignaled() const override;
     bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
-private:
-    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
-
-    bool getAutoRefresh() const override;
-    bool getSidebandStreamChanged() const override;
-
-    bool latchSidebandStream(bool& recomputeVisibleRegions) override;
-    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
-    bool hasFrameUpdate() const override;
-
-    status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                            nsecs_t expectedPresentTime) override;
-
-    status_t updateActiveBuffer() override;
-    status_t updateFrameNumber(nsecs_t latchTime) override;
-
-    sp<Layer> createClone() override;
-
-    void onFrameAvailable(const BufferItem& item);
-    void onFrameReplaced(const BufferItem& item);
-    void onSidebandStreamChanged();
-    void onFrameDequeued(const uint64_t bufferId);
-    void onFrameDetached(const uint64_t bufferId);
-    void onFrameCancelled(const uint64_t bufferId);
+    status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
+    sp<IGraphicBufferProducer> getProducer() const;
 
 protected:
     void gatherBufferInfo() override;
@@ -114,19 +86,39 @@
         BufferQueueLayer* mBufferQueueLayer = nullptr;
         Mutex mMutex;
     };
-    // -----------------------------------------------------------------------
-
-public:
-    status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
-
-    sp<IGraphicBufferProducer> getProducer() const;
 
 private:
-    // Temporary - Used only for LEGACY camera mode.
-    uint32_t getProducerStickyTransform() const;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
+
+    bool getAutoRefresh() const override;
+    bool getSidebandStreamChanged() const override;
+
+    bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+    bool hasFrameUpdate() const override;
+
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
+
+    status_t updateActiveBuffer() override;
+    status_t updateFrameNumber(nsecs_t latchTime) override;
+    void setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) override;
+
+    sp<Layer> createClone() override;
 
     void onFirstRef() override;
 
+    void onFrameAvailable(const BufferItem& item);
+    void onFrameReplaced(const BufferItem& item);
+    void onSidebandStreamChanged();
+    void onFrameDequeued(const uint64_t bufferId);
+    void onFrameDetached(const uint64_t bufferId);
+    void onFrameCancelled(const uint64_t bufferId);
+
+    // Temporary - Used only for LEGACY camera mode.
+    uint32_t getProducerStickyTransform() const;
+
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
@@ -138,7 +130,14 @@
     // Local copy of the queued contents of the incoming BufferQueue
     mutable Mutex mQueueItemLock;
     Condition mQueueItemCondition;
-    Vector<BufferItem> mQueueItems;
+
+    struct BufferData {
+        BufferData(BufferItem item, std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame)
+              : item(item), surfaceFrame(std::move(surfaceFrame)) {}
+        BufferItem item;
+        std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame;
+    };
+    std::vector<BufferData> mQueueItems;
     std::atomic<uint64_t> mLastFrameNumberReceived{0};
 
     bool mAutoRefresh{false};
@@ -148,6 +147,11 @@
     std::atomic<bool> mSidebandStreamChanged{false};
 
     sp<ContentsChangedListener> mContentsChangedListener;
+
+    // The last vsync id received on this layer. This will be used when we get
+    // a buffer to correlate the buffer with the vsync id. Can only be accessed
+    // with the SF state lock held.
+    std::optional<int64_t> mFrameTimelineVsyncId;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 4f8fc41..a64b243 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -33,7 +33,6 @@
 #include <renderengine/Image.h>
 
 #include "EffectLayer.h"
-#include "FrameTracer/FrameTracer.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -49,7 +48,6 @@
 
 BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
       : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
-    mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
 }
 
@@ -98,9 +96,6 @@
 
     // Prevent tracing the same release multiple times.
     if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
-        mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
-                                           std::make_shared<FenceTime>(releaseFence),
-                                           FrameTracer::FrameEvent::RELEASE_FENCE);
         mPreviousReleasedFrameNumber = mPreviousFrameNumber;
     }
 }
@@ -165,6 +160,10 @@
 bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
     mCurrentStateModified = mCurrentState.modified;
     bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+    if (stateUpdateAvailable && mCallbackHandleAcquireTime != -1) {
+        // Update the acquire fence time if we have a buffer
+        mSurfaceFrame->setAcquireFenceTime(mCallbackHandleAcquireTime);
+    }
     mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
@@ -262,12 +261,14 @@
 
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
                                  nsecs_t postTime, nsecs_t desiredPresentTime,
-                                 const client_cache_t& clientCacheId) {
+                                 const client_cache_t& clientCacheId, uint64_t frameNumber) {
+    ATRACE_CALL();
+
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
     }
 
-    mCurrentState.frameNumber++;
+    mCurrentState.frameNumber = frameNumber;
 
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
@@ -276,10 +277,7 @@
 
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
-                                      postTime);
-    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
-                                           postTime, FrameTracer::FrameEvent::POST);
+                                      mOwnerUid, postTime);
     desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
@@ -429,10 +427,6 @@
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
 bool BufferStateLayer::fenceHasSignaled() const {
-    if (latchUnsignaledBuffers()) {
-        return true;
-    }
-
     const bool fenceSignaled =
             getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
     if (!fenceSignaled) {
@@ -519,13 +513,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-status_t BufferStateLayer::bindTextureImage() {
-    const State& s(getDrawingState());
-    auto& engine(mFlinger->getRenderEngine());
-
-    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
-}
-
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
                                           nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
@@ -570,30 +557,9 @@
         handle->frameNumber = mDrawingState.frameNumber;
     }
 
-    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
-        // Bind the new buffer to the GL texture.
-        //
-        // Older devices require the "implicit" synchronization provided
-        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
-        // devices will either call this in Layer::onDraw, or (if it's not
-        // a GL-composited layer) not at all.
-        status_t err = bindTextureImage();
-        if (err != NO_ERROR) {
-            mFlinger->mTimeStats->onDestroy(layerId);
-            mFlinger->mFrameTracer->onDestroy(layerId);
-            return BAD_VALUE;
-        }
-    }
-
-    const uint64_t bufferID = getCurrentBufferId();
     mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
-                                          mBufferInfo.mFenceTime);
-    mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
-                                       mBufferInfo.mFenceTime,
-                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+                                          std::make_shared<FenceTime>(mDrawingState.acquireFence));
     mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
-                                           FrameTracer::FrameEvent::LATCH);
 
     mCurrentStateModified = false;
 
@@ -716,6 +682,10 @@
     mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
 }
 
+uint32_t BufferStateLayer::getEffectiveScalingMode() const {
+   return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+
 Rect BufferStateLayer::computeCrop(const State& s) {
     if (s.crop.isEmpty() && s.buffer) {
         return s.buffer->getBounds();
@@ -774,6 +744,31 @@
                                         static_cast<float>(s.active.transform.ty() + s.active.h)),
                               radius);
 }
+
+bool BufferStateLayer::bufferNeedsFiltering() const {
+    const State& s(getDrawingState());
+    if (!s.buffer) {
+        return false;
+    }
+
+    uint32_t bufferWidth = s.buffer->width;
+    uint32_t bufferHeight = s.buffer->height;
+
+    // Undo any transformations on the buffer and return the result.
+    if (s.transform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+
+    if (s.transformToDisplayInverse) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    const Rect layerSize{getBounds()};
+    return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 00fa7f7..104a13b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -36,9 +36,7 @@
 
     ~BufferStateLayer() override;
 
-    // -----------------------------------------------------------------------
-    // Interface implementation for Layer
-    // -----------------------------------------------------------------------
+    // Implements Layer.
     const char* getType() const override { return "BufferStateLayer"; }
 
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -72,7 +70,8 @@
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
-                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
+                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
+                   uint64_t frameNumber) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -93,7 +92,6 @@
         return false;
     }
     bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
-    bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
     void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -111,12 +109,15 @@
     bool fenceHasSignaled() const override;
     bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
     bool onPreComposition(nsecs_t refreshStartTime) override;
+    uint32_t getEffectiveScalingMode() const override;
 
 protected:
     void gatherBufferInfo() override;
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
 private:
+    friend class SlotGenerationTest;
+
     bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
                                  nsecs_t requestedPresentTime);
 
@@ -129,7 +130,6 @@
 
     bool hasFrameUpdate() const override;
 
-    status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             nsecs_t expectedPresentTime) override;
 
@@ -141,10 +141,10 @@
     // Crop that applies to the buffer
     Rect computeCrop(const State& s);
 
-private:
-    friend class SlotGenerationTest;
     bool willPresentCurrentTransaction() const;
 
+    bool bufferNeedsFiltering() const override;
+
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 78bbcba..aac6c91 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -79,17 +79,18 @@
 status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                uint32_t flags, const sp<IBinder>& parentHandle,
                                LayerMetadata metadata, sp<IBinder>* handle,
-                               sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
+                               sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+                               uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle, nullptr, outTransformHint);
+                                 parentHandle, outLayerId, nullptr, outTransformHint);
 }
 
 status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                          PixelFormat format, uint32_t flags,
                                          const sp<IGraphicBufferProducer>& parent,
                                          LayerMetadata metadata, sp<IBinder>* handle,
-                                         sp<IGraphicBufferProducer>* gbp,
+                                         sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                          uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
@@ -103,11 +104,12 @@
     }
 
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, layer, outTransformHint);
+                                 nullptr, outLayerId, layer, outTransformHint);
 }
 
-status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
-    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+                               int32_t* outLayerId) {
+    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index e9063e5..15cd763 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -28,13 +28,9 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
 class Layer;
 class SurfaceFlinger;
 
-// ---------------------------------------------------------------------------
-
 class Client : public BnSurfaceComposerClient
 {
 public:
@@ -54,17 +50,18 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp,
+                                   sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                    uint32_t* outTransformHint = nullptr);
 
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp,
+                                             sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                              uint32_t* outTransformHint = nullptr);
 
-    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
+    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle,
+                           int32_t* outLayerId);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
@@ -80,7 +77,6 @@
     mutable Mutex mLock;
 };
 
-// ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_SF_CLIENT_H
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index b7d61ce..b0cc030 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -21,8 +21,6 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
 class Colorizer {
     bool mEnabled;
 public:
@@ -59,9 +57,6 @@
     }
 };
 
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
+} // namespace android
 
 #endif /* ANDROID_SURFACE_FLINGER_COLORIZER_H */
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b3b9fe5..57dc60b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -6,7 +6,6 @@
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
@@ -96,8 +95,9 @@
         "tests/MockHWC2.cpp",
         "tests/MockHWComposer.cpp",
         "tests/MockPowerAdvisor.cpp",
-        "tests/OutputTest.cpp",
         "tests/OutputLayerTest.cpp",
+        "tests/OutputTest.cpp",
+        "tests/ProjectionSpaceTest.cpp",
         "tests/RenderSurfaceTest.cpp",
     ],
     static_libs: [
@@ -121,13 +121,6 @@
         //
         // You can either "make dist tests" before flashing, or set this
         // option to false temporarily.
-
-
-        // FIXME: ASAN build is broken for a while, but was not discovered
-        // since new PM silently suppressed ASAN. Temporarily turn off ASAN
-        // to unblock the compiler upgrade process.
-        // address: true,
-        // http://b/139747256
-        address: false,
+        address: true,
     },
 }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index a38d1f3..01dd534 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -34,8 +34,8 @@
  */
 class Display : public virtual Output {
 public:
-    // Gets the HWC DisplayId for the display if there is one
-    virtual const std::optional<DisplayId>& getId() const = 0;
+    // Gets the DisplayId for the display
+    virtual DisplayId getId() const = 0;
 
     // True if the display is secure
     virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6bc677d..95ba9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -20,12 +20,14 @@
 #include <optional>
 #include <string>
 
+#include <ui/DisplayId.h>
 #include <ui/DisplayInfo.h>
 #include <ui/PixelFormat.h>
 #include <ui/Size.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
 
 namespace android::compositionengine {
 
@@ -65,6 +67,9 @@
 
     // Debugging. Human readable name for the display.
     std::string name;
+
+    // Generator for IDs of virtual displays, which are backed by the GPU.
+    DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator;
 };
 
 /**
@@ -95,6 +100,12 @@
         return *this;
     }
 
+    DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator(
+            DisplayIdGenerator<GpuVirtualDisplayId>& generator) {
+        mArgs.gpuVirtualDisplayIdGenerator = &generator;
+        return *this;
+    }
+
     DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
         mArgs.isSecure = isSecure;
         return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 6cc90cb..5c7f12d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -80,10 +80,6 @@
         // The clip region, or visible region that is being rendered to
         const Region& clip;
 
-        // If true, the layer should use an identity transform for its position
-        // transform. Used only by the captureScreen API call.
-        const bool useIdentityTransform;
-
         // If set to true, the layer should enable filtering when rendering.
         const bool needsFiltering;
 
@@ -148,7 +144,6 @@
 static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
                               const LayerFE::ClientCompositionTargetSettings& rhs) {
     return lhs.clip.hasSameRects(rhs.clip) &&
-            lhs.useIdentityTransform == rhs.useIdentityTransform &&
             lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
             lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
             lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
@@ -170,7 +165,6 @@
     *os << "ClientCompositionTargetSettings{";
     *os << "\n    .clip = \n";
     PrintTo(settings.clip, os);
-    *os << "\n    .useIdentityTransform = " << settings.useIdentityTransform;
     *os << "\n    .needsFiltering = " << settings.needsFiltering;
     *os << "\n    .isSecure = " << settings.isSecure;
     *os << "\n    .supportsProtectedContent = " << settings.supportsProtectedContent;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a9763b..5a3b9ac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -20,6 +20,7 @@
 
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
+#include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -116,7 +117,10 @@
     FloatRect geomLayerBounds;
 
     // length of the shadow in screen space
-    float shadowRadius;
+    float shadowRadius{0.f};
+
+    // List of regions that require blur
+    std::vector<BlurRegion> blurRegions;
 
     /*
      * Geometry state
@@ -130,16 +134,6 @@
     Rect geomContentCrop;
     Rect geomCrop;
 
-    /*
-     * Extra metadata
-     */
-
-    // The type for this layer
-    int type{0};
-
-    // The appId for this layer
-    int appId{0};
-
     GenericLayerMetadataMap metadata;
 
     /*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index baf5258..3be1cc4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,11 +163,15 @@
     virtual void setCompositionEnabled(bool) = 0;
 
     // Sets the projection state to use
-    virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
-                               const Rect& viewport, const Rect& sourceClip,
-                               const Rect& destinationClip, bool needsFiltering) = 0;
+    virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                               const Rect& orientedDisplaySpaceRect) = 0;
     // Sets the bounds to use
-    virtual void setBounds(const ui::Size&) = 0;
+    virtual void setDisplaySize(const ui::Size&) = 0;
+    // Gets the transform hint used in layers that belong to this output. Used to guide
+    // composition orientation so that HW overlay can be used when display isn't in its natural
+    // orientation on some devices. Therefore usually we only use transform hint from display
+    // output.
+    virtual ui::Transform::RotationFlags getTransformHint() const = 0;
 
     // Sets the layer stack filtering settings for this output. See
     // belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
new file mode 100644
index 0000000..7ca91d8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <android-base/stringprintf.h>
+#include <ui/Rect.h>
+#include <ui/Rotation.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace compositionengine {
+
+// Geometrical space to which content is projected.
+// For example, this can be the layer space or the physical display space.
+struct ProjectionSpace {
+    ProjectionSpace() = default;
+    ProjectionSpace(ui::Size size, Rect content)
+          : bounds(std::move(size)), content(std::move(content)) {}
+
+    // Bounds of this space. Always starts at (0,0).
+    Rect bounds;
+
+    // Rect onto which content is projected.
+    Rect content;
+
+    // The orientation of this space. This value is meaningful only in relation to the rotation
+    // of another projection space and it's used to determine the rotating transformation when
+    // mapping between the two.
+    // As a convention when using this struct orientation = 0 for the "oriented*" projection
+    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+    // of the display space will become 90, while  the orientation of the layer stack space will
+    // remain the same.
+    ui::Rotation orientation = ui::ROTATION_0;
+
+    // Returns a transform which maps this.content into destination.content
+    // and also rotates according to this.orientation and destination.orientation
+    ui::Transform getTransform(const ProjectionSpace& destination) const {
+        ui::Rotation rotation = destination.orientation - orientation;
+
+        // Compute a transformation which rotates the destination in a way it has the same
+        // orientation as us.
+        const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
+        ui::Transform inverseRotatingTransform;
+        inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
+                                     destination.bounds.height());
+        // The destination content rotated so it has the same orientation as us.
+        Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+
+        // Compute translation from the source content to (0, 0).
+        const float sourceX = content.left;
+        const float sourceY = content.top;
+        ui::Transform sourceTranslation;
+        sourceTranslation.set(-sourceX, -sourceY);
+
+        // Compute scaling transform which maps source content to destination content, assuming
+        // they are both at (0, 0).
+        ui::Transform scale;
+        const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
+        const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+        scale.set(scaleX, 0, 0, scaleY);
+
+        // Compute translation from (0, 0) to the orientated destination content.
+        const float destX = orientedDestContent.left;
+        const float destY = orientedDestContent.top;
+        ui::Transform destTranslation;
+        destTranslation.set(destX, destY);
+
+        // Compute rotation transform.
+        const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
+        auto orientedDestWidth = destination.bounds.width();
+        auto orientedDestHeight = destination.bounds.height();
+        if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+            std::swap(orientedDestWidth, orientedDestHeight);
+        }
+        ui::Transform rotationTransform;
+        rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight);
+
+        // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
+        // Apply the logical translation, scale to physical size, apply the
+        // physical translation and finally rotate to the physical orientation.
+        return rotationTransform * destTranslation * scale * sourceTranslation;
+    }
+};
+
+} // namespace compositionengine
+
+inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
+    return android::base::
+            StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
+                         to_string(space.bounds).c_str(), to_string(space.content).c_str(),
+                         toCString(space.orientation));
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) {
+    *os << to_string(space);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 7a4f738..54e91ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -57,7 +57,7 @@
     void finishFrame(const CompositionRefreshArgs&) override;
 
     // compositionengine::Display overrides
-    const std::optional<DisplayId>& getId() const override;
+    DisplayId getId() const override;
     bool isSecure() const override;
     bool isVirtual() const override;
     void disconnect() override;
@@ -85,12 +85,14 @@
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
     // Testing
-    void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+    void setDisplayIdForTesting(DisplayId displayId);
 
 private:
     bool mIsVirtual = false;
-    std::optional<DisplayId> mId;
+    bool mIsDisconnected = false;
+    DisplayId mId;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+    DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6f25e63..651230c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,11 +38,11 @@
     bool isValid() const override;
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
-    void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
-                       const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
-                       bool needsFiltering) override;
-    void setBounds(const ui::Size&) override;
+    void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                       const Rect& orientedDisplaySpaceRect) override;
+    void setDisplaySize(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+    ui::Transform::RotationFlags getTransformHint() const override;
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66ed2b6..06e6a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -29,6 +29,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
+#include <compositionengine/ProjectionSpace.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -38,7 +39,7 @@
 namespace compositionengine::impl {
 
 struct OutputCompositionState {
-    // If false, composition will not per performed for this display
+    // If false, composition will not be performed for this display
     bool isEnabled{false};
 
     // If false, this output is not considered secure
@@ -50,8 +51,7 @@
     // If true, the current frame on this output uses device composition
     bool usesDeviceComposition{false};
 
-    // If true, the client target should be flipped when performing client
-    // composition
+    // If true, the client target should be flipped when performing client composition
     bool flipClientTarget{false};
 
     // If true, the current frame reused the buffer from a previous client composition
@@ -63,28 +63,26 @@
     // The layer stack to display on this display
     uint32_t layerStackId{~0u};
 
-    // The physical space screen bounds
-    Rect bounds;
+    // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
+    // which gets projected on the display. The orientation of this space is always ROTATION_0.
+    ProjectionSpace layerStackSpace;
 
-    // The logical to physical transformation to use
+    // Oriented physical display space. It will have the same size as displaySpace oriented to
+    // match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
+    ProjectionSpace orientedDisplaySpace;
+
+    // The space of the framebuffer. Its bounds match the size of the framebuffer and its
+    // orientation matches the orientation of the display. Typically the framebuffer space will
+    // be identical to the physical display space.
+    ProjectionSpace framebufferSpace;
+
+    // The space of the physical display. It is as big as the currently active display mode. The
+    // content in this space can be rotated.
+    ProjectionSpace displaySpace;
+
+    // Transformation from layerStackSpace to displaySpace
     ui::Transform transform;
 
-    // The physical orientation of the display, expressed as ui::Transform
-    // orientation flags.
-    uint32_t orientation{0};
-
-    // The logical space user visible bounds
-    Rect frame;
-
-    // The logical space user viewport rectangle
-    Rect viewport;
-
-    // The physical space source clip rectangle
-    Rect sourceClip;
-
-    // The physical space destination clip rectangle
-    Rect destinationClip;
-
     // If true, RenderEngine filtering should be enabled
     bool needsFiltering{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 75394fa..d2b38d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -110,7 +110,7 @@
     void dump(std::string& result) const;
 
     // Timestamp for when the layer is queued for client composition
-    nsecs_t clientCompositionTimestamp;
+    nsecs_t clientCompositionTimestamp{0};
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 3a4c70f..08a8b84 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,7 +32,7 @@
     Display();
     virtual ~Display();
 
-    MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+    MOCK_CONST_METHOD0(getId, DisplayId());
     MOCK_CONST_METHOD0(isSecure, bool());
     MOCK_CONST_METHOD0(isVirtual, bool());
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4661c5d..95db4da 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,11 +36,10 @@
     MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
-    MOCK_METHOD7(setProjection,
-                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
-                      const Rect&, bool));
-    MOCK_METHOD1(setBounds, void(const ui::Size&));
+    MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
+    MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+    MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
 
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d201104..0b0b8d5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,18 +51,26 @@
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mIsVirtual = !args.physical;
-    mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
     mPowerAdvisor = args.powerAdvisor;
-
     editState().isSecure = args.isSecure;
-
+    editState().displaySpace.bounds = Rect(args.pixels);
     setLayerStackFilter(args.layerStackId,
-                        args.physical ? args.physical->type == DisplayConnectionType::Internal
-                                      : false);
+                        args.physical && args.physical->type == DisplayConnectionType::Internal);
     setName(args.name);
+    mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator;
 
-    if (!args.physical && args.useHwcVirtualDisplays) {
-        mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+    if (args.physical) {
+        mId = args.physical->id;
+    } else {
+        std::optional<DisplayId> id;
+        if (args.useHwcVirtualDisplays) {
+            id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+        }
+        if (!id) {
+            id = mGpuVirtualDisplayIdGenerator->nextId();
+        }
+        LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID");
+        mId = *id;
     }
 }
 
@@ -77,7 +85,7 @@
     return Output::isValid() && mPowerAdvisor;
 }
 
-const std::optional<DisplayId>& Display::getId() const {
+DisplayId Display::getId() const {
     return mId;
 }
 
@@ -93,31 +101,36 @@
     return mId;
 }
 
-void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+void Display::setDisplayIdForTesting(DisplayId displayId) {
     mId = displayId;
 }
 
 void Display::disconnect() {
-    if (!mId) {
+    if (mIsDisconnected) {
         return;
     }
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.disconnectDisplay(*mId);
-    mId.reset();
+    mIsDisconnected = true;
+    if (const auto id = GpuVirtualDisplayId::tryCast(mId)) {
+        mGpuVirtualDisplayIdGenerator->markUnused(*id);
+        return;
+    }
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    LOG_FATAL_IF(!halDisplayId);
+    getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId);
 }
 
 void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
     Output::setColorTransform(args);
-
-    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
         return;
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
+    status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
-             mId ? to_string(*mId).c_str() : "", result);
+             to_string(mId).c_str(), result);
 }
 
 void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -139,8 +152,10 @@
 
     Output::setColorProfile(colorProfile);
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
+    const auto physicalId = PhysicalDisplayId::tryCast(mId);
+    LOG_FATAL_IF(!physicalId);
+    getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
+                                                              colorProfile.renderIntent);
 }
 
 void Display::dump(std::string& out) const {
@@ -149,14 +164,8 @@
     StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
 
     out.append("\n   ");
-
     dumpVal(out, "isVirtual", mIsVirtual);
-    if (mId) {
-        dumpVal(out, "hwcId", to_string(*mId));
-    } else {
-        StringAppendF(&out, "no hwcId, ");
-    }
-
+    dumpVal(out, "DisplayId", to_string(mId));
     out.append("\n");
 
     Output::dumpBase(out);
@@ -177,31 +186,33 @@
 
 std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
         const sp<compositionengine::LayerFE>& layerFE) const {
-    auto result = impl::createOutputLayer(*this, layerFE);
+    auto outputLayer = impl::createOutputLayer(*this, layerFE);
 
-    if (result && mId) {
+    if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+        outputLayer && !mIsDisconnected && halDisplayId) {
         auto& hwc = getCompositionEngine().getHwComposer();
-        auto displayId = *mId;
         // Note: For the moment we ensure it is safe to take a reference to the
         // HWComposer implementation by destroying all the OutputLayers (and
         // hence the HWC2::Layers they own) before setting a new HWComposer. See
         // for example SurfaceFlinger::updateVrFlinger().
         // TODO(b/121291683): Make this safer.
-        auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
-                                                     [&hwc, displayId](HWC2::Layer* layer) {
-                                                         hwc.destroyLayer(displayId, layer);
-                                                     });
+        auto hwcLayer =
+                std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
+                                             [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
+                                                 hwc.destroyLayer(id, layer);
+                                             });
         ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
                  getName().c_str());
-        result->setHwcLayer(std::move(hwcLayer));
+        outputLayer->setHwcLayer(std::move(hwcLayer));
     }
-    return result;
+    return outputLayer;
 }
 
 void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     Output::setReleasedLayers(refreshArgs);
 
-    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+    if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
+        refreshArgs.layersWithQueuedFrames.empty()) {
         return;
     }
 
@@ -237,19 +248,25 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
+    if (mIsDisconnected) {
+        return;
+    }
+
     // Default to the base settings -- client composition only.
     Output::chooseCompositionStrategy();
 
-    // If we don't have a HWC display, then we are done
-    if (!mId) {
+    // If we don't have a HWC display, then we are done.
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (!halDisplayId) {
         return;
     }
 
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
-    if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
-                                                          &changes);
+    if (status_t result =
+                hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+                                                &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -270,8 +287,12 @@
 
 bool Display::getSkipColorTransform() const {
     const auto& hwc = getCompositionEngine().getHwComposer();
-    return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)
-               : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
+    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+        return hwc.hasDisplayCapability(*halDisplayId,
+                                        hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+    }
+
+    return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
 }
 
 bool Display::anyLayersRequireClientComposition() const {
@@ -338,16 +359,17 @@
 }
 
 compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
-    auto result = impl::Output::presentAndGetFrameFences();
+    auto fences = impl::Output::presentAndGetFrameFences();
 
-    if (!mId) {
-        return result;
+    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    if (mIsDisconnected || !halDisplayIdOpt) {
+        return fences;
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.presentAndGetReleaseFences(*mId);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
 
-    result.presentFence = hwc.getPresentFence(*mId);
+    fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
     // TODO(b/121291683): Change HWComposer call to return entire map
     for (const auto* layer : getOutputLayersOrderedByZ()) {
@@ -356,19 +378,19 @@
             continue;
         }
 
-        result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+        fences.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*halDisplayIdOpt, hwcLayer));
     }
 
-    hwc.clearReleaseFences(*mId);
+    hwc.clearReleaseFences(*halDisplayIdOpt);
 
-    return result;
+    return fences;
 }
 
 void Display::setExpensiveRenderingExpected(bool enabled) {
     Output::setExpensiveRenderingExpected(enabled);
 
-    if (mPowerAdvisor && mId) {
-        mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+    if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
+        mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
     }
 }
 
@@ -377,11 +399,10 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (!mId) {
-        if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
-            ALOGV("Skipping display composition");
-            return;
-        }
+    if (GpuVirtualDisplayId::tryCast(mId) &&
+        getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+        ALOGV("Skipping display composition");
+        return;
     }
 
     impl::Output::finishFrame(refreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 9598430..9d1bb02 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -77,6 +77,7 @@
 
 void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
     transform.dump(out, name);
+    out.append(" ");
 }
 
 void dumpVal(std::string& out, const char* name, const ui::Size& size) {
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 02e3a45..1338538 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -75,10 +75,6 @@
     dumpVal(out, "alpha", alpha);
     dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
 
-    out.append("\n      ");
-    dumpVal(out, "type", type);
-    dumpVal(out, "appId", appId);
-
     if (!metadata.empty()) {
         out.append("\n      metadata {");
         for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b07c904..3852f45 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -69,6 +69,19 @@
     return Reversed<T>(c);
 }
 
+struct ScaleVector {
+    float x;
+    float y;
+};
+
+// Returns a ScaleVector (x, y) such that from.scale(x, y) = to',
+// where to' will have the same size as "to". In the case where "from" and "to"
+// start at the origin to'=to.
+ScaleVector getScale(const Rect& from, const Rect& to) {
+    return {.x = static_cast<float>(to.width()) / from.width(),
+            .y = static_cast<float>(to.height()) / from.height()};
+}
+
 } // namespace
 
 std::shared_ptr<Output> createOutput(
@@ -105,28 +118,82 @@
     dirtyEntireOutput();
 }
 
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
-                           const Rect& viewport, const Rect& sourceClip,
-                           const Rect& destinationClip, bool needsFiltering) {
+void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                           const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
-    outputState.transform = transform;
-    outputState.orientation = orientation;
-    outputState.sourceClip = sourceClip;
-    outputState.destinationClip = destinationClip;
-    outputState.frame = frame;
-    outputState.viewport = viewport;
-    outputState.needsFiltering = needsFiltering;
+
+    outputState.displaySpace.orientation = orientation;
+    LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+                 "The display bounds are unknown.");
+
+    // Compute orientedDisplaySpace
+    ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+        std::swap(orientedSize.width, orientedSize.height);
+    }
+    outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+    // Compute displaySpace.content
+    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
+    ui::Transform rotation;
+    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
+        const auto displaySize = outputState.displaySpace.bounds;
+        rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
+    }
+    outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+
+    // Compute framebufferSpace
+    outputState.framebufferSpace.orientation = orientation;
+    LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+                 "The framebuffer bounds are unknown.");
+    const auto scale =
+            getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
+    outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+
+    // Compute layerStackSpace
+    outputState.layerStackSpace.content = layerStackSpaceRect;
+    outputState.layerStackSpace.bounds = layerStackSpaceRect;
+
+    outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
+    outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
+    dirtyEntireOutput();
+}
+
+void Output::setDisplaySize(const ui::Size& size) {
+    mRenderSurface->setDisplaySize(size);
+
+    auto& state = editState();
+
+    // Update framebuffer space
+    const Rect newBounds(size);
+    ScaleVector scale;
+    scale = getScale(state.framebufferSpace.bounds, newBounds);
+    state.framebufferSpace.bounds = newBounds;
+    state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
+
+    // Update display space
+    scale = getScale(state.displaySpace.bounds, newBounds);
+    state.displaySpace.bounds = newBounds;
+    state.displaySpace.content.scaleSelf(scale.x, scale.y);
+    state.transform = state.layerStackSpace.getTransform(state.displaySpace);
+
+    // Update oriented display space
+    const auto orientation = state.displaySpace.orientation;
+    ui::Size orientedSize = size;
+    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+        std::swap(orientedSize.width, orientedSize.height);
+    }
+    const Rect newOrientedBounds(orientedSize);
+    scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
+    state.orientedDisplaySpace.bounds = newOrientedBounds;
+    state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
 
     dirtyEntireOutput();
 }
 
-// TODO(b/121291683): Rename setSize() once more is moved.
-void Output::setBounds(const ui::Size& size) {
-    mRenderSurface->setDisplaySize(size);
-    // TODO(b/121291683): Rename outputState.size once more is moved.
-    editState().bounds = Rect(mRenderSurface->getSize());
-
-    dirtyEntireOutput();
+ui::Transform::RotationFlags Output::getTransformHint() const {
+    return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
 }
 
 void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
@@ -232,8 +299,7 @@
 
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
-    editState().bounds = Rect(mRenderSurface->getSize());
-
+    editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
     dirtyEntireOutput();
 }
 
@@ -251,7 +317,7 @@
 
 Region Output::getDirtyRegion(bool repaintEverything) const {
     const auto& outputState = getState();
-    Region dirty(outputState.viewport);
+    Region dirty(outputState.layerStackSpace.content);
     if (!repaintEverything) {
         dirty.andSelf(outputState.dirtyRegion);
     }
@@ -336,7 +402,7 @@
 
     // Compute the resulting coverage for this output, and store it for later
     const ui::Transform& tr = outputState.transform;
-    Region undefinedRegion{outputState.bounds};
+    Region undefinedRegion{outputState.displaySpace.bounds};
     undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
 
     outputState.undefinedRegion = undefinedRegion;
@@ -539,7 +605,7 @@
     // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
     const auto& outputState = getState();
     Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
-    drawRegion.andSelf(outputState.bounds);
+    drawRegion.andSelf(outputState.displaySpace.bounds);
     if (drawRegion.isEmpty()) {
         return;
     }
@@ -556,8 +622,8 @@
     outputLayerState.visibleRegion = visibleRegion;
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
-    outputLayerState.outputSpaceVisibleRegion =
-            outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+    outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
+            visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
     outputLayerState.shadowRegion = shadowRegion;
 }
 
@@ -603,7 +669,8 @@
 compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
     compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
     for (auto* layer : getOutputLayersOrderedByZ()) {
-        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0 ||
+            layer->getLayerFE().getCompositionState()->blurRegions.size() > 0) {
             layerRequestingBgComposition = layer;
         }
     }
@@ -835,6 +902,8 @@
             needsProtected == renderEngine.isProtected()) {
             mRenderSurface->setProtected(needsProtected);
         }
+    } else if (!outputState.isSecure && renderEngine.isProtected()) {
+        renderEngine.useProtectedContext(false);
     }
 
     base::unique_fd fd;
@@ -862,9 +931,10 @@
     ALOGV("hasClientComposition");
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
-    clientCompositionDisplay.clip = outputState.sourceClip;
-    clientCompositionDisplay.orientation = outputState.orientation;
+    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
+    clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+    clientCompositionDisplay.orientation =
+            ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
@@ -947,11 +1017,10 @@
     ALOGV("Rendering client layers");
 
     const auto& outputState = getState();
-    const Region viewportRegion(outputState.viewport);
-    const bool useIdentityTransform = false;
+    const Region viewportRegion(outputState.layerStackSpace.content);
     bool firstLayer = true;
     // Used when a layer clears part of the buffer.
-    Region dummyRegion;
+    Region stubRegion;
 
     for (auto* layer : getOutputLayersOrderedByZ()) {
         const auto& layerState = layer->getState();
@@ -984,18 +1053,17 @@
                 !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
 
         if (clientComposition || clearClientComposition) {
-            compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
-                    clip,
-                    useIdentityTransform,
-                    layer->needsFiltering() || outputState.needsFiltering,
-                    outputState.isSecure,
-                    supportsProtectedContent,
-                    clientComposition ? clearRegion : dummyRegion,
-                    outputState.viewport,
-                    outputDataspace,
-                    realContentIsVisible,
-                    !clientComposition, /* clearContent  */
-            };
+            compositionengine::LayerFE::ClientCompositionTargetSettings
+                    targetSettings{.clip = clip,
+                                   .needsFiltering =
+                                           layer->needsFiltering() || outputState.needsFiltering,
+                                   .isSecure = outputState.isSecure,
+                                   .supportsProtectedContent = supportsProtectedContent,
+                                   .clearRegion = clientComposition ? clearRegion : stubRegion,
+                                   .viewport = outputState.layerStackSpace.content,
+                                   .dataspace = outputDataspace,
+                                   .realContentIsVisible = realContentIsVisible,
+                                   .clearContent = !clientComposition};
             std::vector<LayerFE::LayerSettings> results =
                     layerFE.prepareClientCompositionList(targetSettings);
             if (realContentIsVisible && !results.empty()) {
@@ -1092,7 +1160,7 @@
 
 void Output::dirtyEntireOutput() {
     auto& outputState = editState();
-    outputState.dirtyRegion.set(outputState.bounds);
+    outputState.dirtyRegion.set(outputState.displaySpace.bounds);
 }
 
 void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 4835aef..ee30ad8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -37,12 +37,14 @@
     dumpVal(out, "transform", transform);
 
     out.append("\n   ");
-
-    dumpVal(out, "bounds", bounds);
-    dumpVal(out, "frame", frame);
-    dumpVal(out, "viewport", viewport);
-    dumpVal(out, "sourceClip", sourceClip);
-    dumpVal(out, "destinationClip", destinationClip);
+    dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
+    out.append("\n   ");
+    dumpVal(out, "framebufferSpace", to_string(framebufferSpace));
+    out.append("\n   ");
+    dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
+    out.append("\n   ");
+    dumpVal(out, "displaySpace", to_string(displaySpace));
+    out.append("\n   ");
     dumpVal(out, "needsFiltering", needsFiltering);
 
     out.append("\n   ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1faf775..0faab6f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -77,7 +77,7 @@
     FloatRect activeCropFloat =
             reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
-    const Rect& viewport = getOutput().getState().viewport;
+    const Rect& viewport = getOutput().getState().layerStackSpace.content;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     // Transform to screen space.
@@ -133,7 +133,8 @@
          * the code below applies the primary display's inverse transform to the
          * buffer
          */
-        uint32_t invTransformOrient = outputState.orientation;
+        uint32_t invTransformOrient =
+                ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
         // calculate the inverse transform
         if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
             invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -189,7 +190,7 @@
     Rect activeCrop = layerState.geomCrop;
     if (!activeCrop.isEmpty() && bufferSize.isValid()) {
         activeCrop = layerTransform.transform(activeCrop);
-        if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+        if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
             activeCrop.clear();
         }
         activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -215,7 +216,7 @@
     // transformation. We then round upon constructing 'frame'.
     Rect frame{
             layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
-    if (!frame.intersect(outputState.viewport, &frame)) {
+    if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
         frame.clear();
     }
     const ui::Transform displayTransform{outputState.transform};
@@ -402,13 +403,6 @@
               outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
-                                       static_cast<uint32_t>(outputIndependentState.appId));
-        error != hal::Error::NONE) {
-        ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
     for (const auto& [name, entry] : outputIndependentState.metadata) {
         if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
             error != hal::Error::NONE) {
@@ -568,7 +562,7 @@
     const auto& outputState = getOutput().getState();
 
     Rect frame = layerFEState->cursorFrame;
-    frame.intersect(outputState.viewport, &frame);
+    frame.intersect(outputState.layerStackSpace.content, &frame);
     Rect position = outputState.transform.transform(frame);
 
     if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 2773fd3..b47f7fd 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -48,6 +48,8 @@
 
 namespace impl {
 
+constexpr auto DEFAULT_USAGE = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
 std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
         const compositionengine::CompositionEngine& compositionEngine,
         compositionengine::Display& display,
@@ -81,7 +83,7 @@
     ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
     status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
     ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
-    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+    status = native_window_set_usage(window, DEFAULT_USAGE);
     ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
 }
 
@@ -109,7 +111,7 @@
 }
 
 void RenderSurface::setProtected(bool useProtected) {
-    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+    uint64_t usageFlags = DEFAULT_USAGE;
     if (useProtected) {
         usageFlags |= GRALLOC_USAGE_PROTECTED;
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index d889d74..d5bf569 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -30,6 +30,7 @@
 namespace {
 
 using ::testing::_;
+using ::testing::DoAll;
 using ::testing::InSequence;
 using ::testing::Ref;
 using ::testing::Return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 09f37fb..1befbf8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -56,8 +56,9 @@
 using testing::SetArgPointee;
 using testing::StrictMock;
 
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
-constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42};
+// TODO(b/160679868) Use VirtualDisplayId
+constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43};
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
 constexpr int32_t DEFAULT_LAYER_STACK = 123;
@@ -159,6 +160,7 @@
         EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     }
 
     DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
@@ -175,6 +177,7 @@
     DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
         return DisplayCreationArgsBuilder()
                 .setUseHwcVirtualDisplays(false)
+                .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                 .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
                 .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                 .setIsSecure(false)
@@ -188,6 +191,7 @@
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator;
 };
 
 struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -244,7 +248,7 @@
                                        getDisplayCreationArgsForNonHWCVirtualDisplay());
     EXPECT_FALSE(display->isSecure());
     EXPECT_TRUE(display->isVirtual());
-    EXPECT_EQ(std::nullopt, display->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
 }
 
 /*
@@ -331,6 +335,7 @@
     mDisplay->setConfiguration(
             DisplayCreationArgsBuilder()
                     .setUseHwcVirtualDisplays(true)
+                    .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                     .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
                     .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                     .setIsSecure(false)
@@ -339,7 +344,7 @@
                     .setName(getDisplayNameFromCurrentTest())
                     .build());
 
-    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
     EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -351,6 +356,7 @@
     mDisplay->setConfiguration(
             DisplayCreationArgsBuilder()
                     .setUseHwcVirtualDisplays(false)
+                    .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                     .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
                     .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                     .setIsSecure(false)
@@ -359,7 +365,7 @@
                     .setName(getDisplayNameFromCurrentTest())
                     .build());
 
-    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
     EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -374,16 +380,13 @@
 using DisplayDisconnectTest = PartialMockDisplayTestCommon;
 
 TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
-    // The first call to disconnect will disconnect the display with the HWC and
-    // set mHwcId to -1.
-    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+    // The first call to disconnect will disconnect the display with the HWC.
+    EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
     mDisplay->disconnect();
-    EXPECT_FALSE(mDisplay->getId());
 
     // Subsequent calls will do nothing,
-    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+    EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(0);
     mDisplay->disconnect();
-    EXPECT_FALSE(mDisplay->getId());
 }
 
 /*
@@ -401,7 +404,8 @@
     // Identity matrix sets an identity state value
     const mat4 kIdentity;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kIdentity))
+            .Times(1);
 
     refreshArgs.colorTransformMatrix = kIdentity;
     mDisplay->setColorTransform(refreshArgs);
@@ -409,7 +413,8 @@
     // Non-identity matrix sets a non-identity state value
     const mat4 kNonIdentity = mat4() * 2;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kNonIdentity))
+            .Times(1);
 
     refreshArgs.colorTransformMatrix = kNonIdentity;
     mDisplay->setColorTransform(refreshArgs);
@@ -526,13 +531,14 @@
     sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
     StrictMock<HWC2::mock::Layer> hwcLayer;
 
-    EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+    EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
+            .WillOnce(Return(&hwcLayer));
 
     auto outputLayer = mDisplay->createOutputLayer(layerFE);
 
     EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
 
-    EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+    EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
     outputLayer.reset();
 }
 
@@ -605,7 +611,7 @@
     auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
     std::shared_ptr<Display> nonHwcDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine, args);
-    EXPECT_FALSE(nonHwcDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId()));
 
     nonHwcDisplay->chooseCompositionStrategy();
 
@@ -616,7 +622,8 @@
 
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -638,7 +645,7 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -668,7 +675,7 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
             .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
@@ -698,7 +705,7 @@
 
 TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
     EXPECT_CALL(mHwComposer,
-                hasDisplayCapability(DEFAULT_DISPLAY_ID,
+                hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
                                      hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
             .WillOnce(Return(true));
     EXPECT_TRUE(mDisplay->getSkipColorTransform());
@@ -856,13 +863,16 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
-    EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
-    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+    EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
+            .WillOnce(Return(presentFence));
+    EXPECT_CALL(mHwComposer,
+                getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer1.hwc2Layer))
             .WillOnce(Return(layer1Fence));
-    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+    EXPECT_CALL(mHwComposer,
+                getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer2.hwc2Layer))
             .WillOnce(Return(layer2Fence));
-    EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+    EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
 
     auto result = mDisplay->presentAndGetFrameFences();
 
@@ -907,7 +917,7 @@
 
     mDisplay->editState().isEnabled = true;
     mDisplay->editState().usesClientComposition = false;
-    mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
     CompositionRefreshArgs refreshArgs;
@@ -928,7 +938,7 @@
 
     nonHwcDisplay->editState().isEnabled = true;
     nonHwcDisplay->editState().usesClientComposition = false;
-    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
     CompositionRefreshArgs refreshArgs;
@@ -949,7 +959,7 @@
 
     nonHwcDisplay->editState().isEnabled = true;
     nonHwcDisplay->editState().usesClientComposition = false;
-    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
 
     CompositionRefreshArgs refreshArgs;
@@ -970,7 +980,7 @@
 
     nonHwcDisplay->editState().isEnabled = true;
     nonHwcDisplay->editState().usesClientComposition = false;
-    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
     CompositionRefreshArgs refreshArgs;
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index d21b97e..87911cc 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -66,7 +66,6 @@
     MOCK_METHOD1(setTransform, Error(hal::Transform));
     MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
     MOCK_METHOD1(setZOrder, Error(uint32_t));
-    MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
 
     MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
     MOCK_METHOD3(setLayerGenericMetadata,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 75a4fec..84c027b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -42,63 +42,69 @@
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability));
+    MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
 
     MOCK_METHOD3(allocateVirtualDisplay,
                  std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
-    MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId));
-    MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
-    MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+    MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+    MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
+    MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
     MOCK_METHOD3(getDeviceCompositionChanges,
-                 status_t(DisplayId, bool,
+                 status_t(HalDisplayId, bool,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
-                 status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+                 status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
-    MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode));
-    MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
-    MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
-    MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+    MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+    MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
+    MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
+    MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
+    MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
-    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
-    MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
-    MOCK_METHOD1(clearReleaseFences, void(DisplayId));
-    MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
-    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
-    MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
-    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
+    MOCK_METHOD3(setOutputBuffer,
+                 status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+    MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
+    MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
+    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
+    MOCK_CONST_METHOD2(getRenderIntents,
+                       std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
     MOCK_METHOD4(getDisplayedContentSamplingAttributes,
-                 status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
-    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+                 status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
-                 status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float));
-    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
+                 status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
 
     MOCK_METHOD2(onHotplug,
                  std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
+    MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
     MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
-    MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync));
-    MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
-    MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
-    MOCK_CONST_METHOD1(getConfigs,
-                       std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
-    MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
-    MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
-    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
-    MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+    MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
+    MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(
+            getConfigs,
+            std::vector<std::shared_ptr<const HWC2::Display::Config>>(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getActiveConfig,
+                       std::shared_ptr<const HWC2::Display::Config>(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, int(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
+    MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
     MOCK_CONST_METHOD0(isUsingVrComposer, bool());
-    MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(DisplayId));
-    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
-    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+    MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId));
     MOCK_METHOD4(setActiveConfigWithConstraints,
-                 status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+                 status_t(PhysicalDisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
                           hal::VsyncPeriodChangeTimeline*));
-    MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
-    MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*));
-    MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType));
+    MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
+    MOCK_METHOD2(getSupportedContentTypes,
+                 status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
+    MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
     MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
                        const std::unordered_map<std::string, bool>&());
 
@@ -107,8 +113,8 @@
     MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
     MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
     MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hal::HWDisplayId));
-    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(DisplayId));
+    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
+    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 020f93a..dcfc162 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -21,6 +21,7 @@
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
+#include <log/log.h>
 
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
@@ -55,6 +56,22 @@
     return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
 }
 
+ui::Rotation toRotation(uint32_t rotationFlag) {
+    switch (rotationFlag) {
+        case ui::Transform::RotationFlags::ROT_0:
+            return ui::ROTATION_0;
+        case ui::Transform::RotationFlags::ROT_90:
+            return ui::ROTATION_90;
+        case ui::Transform::RotationFlags::ROT_180:
+            return ui::ROTATION_180;
+        case ui::Transform::RotationFlags::ROT_270:
+            return ui::ROTATION_270;
+        default:
+            LOG_FATAL("Unexpected rotation flag %d", rotationFlag);
+            return ui::Rotation(-1);
+    }
+}
+
 struct OutputLayerTest : public testing::Test {
     struct OutputLayer final : public impl::OutputLayer {
         OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
@@ -138,7 +155,7 @@
         mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomBufferTransform = TR_IDENT;
 
-        mOutputState.viewport = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
     }
 
     FloatRect calculateOutputSourceCrop() {
@@ -209,7 +226,7 @@
 
         mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
 
         EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
     }
@@ -223,7 +240,7 @@
 }
 
 TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
-    mOutputState.viewport = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
 
     const FloatRect expected{0.f, 0.f, 960.f, 540.f};
     EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -245,7 +262,7 @@
         mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
-        mOutputState.viewport = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
@@ -293,7 +310,7 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
-    mOutputState.viewport = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
     const Rect expected{0, 0, 960, 540};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
@@ -358,7 +375,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -470,7 +487,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -676,8 +693,6 @@
     static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
             static_cast<Hwc2::IComposerClient::BlendMode>(41);
     static constexpr float kAlpha = 51.f;
-    static constexpr uint32_t kType = 61u;
-    static constexpr uint32_t kAppId = 62u;
     static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
@@ -711,8 +726,6 @@
 
         mLayerFEState.blendMode = kBlendMode;
         mLayerFEState.alpha = kAlpha;
-        mLayerFEState.type = kType;
-        mLayerFEState.appId = kAppId;
         mLayerFEState.colorTransform = kColorTransform;
         mLayerFEState.color = kColor;
         mLayerFEState.surfaceDamage = kSurfaceDamage;
@@ -746,7 +759,6 @@
 
         EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
     }
 
     void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
@@ -858,7 +870,7 @@
     // This test simulates a scenario where displayInstallOrientation is set to
     // ROT_90. This only has an effect on the transform; orientation stays 0 (see
     // DisplayDevice::setProjection).
-    mOutputState.orientation = TR_IDENT;
+    mOutputState.displaySpace.orientation = ui::ROTATION_0;
     mOutputState.transform = ui::Transform{TR_ROT_90};
     // Buffers are pre-rotated based on the transform hint (ROT_90); their
     // geomBufferTransform is set to the inverse transform.
@@ -988,7 +1000,7 @@
 
         mLayerFEState.cursorFrame = kDefaultCursorFrame;
 
-        mOutputState.viewport = kDefaultDisplayViewport;
+        mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
         mOutputState.transform = ui::Transform{kDefaultTransform};
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 59ed72e..9badb99 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -136,7 +136,7 @@
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
 
-        mOutput->editState().bounds = kDefaultDisplaySize;
+        mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
     }
 
     void injectOutputLayer(InjectedLayer& layer) {
@@ -235,42 +235,123 @@
  * Output::setProjection()
  */
 
-TEST_F(OutputTest, setProjectionTriviallyWorks) {
-    const ui::Transform transform{ui::Transform::ROT_180};
-    const int32_t orientation = 123;
-    const Rect frame{1, 2, 3, 4};
-    const Rect viewport{5, 6, 7, 8};
-    const Rect sourceClip{9, 10, 11, 12};
-    const Rect destinationClip{13, 14, 15, 16};
-    const bool needsFiltering = true;
+TEST_F(OutputTest, setProjectionWorks) {
+    const Rect displayRect{0, 0, 1000, 2000};
+    mOutput->editState().displaySpace.bounds = displayRect;
+    mOutput->editState().framebufferSpace.bounds = displayRect;
 
-    mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
-                           needsFiltering);
+    const ui::Rotation orientation = ui::ROTATION_90;
+    const Rect frame{50, 60, 100, 100};
+    const Rect viewport{10, 20, 30, 40};
 
-    EXPECT_THAT(mOutput->getState().transform, transform);
-    EXPECT_EQ(orientation, mOutput->getState().orientation);
-    EXPECT_EQ(frame, mOutput->getState().frame);
-    EXPECT_EQ(viewport, mOutput->getState().viewport);
-    EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
-    EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
-    EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
+    mOutput->setProjection(orientation, viewport, frame);
+
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+    const auto state = mOutput->getState();
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+    EXPECT_EQ(viewport, state.layerStackSpace.content);
+    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+    EXPECT_EQ(displayRect, state.displaySpace.bounds);
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+    EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
+    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+    EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
+}
+
+TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
+    const Rect displayRect{0, 0, 1000, 2000};
+    const Rect framebufferRect{0, 0, 500, 1000};
+    mOutput->editState().displaySpace.bounds = displayRect;
+    mOutput->editState().framebufferSpace.bounds = framebufferRect;
+
+    const ui::Rotation orientation = ui::ROTATION_90;
+    const Rect frame{50, 60, 100, 100};
+    const Rect viewport{10, 20, 30, 40};
+
+    mOutput->setProjection(orientation, viewport, frame);
+
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+    const auto state = mOutput->getState();
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+    EXPECT_EQ(viewport, state.layerStackSpace.content);
+    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+    EXPECT_EQ(displayRect, state.displaySpace.bounds);
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+    EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+    EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
+    EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
+    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
 }
 
 /*
- * Output::setBounds()
+ * Output::setDisplaySize()
  */
 
-TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
-    const ui::Size displaySize{200, 400};
+TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
+    mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
+    mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
+    mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
+    mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
+    mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
+    mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
+    mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
+    mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
+    mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
+    mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
 
-    EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
-    EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+    const ui::Size newDisplaySize{500, 1000};
 
-    mOutput->setBounds(displaySize);
+    EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1);
 
-    EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
+    mOutput->setDisplaySize(newDisplaySize);
 
-    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+    const auto state = mOutput->getState();
+
+    const Rect displayRect(newDisplaySize);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+    EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
+    EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+
+    EXPECT_EQ(displayRect, state.displaySpace.bounds);
+    EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
+    EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+
+    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+    EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
+    EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+
+    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+    EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
 }
 
 /*
@@ -431,7 +512,7 @@
 
     mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
+    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
 }
 
 /*
@@ -440,7 +521,7 @@
 
 TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
     const Rect viewport{100, 200};
-    mOutput->editState().viewport = viewport;
+    mOutput->editState().layerStackSpace.content = viewport;
     mOutput->editState().dirtyRegion.set(50, 300);
 
     {
@@ -452,7 +533,7 @@
 
 TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
     const Rect viewport{100, 200};
-    mOutput->editState().viewport = viewport;
+    mOutput->editState().layerStackSpace.content = viewport;
     mOutput->editState().dirtyRegion.set(50, 300);
 
     {
@@ -860,7 +941,7 @@
     OutputRebuildLayerStacksTest() {
         mOutput.mState.isEnabled = true;
         mOutput.mState.transform = kIdentityTransform;
-        mOutput.mState.bounds = kOutputBounds;
+        mOutput.mState.displaySpace.bounds = kOutputBounds;
 
         mRefreshArgs.updatingOutputGeometryThisFrame = true;
 
@@ -1067,8 +1148,8 @@
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
 
-        mOutput.mState.bounds = Rect(0, 0, 200, 300);
-        mOutput.mState.viewport = Rect(0, 0, 200, 300);
+        mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
+        mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
         mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
 
         mLayer.layerFEState.isVisible = true;
@@ -1148,7 +1229,7 @@
 }
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
-    mOutput.mState.bounds = Rect(0, 0, 0, 0);
+    mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
 
     ensureOutputLayerIfVisible();
 }
@@ -1345,7 +1426,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
 
     EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1371,7 +1452,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
 
     EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2785,12 +2866,12 @@
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
         mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
 
-        mOutput.mState.frame = kDefaultOutputFrame;
-        mOutput.mState.viewport = kDefaultOutputViewport;
-        mOutput.mState.sourceClip = kDefaultOutputSourceClip;
-        mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
-        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
-        mOutput.mState.orientation = kDefaultOutputOrientation;
+        mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
+        mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+        mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
+        mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
+        mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
         mOutput.mState.dataspace = kDefaultOutputDataspace;
         mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
         mOutput.mState.isSecure = false;
@@ -2825,7 +2906,9 @@
     // Call this member function to start using the mini-DSL defined above.
     [[nodiscard]] auto verify() { return ExecuteState::make(this); }
 
-    static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+    static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0;
+    static constexpr uint32_t kDefaultOutputOrientationFlags =
+            ui::Transform::toRotationFlags(kDefaultOutputOrientation);
     static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
     static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
     static constexpr float kDefaultMaxLuminance = 0.9f;
@@ -2834,7 +2917,6 @@
 
     static const Rect kDefaultOutputFrame;
     static const Rect kDefaultOutputViewport;
-    static const Rect kDefaultOutputSourceClip;
     static const Rect kDefaultOutputDestinationClip;
     static const mat4 kDefaultColorTransformMat;
 
@@ -2856,7 +2938,6 @@
 
 const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
 const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
 const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
 const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
 const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
@@ -2871,6 +2952,7 @@
     mOutput.mState.usesClientComposition = false;
 
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
@@ -2883,6 +2965,7 @@
     mOutput.mState.flipClientTarget = true;
 
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -2892,6 +2975,7 @@
 
 TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) {
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
 
@@ -2904,6 +2988,7 @@
     mOutput.mState.flipClientTarget = true;
 
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
 
@@ -2914,6 +2999,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2936,6 +3022,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2963,6 +3050,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2991,6 +3079,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3019,6 +3108,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3050,6 +3140,7 @@
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
@@ -3072,6 +3163,7 @@
 struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
     OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
                 .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3122,9 +3214,9 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3133,9 +3225,9 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3144,10 +3236,10 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientation})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3156,10 +3248,10 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientation})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3169,9 +3261,9 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(true)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3219,6 +3311,8 @@
     mOutput.mState.isSecure = false;
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3311,6 +3405,7 @@
         EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
         EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
@@ -3414,12 +3509,12 @@
 struct GenerateClientCompositionRequestsTest_ThreeLayers
       : public GenerateClientCompositionRequestsTest {
     GenerateClientCompositionRequestsTest_ThreeLayers() {
-        mOutput.mState.frame = kDisplayFrame;
-        mOutput.mState.viewport = kDisplayViewport;
-        mOutput.mState.sourceClip = kDisplaySourceClip;
-        mOutput.mState.destinationClip = kDisplayDestinationClip;
-        mOutput.mState.transform = ui::Transform{kDisplayOrientation};
-        mOutput.mState.orientation = kDisplayOrientation;
+        mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
+        mOutput.mState.layerStackSpace.content = kDisplayViewport;
+        mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+        mOutput.mState.transform =
+                ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
+        mOutput.mState.displaySpace.orientation = kDisplayOrientation;
         mOutput.mState.needsFiltering = false;
         mOutput.mState.isSecure = false;
 
@@ -3443,12 +3538,11 @@
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
     }
 
-    static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+    static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
     static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
 
     static const Rect kDisplayFrame;
     static const Rect kDisplayViewport;
-    static const Rect kDisplaySourceClip;
     static const Rect kDisplayDestinationClip;
 
     std::array<Layer, 3> mLayers;
@@ -3456,7 +3550,6 @@
 
 const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
 const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
 const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
                                                                                       203);
 
@@ -3583,15 +3676,14 @@
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
     Region accumClearRegion(Rect(10, 11, 12, 13));
-    Region dummyRegion;
+    Region stubRegion;
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false,       /* identity transform */
-            false,       /* needs filtering */
-            false,       /* secure */
-            false,       /* supports protected content */
-            dummyRegion, /* clear region */
+            false,      /* needs filtering */
+            false,      /* secure */
+            false,      /* supports protected content */
+            stubRegion, /* clear region */
             kDisplayViewport,
             kDisplayDataspace,
             false /* realContentIsVisible */,
@@ -3599,7 +3691,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3643,7 +3734,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(Rect(10, 10, 20, 20)),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3655,7 +3745,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(Rect(0, 0, 30, 30)),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3667,7 +3756,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(Rect(0, 0, 40, 201)),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3699,7 +3787,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3711,7 +3798,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3723,7 +3809,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3755,7 +3840,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3768,7 +3852,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3780,7 +3863,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3811,7 +3893,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3823,7 +3904,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3835,7 +3915,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3864,7 +3943,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3876,7 +3954,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3888,7 +3965,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3938,6 +4014,34 @@
     mOutput->updateAndWriteCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    // Layer requesting blur, or below, should request client composition.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    BlurRegion region;
+    layer2.layerFEState.blurRegions.push_back(region);
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
     // In split-screen landscape mode, the screen is rotated 90 degrees, with
     // one layer on the left covering the left side of the output, and one layer
@@ -3945,17 +4049,15 @@
 
     const Rect kPortraitFrame(0, 0, 1000, 2000);
     const Rect kPortraitViewport(0, 0, 2000, 1000);
-    const Rect kPortraitSourceClip(0, 0, 1000, 2000);
     const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
-    const uint32_t kPortraitOrientation = TR_ROT_90;
+    const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
     constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
-    mOutput.mState.frame = kPortraitFrame;
-    mOutput.mState.viewport = kPortraitViewport;
-    mOutput.mState.sourceClip = kPortraitSourceClip;
-    mOutput.mState.destinationClip = kPortraitDestinationClip;
-    mOutput.mState.transform = ui::Transform{kPortraitOrientation};
-    mOutput.mState.orientation = kPortraitOrientation;
+    mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
+    mOutput.mState.layerStackSpace.content = kPortraitViewport;
+    mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+    mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
+    mOutput.mState.displaySpace.orientation = kPortraitOrientation;
     mOutput.mState.needsFiltering = false;
     mOutput.mState.isSecure = true;
 
@@ -3982,7 +4084,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
             Region(Rect(0, 0, 1000, 1000)),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
@@ -4000,7 +4101,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
             Region(Rect(1000, 0, 2000, 1000)),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
@@ -4034,7 +4134,6 @@
     Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
-            false,                                                    /* identity transform */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
@@ -4080,7 +4179,6 @@
     Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
-            false,                                                    /* identity transform */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
new file mode 100644
index 0000000..704f5a8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <compositionengine/ProjectionSpace.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+// Returns a rectangular strip along the side of the given rect pointed by
+// rotation. E.g. if rotation is ROTATION_0, the srip will be along the top
+// side, if it is ROTATION_90 the stip will be along the right wall.
+// One of the dimensions of the strip will be 0 and the other one will match
+// the length of the corresponding side.
+// The strip will be contained inside the given rect.
+Rect getSideStrip(const Rect& rect, ui::Rotation rotation) {
+    int width, height;
+    if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+        width = 0;
+        height = rect.height();
+    } else {
+        width = rect.width();
+        height = 0;
+    }
+
+    if (rotation == ui::ROTATION_0 || rotation == ui::ROTATION_270) {
+        return Rect(rect.left, rect.top, rect.left + width, rect.top + height);
+    }
+
+    if (rotation == ui::ROTATION_90) {
+        return Rect(rect.right, rect.top, rect.right + width, rect.top + height);
+    }
+
+    if (rotation == ui::ROTATION_180) {
+        return Rect(rect.left, rect.bottom, rect.left + width, rect.bottom + height);
+    }
+
+    return Rect::INVALID_RECT;
+}
+} // namespace
+
+TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
+    ProjectionSpace space;
+    space.content = Rect(100, 200);
+    space.bounds = Rect(100, 200);
+
+    const ui::Transform identity;
+    for (int rotation = 0; rotation <= 3; rotation++) {
+        space.orientation = ui::Rotation(rotation);
+        EXPECT_EQ(space.getTransform(space), identity);
+    }
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
+    ProjectionSpace source;
+    source.content = Rect(10, 10, 20, 20);
+    source.bounds = Rect(100, 200);
+
+    ProjectionSpace dest;
+    dest.content = Rect(10, 20, 30, 20);
+    dest.bounds = source.bounds;
+
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
+    ProjectionSpace source;
+    source.content = Rect(0, 0, 20, 20);
+    source.bounds = Rect(100, 200);
+
+    ProjectionSpace dest;
+    dest.content = Rect(0, 0, 40, 30);
+    dest.bounds = source.bounds;
+
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getSideStripTest) {
+    const Rect rect(10, 20, 40, 100);
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_0), Rect(10, 20, 40, 20));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_90), Rect(40, 20, 40, 100));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_180), Rect(10, 100, 40, 100));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_270), Rect(10, 20, 10, 100));
+}
+
+void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content)
+            << "Source content doesn't map to dest content when projecting " << to_string(source)
+            << " onto " << to_string(dest);
+
+    // We take a strip at the top (according to the orientation) of each
+    // content rect and verify that transform maps between them. This way we
+    // verify that the transform is rotating properly.
+    // In the following example the strip is marked with asterisks:
+    //
+    //      *******                +-------*
+    //      |     |                |       *
+    //      |     |                |       *
+    //      +-----+                +-------*
+    // source(ROTATION_0)      dest (ROTATION_90)
+    const auto sourceStrip = getSideStrip(source.content, source.orientation);
+    const auto destStrip = getSideStrip(dest.content, dest.orientation);
+    ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
+    ASSERT_NE(destStrip, Rect::INVALID_RECT);
+    const auto mappedStrip = transform.transform(sourceStrip);
+    EXPECT_EQ(mappedStrip, destStrip)
+            << to_string(sourceStrip) << " maps to " << to_string(mappedStrip) << " instead of "
+            << to_string(destStrip) << " when projecting " << to_string(source) << " onto "
+            << to_string(dest);
+}
+
+TEST(ProjectionSpaceTest, getTransformWithOrienations) {
+    ProjectionSpace source;
+    source.bounds = Rect(12, 13, 678, 789);
+    source.content = Rect(40, 50, 234, 343);
+    ProjectionSpace dest;
+    dest.bounds = Rect(17, 18, 879, 564);
+    dest.content = Rect(43, 52, 432, 213);
+
+    for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
+        source.orientation = ui::Rotation(sourceRot);
+        for (int destRot = 0; destRot <= 3; destRot++) {
+            dest.orientation = ui::Rotation(destRot);
+            testTransform(source, dest);
+        }
+    }
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index fd47e45..6ce8a6b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -33,7 +33,7 @@
 
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
 const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
 
 using testing::_;
@@ -48,7 +48,7 @@
 class RenderSurfaceTest : public testing::Test {
 public:
     RenderSurfaceTest() {
-        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(DEFAULT_DISPLAY_ID));
         EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
         EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
         EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
@@ -82,7 +82,8 @@
     EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
             .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+            .WillOnce(Return(NO_ERROR));
 
     mSurface.initialize();
 }
@@ -136,7 +137,9 @@
 
 TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
     EXPECT_FALSE(mSurface.isProtected());
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+    EXPECT_CALL(*mNativeWindow,
+                setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+                         GRALLOC_USAGE_PROTECTED))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.setProtected(true);
@@ -145,7 +148,8 @@
 
 TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
     EXPECT_FALSE(mSurface.isProtected());
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+            .WillOnce(Return(NO_ERROR));
 
     mSurface.setProtected(false);
     EXPECT_FALSE(mSurface.isProtected());
@@ -153,9 +157,12 @@
 
 TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) {
     EXPECT_FALSE(mSurface.isProtected());
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+    EXPECT_CALL(*mNativeWindow,
+                setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+                         GRALLOC_USAGE_PROTECTED))
             .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+            .WillOnce(Return(NO_ERROR));
 
     mSurface.setProtected(true);
     EXPECT_TRUE(mSurface.isProtected());
@@ -165,7 +172,9 @@
 
 TEST_F(RenderSurfaceTest, setProtectedEnableWithError) {
     EXPECT_FALSE(mSurface.isProtected());
-    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+    EXPECT_CALL(*mNativeWindow,
+                setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+                         GRALLOC_USAGE_PROTECTED))
             .WillOnce(Return(INVALID_OPERATION));
     mSurface.setProtected(true);
     EXPECT_FALSE(mSurface.isProtected());
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9aa274b..cbc201f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -29,6 +29,7 @@
 #include <compositionengine/DisplayColorProfileCreationArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/DisplaySurface.h>
+#include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/RenderSurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/OutputCompositionState.h>
@@ -101,11 +102,11 @@
 }
 
 int DisplayDevice::getWidth() const {
-    return mCompositionDisplay->getState().bounds.getWidth();
+    return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
 }
 
 int DisplayDevice::getHeight() const {
-    return mCompositionDisplay->getState().bounds.getHeight();
+    return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
 }
 
 void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -155,96 +156,38 @@
 }
 
 void DisplayDevice::setDisplaySize(int width, int height) {
-    mCompositionDisplay->setBounds(ui::Size(width, height));
+    LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
+    mCompositionDisplay->setDisplaySize(ui::Size(width, height));
 }
 
-void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
+                                  Rect orientedDisplaySpaceRect) {
     mOrientation = orientation;
 
-    const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
-    const int displayWidth = displayBounds.width();
-    const int displayHeight = displayBounds.height();
-
-    ui::Transform rotation;
-    if (const auto flags = ui::Transform::toRotationFlags(orientation);
-        flags != ui::Transform::ROT_INVALID) {
-        rotation.set(flags, displayWidth, displayHeight);
-    }
-
-    if (!frame.isValid()) {
-        // the destination frame can be invalid if it has never been set,
-        // in that case we assume the whole display frame.
-        frame = Rect(displayWidth, displayHeight);
-    }
-
-    if (viewport.isEmpty()) {
-        // viewport can be invalid if it has never been set, in that case
-        // we assume the whole display size.
-        // it's also invalid to have an empty viewport, so we handle that
-        // case in the same way.
-        viewport = Rect(displayWidth, displayHeight);
-        if (rotation.getOrientation() & ui::Transform::ROT_90) {
-            // viewport is always specified in the logical orientation
-            // of the display (ie: post-rotation).
-            std::swap(viewport.right, viewport.bottom);
-        }
-    }
-
-    ui::Transform logicalTranslation, physicalTranslation, scale;
-    const float sourceWidth = viewport.width();
-    const float sourceHeight = viewport.height();
-    const float destWidth = frame.width();
-    const float destHeight = frame.height();
-    if (sourceWidth != destWidth || sourceHeight != destHeight) {
-        const float scaleX = destWidth / sourceWidth;
-        const float scaleY = destHeight / sourceHeight;
-        scale.set(scaleX, 0, 0, scaleY);
-    }
-
-    const float sourceX = viewport.left;
-    const float sourceY = viewport.top;
-    const float destX = frame.left;
-    const float destY = frame.top;
-    logicalTranslation.set(-sourceX, -sourceY);
-    physicalTranslation.set(destX, destY);
-
-    // need to take care of primary display rotation for globalTransform
-    // for case if the panel is not installed aligned with device orientation
-    if (isPrimary()) {
-        if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
-            flags != ui::Transform::ROT_INVALID) {
-            rotation.set(flags, displayWidth, displayHeight);
-        }
-    }
-
-    // The viewport and frame are both in the logical orientation.
-    // Apply the logical translation, scale to physical size, apply the
-    // physical translation and finally rotate to the physical orientation.
-    ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
-
-    const uint8_t type = globalTransform.getType();
-    const bool needsFiltering =
-            (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
-
-    const Rect& sourceClip = viewport;
-    Rect destinationClip = globalTransform.transform(viewport);
-    if (destinationClip.isEmpty()) {
-        destinationClip = displayBounds;
-    }
-    // Make sure the destination clip is contained in the display bounds
-    destinationClip.intersect(displayBounds, &destinationClip);
-
-    uint32_t transformOrientation;
-
     if (isPrimary()) {
         sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
-        transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
-    } else {
-        transformOrientation = ui::Transform::toRotationFlags(orientation);
     }
 
-    getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
-                                           sourceClip, destinationClip, needsFiltering);
+    if (!orientedDisplaySpaceRect.isValid()) {
+        // The destination frame can be invalid if it has never been set,
+        // in that case we assume the whole display size.
+        orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+    }
+
+    if (layerStackSpaceRect.isEmpty()) {
+        // The layerStackSpaceRect can be invalid if it has never been set, in that case
+        // we assume the whole framebuffer size.
+        layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+        if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+            std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
+        }
+    }
+
+    // We need to take care of display rotation for globalTransform for case if the panel is not
+    // installed aligned with device orientation.
+    const auto transformOrientation = orientation + mPhysicalOrientation;
+    getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect,
+                                           orientedDisplaySpaceRect);
 }
 
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -252,17 +195,12 @@
 }
 
 std::string DisplayDevice::getDebugName() const {
-    std::string displayId;
-    if (const auto id = getId()) {
-        displayId = to_string(*id) + ", ";
-    }
-
     const char* type = "virtual";
     if (mConnectionType) {
         type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
     }
 
-    return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+    return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type,
                               isPrimary() ? ", primary" : "", mDisplayName.c_str());
 }
 
@@ -286,9 +224,7 @@
     return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
 }
 
-// ----------------------------------------------------------------------------
-
-const std::optional<DisplayId>& DisplayDevice::getId() const {
+DisplayId DisplayDevice::getId() const {
     return mCompositionDisplay->getId();
 }
 
@@ -297,7 +233,7 @@
 }
 
 const Rect& DisplayDevice::getBounds() const {
-    return mCompositionDisplay->getState().bounds;
+    return mCompositionDisplay->getState().displaySpace.bounds;
 }
 
 const Region& DisplayDevice::getUndefinedRegion() const {
@@ -312,20 +248,20 @@
     return mCompositionDisplay->getState().layerStackId;
 }
 
+ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
+    return mCompositionDisplay->getTransformHint();
+}
+
 const ui::Transform& DisplayDevice::getTransform() const {
     return mCompositionDisplay->getState().transform;
 }
 
-const Rect& DisplayDevice::getViewport() const {
-    return mCompositionDisplay->getState().viewport;
+const Rect& DisplayDevice::getLayerStackSpaceRect() const {
+    return mCompositionDisplay->getState().layerStackSpace.content;
 }
 
-const Rect& DisplayDevice::getFrame() const {
-    return mCompositionDisplay->getState().frame;
-}
-
-const Rect& DisplayDevice::getSourceClip() const {
-    return mCompositionDisplay->getState().sourceClip;
+const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
+    return mCompositionDisplay->getState().orientedDisplaySpace.content;
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4dabd2b..cc38ab0 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -27,6 +27,7 @@
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
+#include <ui/DisplayId.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/GraphicTypes.h>
@@ -40,7 +41,6 @@
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
-#include "RenderArea.h"
 #include "Scheduler/HwcStrongTypes.h"
 
 namespace android {
@@ -59,12 +59,14 @@
 class DisplaySurface;
 } // namespace compositionengine
 
-class DisplayDevice : public LightRefBase<DisplayDevice> {
+class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
     explicit DisplayDevice(DisplayDeviceCreationArgs& args);
+
+    // Must be destroyed on the main thread because it may call into HWComposer.
     virtual ~DisplayDevice();
 
     std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -93,18 +95,22 @@
 
     static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
 
-    ui::Transform::RotationFlags getTransformHint() const {
-        return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
-    }
-
+    ui::Transform::RotationFlags getTransformHint() const;
     const ui::Transform& getTransform() const;
-    const Rect& getViewport() const;
-    const Rect& getFrame() const;
-    const Rect& getSourceClip() const;
+    const Rect& getLayerStackSpaceRect() const;
+    const Rect& getOrientedDisplaySpaceRect() const;
     bool needsFiltering() const;
     ui::LayerStack getLayerStack() const;
 
-    const std::optional<DisplayId>& getId() const;
+    // Returns the physical ID of this display. This function asserts the ID is physical and it
+    // shouldn't be called for other display types, e.g. virtual.
+    PhysicalDisplayId getPhysicalId() const {
+        const auto displayIdOpt = PhysicalDisplayId::tryCast(getId());
+        LOG_FATAL_IF(!displayIdOpt);
+        return *displayIdOpt;
+    }
+
+    DisplayId getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
     int32_t getSequenceId() const { return mSequenceId; }
 
@@ -193,7 +199,7 @@
 
 struct DisplayDeviceState {
     struct Physical {
-        DisplayId id;
+        PhysicalDisplayId id;
         DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
         std::optional<DeviceProductInfo> deviceProductInfo;
@@ -208,8 +214,8 @@
     std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
     ui::LayerStack layerStack = ui::NO_LAYER_STACK;
-    Rect viewport;
-    Rect frame;
+    Rect layerStackSpaceRect;
+    Rect orientedDisplaySpaceRect;
     ui::Rotation orientation = ui::ROTATION_0;
     uint32_t width = 0;
     uint32_t height = 0;
@@ -244,118 +250,4 @@
     bool isPrimary{false};
 };
 
-class DisplayRenderArea : public RenderArea {
-public:
-    DisplayRenderArea(const sp<const DisplayDevice>& display,
-                      RotationFlags rotation = ui::Transform::ROT_0)
-          : DisplayRenderArea(display, display->getBounds(),
-                              static_cast<uint32_t>(display->getWidth()),
-                              static_cast<uint32_t>(display->getHeight()),
-                              display->getCompositionDataSpace(), rotation) {}
-
-    DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
-                      uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
-                      bool allowSecureLayers = true)
-          : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
-                       display->getViewport(), applyDeviceOrientation(rotation, display)),
-            mDisplay(std::move(display)),
-            mSourceCrop(sourceCrop),
-            mAllowSecureLayers(allowSecureLayers) {}
-
-    const ui::Transform& getTransform() const override { return mTransform; }
-    Rect getBounds() const override { return mDisplay->getBounds(); }
-    int getHeight() const override { return mDisplay->getHeight(); }
-    int getWidth() const override { return mDisplay->getWidth(); }
-    bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
-    sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
-
-    bool needsFiltering() const override {
-        // check if the projection from the logical render area
-        // to the physical render area requires filtering
-        const Rect& sourceCrop = getSourceCrop();
-        int width = sourceCrop.width();
-        int height = sourceCrop.height();
-        if (getRotationFlags() & ui::Transform::ROT_90) {
-            std::swap(width, height);
-        }
-        return width != getReqWidth() || height != getReqHeight();
-    }
-
-    Rect getSourceCrop() const override {
-        // use the projected display viewport by default.
-        if (mSourceCrop.isEmpty()) {
-            return mDisplay->getSourceClip();
-        }
-
-        // If there is a source crop provided then it is assumed that the device
-        // was in portrait orientation. This may not logically be true, so
-        // correct for the orientation error by undoing the rotation
-
-        ui::Rotation logicalOrientation = mDisplay->getOrientation();
-        if (logicalOrientation == ui::Rotation::Rotation90) {
-            logicalOrientation = ui::Rotation::Rotation270;
-        } else if (logicalOrientation == ui::Rotation::Rotation270) {
-            logicalOrientation = ui::Rotation::Rotation90;
-        }
-
-        const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
-        int width = mDisplay->getSourceClip().getWidth();
-        int height = mDisplay->getSourceClip().getHeight();
-        ui::Transform rotation;
-        rotation.set(flags, width, height);
-        return rotation.transform(mSourceCrop);
-    }
-
-private:
-    static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
-                                                const sp<const DisplayDevice>& device) {
-        uint32_t inverseRotate90 = 0;
-        uint32_t inverseReflect = 0;
-
-        // Reverse the logical orientation.
-        ui::Rotation logicalOrientation = device->getOrientation();
-        if (logicalOrientation == ui::Rotation::Rotation90) {
-            logicalOrientation = ui::Rotation::Rotation270;
-        } else if (logicalOrientation == ui::Rotation::Rotation270) {
-            logicalOrientation = ui::Rotation::Rotation90;
-        }
-
-        const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation;
-
-        switch (orientation) {
-            case ui::ROTATION_0:
-                return orientationFlag;
-
-            case ui::ROTATION_90:
-                inverseRotate90 = ui::Transform::ROT_90;
-                inverseReflect = ui::Transform::ROT_180;
-                break;
-
-            case ui::ROTATION_180:
-                inverseReflect = ui::Transform::ROT_180;
-                break;
-
-            case ui::ROTATION_270:
-                inverseRotate90 = ui::Transform::ROT_90;
-                break;
-        }
-
-        const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90;
-        uint32_t reflect = orientationFlag & ui::Transform::ROT_180;
-
-        // Apply reflection for double rotation.
-        if (rotate90 & inverseRotate90) {
-            reflect = ~reflect & ui::Transform::ROT_180;
-        }
-
-        return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
-                                          (reflect ^ inverseReflect));
-    }
-
-    const sp<const DisplayDevice> mDisplay;
-    const Rect mSourceCrop;
-    const bool mAllowSecureLayers;
-    const ui::Transform mTransform = ui::Transform();
-};
-
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dbdffec..1bf43da 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -117,63 +117,7 @@
 
 namespace impl {
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
-    : CommandWriterBase(initialMaxSize) {}
-
-Composer::CommandWriter::~CommandWriter()
-{
-}
-
-void Composer::CommandWriter::setLayerInfo(uint32_t type, uint32_t appId)
-{
-    constexpr uint16_t kSetLayerInfoLength = 2;
-    beginCommand(static_cast<V2_1::IComposerClient::Command>(
-                         IVrComposerClient::VrCommand::SET_LAYER_INFO),
-                 kSetLayerInfoLength);
-    write(type);
-    write(appId);
-    endCommand();
-}
-
-void Composer::CommandWriter::setClientTargetMetadata(
-        const IVrComposerClient::BufferMetadata& metadata)
-{
-    constexpr uint16_t kSetClientTargetMetadataLength = 7;
-    beginCommand(static_cast<V2_1::IComposerClient::Command>(
-                         IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA),
-                 kSetClientTargetMetadataLength);
-    writeBufferMetadata(metadata);
-    endCommand();
-}
-
-void Composer::CommandWriter::setLayerBufferMetadata(
-        const IVrComposerClient::BufferMetadata& metadata)
-{
-    constexpr uint16_t kSetLayerBufferMetadataLength = 7;
-    beginCommand(static_cast<V2_1::IComposerClient::Command>(
-                         IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA),
-                 kSetLayerBufferMetadataLength);
-    writeBufferMetadata(metadata);
-    endCommand();
-}
-
-void Composer::CommandWriter::writeBufferMetadata(
-        const IVrComposerClient::BufferMetadata& metadata)
-{
-    write(metadata.width);
-    write(metadata.height);
-    write(metadata.stride);
-    write(metadata.layerCount);
-    writeSigned(static_cast<int32_t>(metadata.format));
-    write64(metadata.usage);
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
-Composer::Composer(const std::string& serviceName)
-    : mWriter(kWriterInitialSize),
-      mIsUsingVrComposer(serviceName == std::string("vr"))
-{
+Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
     mComposer = V2_1::IComposer::getService(serviceName);
 
     if (mComposer == nullptr) {
@@ -215,15 +159,6 @@
     if (mClient == nullptr) {
         LOG_ALWAYS_FATAL("failed to create composer client");
     }
-
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-    if (mIsUsingVrComposer) {
-        sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient);
-        if (vrClient == nullptr) {
-            LOG_ALWAYS_FATAL("failed to create vr composer client");
-        }
-    }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 }
 
 Composer::~Composer() = default;
@@ -262,10 +197,6 @@
     }
 }
 
-bool Composer::isRemote() {
-    return mClient->isRemote();
-}
-
 void Composer::resetCommands() {
     mWriter.reset();
 }
@@ -587,20 +518,6 @@
 {
     mWriter.selectDisplay(display);
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-    if (mIsUsingVrComposer && target.get()) {
-        IVrComposerClient::BufferMetadata metadata = {
-                .width = target->getWidth(),
-                .height = target->getHeight(),
-                .stride = target->getStride(),
-                .layerCount = target->getLayerCount(),
-                .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()),
-                .usage = target->getUsage(),
-        };
-        mWriter.setClientTargetMetadata(metadata);
-    }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
     const native_handle_t* handle = nullptr;
     if (target.get()) {
         handle = target->getNativeBuffer()->handle;
@@ -720,20 +637,6 @@
     mWriter.selectDisplay(display);
     mWriter.selectLayer(layer);
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-    if (mIsUsingVrComposer && buffer.get()) {
-        IVrComposerClient::BufferMetadata metadata = {
-                .width = buffer->getWidth(),
-                .height = buffer->getHeight(),
-                .stride = buffer->getStride(),
-                .layerCount = buffer->getLayerCount(),
-                .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()),
-                .usage = buffer->getUsage(),
-        };
-        mWriter.setLayerBufferMetadata(metadata);
-    }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
     const native_handle_t* handle = nullptr;
     if (buffer.get()) {
         handle = buffer->getNativeBuffer()->handle;
@@ -850,27 +753,6 @@
     return Error::NONE;
 }
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
-                             uint32_t appId)
-{
-    if (mIsUsingVrComposer) {
-        mWriter.selectDisplay(display);
-        mWriter.selectLayer(layer);
-        mWriter.setLayerInfo(type, appId);
-    }
-    return Error::NONE;
-}
-#else
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) {
-    if (mIsUsingVrComposer) {
-        mWriter.selectDisplay(display);
-        mWriter.selectLayer(layer);
-    }
-    return Error::NONE;
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
 Error Composer::execute()
 {
     // prepare input command queue
@@ -1676,6 +1558,7 @@
     if (found == mReturnData.end()) {
         outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
         outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+        return;
     }
 
     ReturnData& data = found->second;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 00ef782..5b66809 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -27,9 +27,6 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
@@ -47,10 +44,6 @@
 
 namespace Hwc2 {
 
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-using frameworks::vr::composer::V2_0::IVrComposerClient;
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
 namespace types = hardware::graphics::common;
 
 namespace V2_1 = hardware::graphics::composer::V2_1;
@@ -91,11 +84,6 @@
 
     virtual void registerCallback(const sp<IComposerCallback>& callback) = 0;
 
-    // Returns true if the connected composer service is running in a remote
-    // process, false otherwise. This will return false if the service is
-    // configured in passthrough mode, for example.
-    virtual bool isRemote() = 0;
-
     // Reset all pending commands in the command buffer. Useful if you want to
     // skip a frame but have already queued some commands.
     virtual void resetCommands() = 0;
@@ -104,7 +92,6 @@
     virtual Error executeCommands() = 0;
 
     virtual uint32_t getMaxVirtualDisplayCount() = 0;
-    virtual bool isUsingVrComposer() const = 0;
     virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
                                        Display* outDisplay) = 0;
     virtual Error destroyVirtualDisplay(Display display) = 0;
@@ -188,7 +175,6 @@
     virtual Error setLayerVisibleRegion(Display display, Layer layer,
                                         const std::vector<IComposerClient::Rect>& visible) = 0;
     virtual Error setLayerZOrder(Display display, Layer layer, uint32_t z) = 0;
-    virtual Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) = 0;
 
     // Composer HAL 2.2
     virtual Error setLayerPerFrameMetadata(
@@ -344,11 +330,6 @@
 
     void registerCallback(const sp<IComposerCallback>& callback) override;
 
-    // Returns true if the connected composer service is running in a remote
-    // process, false otherwise. This will return false if the service is
-    // configured in passthrough mode, for example.
-    bool isRemote() override;
-
     // Reset all pending commands in the command buffer. Useful if you want to
     // skip a frame but have already queued some commands.
     void resetCommands() override;
@@ -357,7 +338,6 @@
     Error executeCommands() override;
 
     uint32_t getMaxVirtualDisplayCount() override;
-    bool isUsingVrComposer() const override { return mIsUsingVrComposer; }
     Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
                                Display* outDisplay) override;
     Error destroyVirtualDisplay(Display display) override;
@@ -436,7 +416,6 @@
     Error setLayerVisibleRegion(Display display, Layer layer,
                                 const std::vector<IComposerClient::Rect>& visible) override;
     Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-    Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) override;
 
     // Composer HAL 2.2
     Error setLayerPerFrameMetadata(
@@ -490,29 +469,11 @@
             IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
 
 private:
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-    class CommandWriter : public CommandWriterBase {
-    public:
-        explicit CommandWriter(uint32_t initialMaxSize);
-        ~CommandWriter() override;
-
-        void setLayerInfo(uint32_t type, uint32_t appId);
-        void setClientTargetMetadata(
-                const IVrComposerClient::BufferMetadata& metadata);
-        void setLayerBufferMetadata(
-                const IVrComposerClient::BufferMetadata& metadata);
-
-    private:
-        void writeBufferMetadata(
-                const IVrComposerClient::BufferMetadata& metadata);
-    };
-#else
     class CommandWriter : public CommandWriterBase {
     public:
         explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
         ~CommandWriter() override {}
     };
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
@@ -531,10 +492,6 @@
         64 * 1024 / sizeof(uint32_t) - 16;
     CommandWriter mWriter;
     CommandReader mReader;
-
-    // When true, the we attach to the vr_hwcomposer service instead of the
-    // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr.
-    const bool mIsUsingVrComposer;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 52a6380..98209bb 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #undef LOG_TAG
 #define LOG_TAG "DisplayIdentification"
 
@@ -38,7 +34,6 @@
 constexpr size_t kEdidBlockSize = 128;
 constexpr size_t kEdidHeaderLength = 5;
 
-constexpr uint16_t kFallbackEdidManufacturerId = 0;
 constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
 
 std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
@@ -126,8 +121,8 @@
         constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
 
         if (tag == kVendorSpecificDataBlockTag) {
-            const uint32_t ieeeRegistrationId =
-                    dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16);
+            const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
+                    dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
             constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
 
             if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
@@ -152,14 +147,6 @@
 
 } // namespace
 
-uint16_t DisplayId::manufacturerId() const {
-    return static_cast<uint16_t>(value >> 40);
-}
-
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
-    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
-}
-
 bool isEdid(const DisplayIdentificationData& data) {
     const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
     return data.size() >= sizeof(kMagic) &&
@@ -184,7 +171,7 @@
 
     // Plug and play ID encoded as big-endian 16-bit value.
     const uint16_t manufacturerId =
-            (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];
+            static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
 
     const auto pnpId = getPnpId(manufacturerId);
     if (!pnpId) {
@@ -197,7 +184,8 @@
         ALOGE("Invalid EDID: product ID is truncated.");
         return {};
     }
-    const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8);
+    const uint16_t productId =
+            static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
 
     constexpr size_t kManufactureWeekOffset = 16;
     if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
@@ -323,8 +311,8 @@
     return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
 }
 
-std::optional<PnpId> getPnpId(DisplayId displayId) {
-    return getPnpId(displayId.manufacturerId());
+std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
+    return getPnpId(displayId.getManufacturerId());
 }
 
 std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
@@ -339,21 +327,15 @@
         return {};
     }
 
-    const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+    const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
     return DisplayIdentificationInfo{.id = displayId,
                                      .name = std::string(edid->displayName),
                                      .deviceProductInfo = buildDeviceProductInfo(*edid)};
 }
 
-DisplayId getFallbackDisplayId(uint8_t port) {
-    return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
-}
-
-DisplayId getVirtualDisplayId(uint32_t id) {
-    return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
+    return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
 }
 
 } // namespace android
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 4819d1d..fbea4e5 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.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.
@@ -24,38 +24,18 @@
 #include <vector>
 
 #include <ui/DeviceProductInfo.h>
-#include <ui/PhysicalDisplayId.h>
+#include <ui/DisplayId.h>
 
 #define LEGACY_DISPLAY_TYPE_PRIMARY 0
 #define LEGACY_DISPLAY_TYPE_EXTERNAL 1
 
 namespace android {
 
-struct DisplayId {
-    using Type = PhysicalDisplayId;
-    Type value;
-
-    uint16_t manufacturerId() const;
-
-    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
-};
-
-inline bool operator==(DisplayId lhs, DisplayId rhs) {
-    return lhs.value == rhs.value;
-}
-
-inline bool operator!=(DisplayId lhs, DisplayId rhs) {
-    return !(lhs == rhs);
-}
-
-inline std::string to_string(DisplayId displayId) {
-    return std::to_string(displayId.value);
-}
 
 using DisplayIdentificationData = std::vector<uint8_t>;
 
 struct DisplayIdentificationInfo {
-    DisplayId id;
+    PhysicalDisplayId id;
     std::string name;
     std::optional<DeviceProductInfo> deviceProductInfo;
 };
@@ -94,23 +74,12 @@
 bool isEdid(const DisplayIdentificationData&);
 std::optional<Edid> parseEdid(const DisplayIdentificationData&);
 std::optional<PnpId> getPnpId(uint16_t manufacturerId);
-std::optional<PnpId> getPnpId(DisplayId);
+std::optional<PnpId> getPnpId(PhysicalDisplayId);
 
 std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
         uint8_t port, const DisplayIdentificationData&);
 
-DisplayId getFallbackDisplayId(uint8_t port);
-DisplayId getVirtualDisplayId(uint32_t id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id);
 
 } // namespace android
 
-namespace std {
-
-template <>
-struct hash<android::DisplayId> {
-    size_t operator()(android::DisplayId displayId) const {
-        return hash<android::DisplayId::Type>()(displayId.value);
-    }
-};
-
-} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 4c3b3e5..14b54cd 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -56,7 +56,7 @@
  *
  */
 
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
                                        const sp<IGraphicBufferConsumer>& consumer,
                                        uint32_t maxWidth, uint32_t maxHeight)
       : ConsumerBase(consumer),
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index a1859f3..759943a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
+#include <ui/DisplayId.h>
 #include <ui/Size.h>
 
 #include "DisplayIdentification.h"
@@ -39,7 +40,7 @@
 
 class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
-    FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+    FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
                        const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
                        uint32_t maxHeight);
 
@@ -69,7 +70,7 @@
     status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
             sp<Fence>& outFence, ui::Dataspace& outDataspace);
 
-    const DisplayId mDisplayId;
+    const PhysicalDisplayId mDisplayId;
 
     // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
     // the device.
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index af76b81..e6bff04 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -208,13 +208,17 @@
         *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
     } else {
         // Get the default vsync period
-        HWConfigId configId = 0;
-        auto intError_2_1 = mComposer.getActiveConfig(mId, &configId);
-        error = static_cast<Error>(intError_2_1);
-        if (error == Error::NONE) {
-            auto config = mConfigs.at(configId);
-            *outVsyncPeriod = config->getVsyncPeriod();
+        std::shared_ptr<const Display::Config> config;
+        error = getActiveConfig(&config);
+        if (error != Error::NONE) {
+            return error;
         }
+        if (!config) {
+            // HWC has updated the display modes and hasn't notified us yet.
+            return Error::BAD_CONFIG;
+        }
+
+        *outVsyncPeriod = config->getVsyncPeriod();
     }
 
     return error;
@@ -890,6 +894,10 @@
             mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
 
     if (validTypes & HdrMetadata::HDR10PLUS) {
+        if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
+            return Error::BAD_PARAMETER;
+        }
+
         std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
         perFrameMetadataBlobs.push_back(
                 {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
@@ -969,12 +977,6 @@
     return static_cast<Error>(intError);
 }
 
-Error Layer::setInfo(uint32_t type, uint32_t appId)
-{
-  auto intError = mComposer.setLayerInfo(mDisplayId, mId, type, appId);
-  return static_cast<Error>(intError);
-}
-
 // Composer HAL 2.3
 Error Layer::setColorTransform(const android::mat4& matrix) {
     if (matrix == mColorMatrix) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 6819ff4..89df84b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_HWC2_H
-#define ANDROID_SF_HWC2_H
+#pragma once
 
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
@@ -36,15 +35,16 @@
 #include "Hal.h"
 
 namespace android {
-    struct DisplayedFrameStats;
-    class Fence;
-    class FloatRect;
-    class GraphicBuffer;
-    namespace Hwc2 {
-        class Composer;
-    }
 
-    class TestableSurfaceFlinger;
+class Fence;
+class FloatRect;
+class GraphicBuffer;
+class TestableSurfaceFlinger;
+struct DisplayedFrameStats;
+
+namespace Hwc2 {
+class Composer;
+} // namespace Hwc2
 
 namespace HWC2 {
 
@@ -61,19 +61,17 @@
 // All calls receive a sequenceId, which will be the value that was supplied to
 // HWC2::Device::registerCallback(). It's used to help differentiate callbacks
 // from different hardware composer instances.
-class ComposerCallback {
- public:
-     virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
-                                    hal::Connection connection) = 0;
-     virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0;
-     virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
-                                  std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0;
-     virtual void onVsyncPeriodTimingChangedReceived(
-             int32_t sequenceId, hal::HWDisplayId display,
-             const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0;
-     virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0;
+struct ComposerCallback {
+    virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0;
+    virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0;
+    virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+                                 std::optional<hal::VsyncPeriodNanos>) = 0;
+    virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId,
+                                                    const hal::VsyncPeriodChangeTimeline&) = 0;
+    virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0;
 
-     virtual ~ComposerCallback() = default;
+protected:
+    ~ComposerCallback() = default;
 };
 
 // Convenience C++ class to access per display functions directly.
@@ -242,15 +240,14 @@
 
 class Display : public HWC2::Display {
 public:
-    Display(android::Hwc2::Composer& composer,
-            const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
-            hal::DisplayType type);
+    Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
+            hal::DisplayType);
     ~Display() override;
 
     // Required by HWC2
     hal::Error acceptChanges() override;
     hal::Error createLayer(Layer** outLayer) override;
-    hal::Error destroyLayer(Layer* layer) override;
+    hal::Error destroyLayer(Layer*) override;
     hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
     hal::Error getActiveConfigIndex(int* outIndex) const override;
     hal::Error getChangedCompositionTypes(
@@ -260,8 +257,7 @@
     int32_t getSupportedPerFrameMetadata() const override;
     hal::Error getRenderIntents(hal::ColorMode colorMode,
                                 std::vector<hal::RenderIntent>* outRenderIntents) const override;
-    hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
-                                            android::mat4* outMatrix) override;
+    hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override;
 
     // Doesn't call into the HWC2 device, so no errors are possible
     std::vector<std::shared_ptr<const Config>> getConfigs() const override;
@@ -287,11 +283,11 @@
     hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
                                const android::sp<android::Fence>& acquireFence,
                                hal::Dataspace dataspace) override;
-    hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override;
+    hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
     hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
-    hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+    hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
                                const android::sp<android::Fence>& releaseFence) override;
-    hal::Error setPowerMode(hal::PowerMode mode) override;
+    hal::Error setPowerMode(hal::PowerMode) override;
     hal::Error setVsyncEnabled(hal::Vsync enabled) override;
     hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
     hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
@@ -319,13 +315,13 @@
     virtual bool isVsyncPeriodSwitchSupported() const override;
 
 private:
-    int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute);
-    void loadConfig(hal::HWConfigId configId);
+    int32_t getAttribute(hal::HWConfigId, hal::Attribute);
+    void loadConfig(hal::HWConfigId);
     void loadConfigs();
 
     // This may fail (and return a null pointer) if no layer with this ID exists
     // on this display
-    Layer* getLayerById(hal::HWLayerId id) const;
+    Layer* getLayerById(hal::HWLayerId) const;
 
     friend android::TestableSurfaceFlinger;
 
@@ -380,7 +376,6 @@
     [[clang::warn_unused_result]] virtual hal::Error setVisibleRegion(
             const android::Region& region) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setZOrder(uint32_t z) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setInfo(uint32_t type, uint32_t appId) = 0;
 
     // Composer HAL 2.3
     [[clang::warn_unused_result]] virtual hal::Error setColorTransform(
@@ -422,7 +417,6 @@
     hal::Error setTransform(hal::Transform transform) override;
     hal::Error setVisibleRegion(const android::Region& region) override;
     hal::Error setZOrder(uint32_t z) override;
-    hal::Error setInfo(uint32_t type, uint32_t appId) override;
 
     // Composer HAL 2.3
     hal::Error setColorTransform(const android::mat4& matrix) override;
@@ -454,5 +448,3 @@
 } // namespace impl
 } // namespace HWC2
 } // namespace android
-
-#endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7a2f0f3..1548d18 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -26,6 +26,7 @@
 
 #include "HWComposer.h"
 
+#include <android-base/properties.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -38,6 +39,7 @@
 #include "../Layer.h" // needed only for debugging
 #include "../Promise.h"
 #include "../SurfaceFlinger.h"
+#include "../SurfaceFlingerProperties.h"
 #include "ComposerHal.h"
 #include "HWC2.h"
 
@@ -143,12 +145,13 @@
 
 namespace impl {
 
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
+      : mComposer(std::move(composer)),
+        mUpdateDeviceProductInfoOnHotplugReconnect(
+                android::sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
 
 HWComposer::HWComposer(const std::string& composerServiceName)
-      : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
-}
+      : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
@@ -186,7 +189,7 @@
     return mCapabilities.count(capability) > 0;
 }
 
-bool HWComposer::hasDisplayCapability(DisplayId displayId,
+bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
                                       hal::DisplayCapability capability) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
@@ -204,6 +207,10 @@
     }
 }
 
+bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const {
+    return mUpdateDeviceProductInfoOnHotplugReconnect;
+}
+
 bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
@@ -214,10 +221,8 @@
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
     auto& displayData = mDisplayData[*displayId];
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
-        return false;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(*displayId).c_str());
 
     {
         std::lock_guard lock(displayData.lastHwVsyncLock);
@@ -244,11 +249,6 @@
 
 std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                             ui::PixelFormat* format) {
-    if (mRemainingHwcVirtualDisplays == 0) {
-        ALOGE("%s: No remaining virtual displays", __FUNCTION__);
-        return {};
-    }
-
     if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
         (width > SurfaceFlinger::maxVirtualDisplaySize ||
          height > SurfaceFlinger::maxVirtualDisplaySize)) {
@@ -256,35 +256,33 @@
               height, SurfaceFlinger::maxVirtualDisplaySize);
         return {};
     }
+
+    const auto displayId = mVirtualIdGenerator.nextId();
+    if (!displayId) {
+        ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+        return {};
+    }
+
     hal::HWDisplayId hwcDisplayId = 0;
     const auto error = static_cast<hal::Error>(
             mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     if (error != hal::Error::NONE) {
         ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+        mVirtualIdGenerator.markUnused(*displayId);
         return {};
     }
 
     auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
                                                          hwcDisplayId, hal::DisplayType::VIRTUAL);
     display->setConnected(true);
-
-    DisplayId displayId;
-    if (mFreeVirtualDisplayIds.empty()) {
-        displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
-    } else {
-        displayId = *mFreeVirtualDisplayIds.begin();
-        mFreeVirtualDisplayIds.erase(displayId);
-    }
-
-    auto& displayData = mDisplayData[displayId];
+    auto& displayData = mDisplayData[*displayId];
     displayData.hwcDisplay = std::move(display);
     displayData.isVirtual = true;
-
-    --mRemainingHwcVirtualDisplays;
     return displayId;
 }
 
-void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) {
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
+                                         PhysicalDisplayId displayId) {
     if (!mInternalHwcDisplayId) {
         mInternalHwcDisplayId = hwcDisplayId;
     } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
@@ -300,7 +298,7 @@
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 }
 
-HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
+HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     HWC2::Layer* layer;
@@ -309,14 +307,14 @@
     return layer;
 }
 
-void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
     RETURN_IF_INVALID_DISPLAY(displayId);
 
     auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
     RETURN_IF_HWC_ERROR(error, displayId);
 }
 
-nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
+nsecs_t HWComposer::getRefreshTimestamp(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
     const auto& displayData = mDisplayData.at(displayId);
     // this returns the last refresh timestamp.
@@ -328,13 +326,13 @@
     return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
 }
 
-bool HWComposer::isConnected(DisplayId displayId) const {
+bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->isConnected();
 }
 
 std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
-        DisplayId displayId) const {
+        PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     const auto& displayData = mDisplayData.at(displayId);
@@ -348,7 +346,7 @@
 }
 
 std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
-        DisplayId displayId) const {
+        PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     std::shared_ptr<const HWC2::Display::Config> config;
@@ -370,7 +368,7 @@
 
 // Composer 2.4
 
-DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
+DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
     const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
 
@@ -385,12 +383,12 @@
     return type;
 }
 
-bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
 }
 
-nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+nsecs_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
 
     nsecs_t vsyncPeriodNanos;
@@ -399,7 +397,7 @@
     return vsyncPeriodNanos;
 }
 
-int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
+int HWComposer::getActiveConfigIndex(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, -1);
 
     int index;
@@ -419,7 +417,7 @@
     return index;
 }
 
-std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     std::vector<ui::ColorMode> modes;
@@ -428,7 +426,7 @@
     return modes;
 }
 
-status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode,
                                         ui::RenderIntent renderIntent) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
@@ -442,14 +440,12 @@
     return NO_ERROR;
 }
 
-void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
+void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
-        return;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
@@ -470,7 +466,7 @@
     ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
 }
 
-status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
                                      const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
                                      ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -483,7 +479,7 @@
 }
 
 status_t HWComposer::getDeviceCompositionChanges(
-        DisplayId displayId, bool frameUsesClientComposition,
+        HalDisplayId displayId, bool frameUsesClientComposition,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -553,12 +549,12 @@
     return NO_ERROR;
 }
 
-sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
+sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
-sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
     auto fence = displayFences.find(layer);
@@ -569,7 +565,7 @@
     return fence->second;
 }
 
-status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -597,14 +593,12 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) {
+status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     const auto& displayData = mDisplayData[displayId];
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
-        return INVALID_OPERATION;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     if (mode == hal::PowerMode::OFF) {
         setVsyncEnabled(displayId, hal::Vsync::DISABLE);
@@ -653,7 +647,8 @@
 }
 
 status_t HWComposer::setActiveConfigWithConstraints(
-        DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints,
+        PhysicalDisplayId displayId, size_t configId,
+        const hal::VsyncPeriodChangeConstraints& constraints,
         hal::VsyncPeriodChangeTimeline* outTimeline) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
@@ -670,7 +665,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
+status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -683,15 +678,14 @@
     return NO_ERROR;
 }
 
-void HWComposer::disconnectDisplay(DisplayId displayId) {
+void HWComposer::disconnectDisplay(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
     // If this was a virtual display, add its slot back for reuse by future
     // virtual displays
     if (displayData.isVirtual) {
-        mFreeVirtualDisplayIds.insert(displayId);
-        ++mRemainingHwcVirtualDisplays;
+        mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId));
     }
 
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
@@ -706,27 +700,25 @@
     mDisplayData.erase(displayId);
 }
 
-status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& buffer) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto& displayData = mDisplayData[displayId];
 
-    if (!displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
-        return INVALID_OPERATION;
-    }
+    LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
 
-void HWComposer::clearReleaseFences(DisplayId displayId) {
+void HWComposer::clearReleaseFences(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     mDisplayData[displayId].releaseFences.clear();
 }
 
-status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -735,12 +727,12 @@
     return NO_ERROR;
 }
 
-int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
     return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
 }
 
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId,
                                                            ui::ColorMode colorMode) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
@@ -750,7 +742,7 @@
     return renderIntents;
 }
 
-mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     mat4 matrix;
@@ -760,7 +752,7 @@
     return matrix;
 }
 
-status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId,
                                                            ui::PixelFormat* outFormat,
                                                            ui::Dataspace* outDataspace,
                                                            uint8_t* outComponentMask) {
@@ -774,7 +766,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled,
                                                       uint8_t componentMask, uint64_t maxFrames) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
@@ -788,7 +780,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames,
                                                uint64_t timestamp, DisplayedFrameStats* outStats) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
@@ -798,7 +790,8 @@
     return NO_ERROR;
 }
 
-std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
+std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
+                                                       float brightness) {
     RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
     auto& display = mDisplayData[displayId].hwcDisplay;
 
@@ -815,11 +808,7 @@
             });
 }
 
-bool HWComposer::isUsingVrComposer() const {
-    return getComposer()->isUsingVrComposer();
-}
-
-status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
     if (error == hal::Error::UNSUPPORTED) {
@@ -833,7 +822,7 @@
 }
 
 status_t HWComposer::getSupportedContentTypes(
-        DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+        PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
             mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
@@ -843,7 +832,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) {
+status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
     if (error == hal::Error::UNSUPPORTED) {
@@ -865,7 +854,8 @@
     result.append(mComposer->dumpDebugInfo());
 }
 
-std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const {
+std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId(
+        hal::HWDisplayId hwcDisplayId) const {
     if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
         it != mPhysicalDisplayIdMap.end()) {
         return it->second;
@@ -873,7 +863,8 @@
     return {};
 }
 
-std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
+        PhysicalDisplayId displayId) const {
     if (const auto it = mDisplayData.find(displayId);
         it != mDisplayData.end() && !it->second.isVirtual) {
         return it->second.hwcDisplay->getId();
@@ -883,11 +874,6 @@
 
 bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
                                             bool hasDisplayIdentificationData) const {
-    if (isUsingVrComposer() && mInternalHwcDisplayId) {
-        ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
-        return true;
-    }
-
     if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
         ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
               hwcDisplayId);
@@ -909,6 +895,16 @@
         info = DisplayIdentificationInfo{.id = *displayId,
                                          .name = std::string(),
                                          .deviceProductInfo = std::nullopt};
+        if (mUpdateDeviceProductInfoOnHotplugReconnect) {
+            uint8_t port;
+            DisplayIdentificationData data;
+            getDisplayIdentificationData(hwcDisplayId, &port, &data);
+            if (auto newInfo = parseDisplayIdentificationData(port, data)) {
+                info->deviceProductInfo = std::move(newInfo->deviceProductInfo);
+            } else {
+                ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+            }
+        }
     } else {
         uint8_t port;
         DisplayIdentificationData data;
@@ -937,7 +933,7 @@
                 port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL;
             }
 
-            return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+            return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
                                              .name = isPrimary ? "Internal display"
                                                                : "External display",
                                              .deviceProductInfo = std::nullopt};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index c355ebd..d8af5bf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -38,6 +38,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
+#include "DisplayIdGenerator.h"
 #include "DisplayIdentification.h"
 #include "HWC2.h"
 #include "Hal.h"
@@ -64,6 +65,8 @@
     const uint32_t id;
 };
 
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
 class HWComposer {
 public:
     struct DeviceRequestedChanges {
@@ -82,23 +85,22 @@
 
     virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
 
-    virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+    virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const = 0;
 
-    virtual bool hasCapability(hal::Capability capability) const = 0;
-    virtual bool hasDisplayCapability(DisplayId displayId,
-                                      hal::DisplayCapability capability) const = 0;
+    virtual bool hasCapability(hal::Capability) const = 0;
+    virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
-                                                            ui::PixelFormat* format) = 0;
+                                                            ui::PixelFormat*) = 0;
 
-    virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0;
+    virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
 
     // Attempts to create a new layer on this display
-    virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
+    virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
     // Destroy a previously created layer
-    virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
+    virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
 
     // Gets any required composition change requests from the HWC device.
     //
@@ -108,107 +110,103 @@
     // with fewer handshakes, but this does not work if client composition is
     // expected.
     virtual status_t getDeviceCompositionChanges(
-            DisplayId, bool frameUsesClientComposition,
+            HalDisplayId, bool frameUsesClientComposition,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
-    virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
-                                     const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
-                                     ui::Dataspace dataspace) = 0;
+    virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+                                     const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
-    virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
+    virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
 
     // set power mode
-    virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0;
+    virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
 
     // Sets a color transform to be applied to the result of composition
-    virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
+    virtual status_t setColorTransform(HalDisplayId, const mat4& transform) = 0;
 
-    // reset state when an external, non-virtual display is disconnected
-    virtual void disconnectDisplay(DisplayId displayId) = 0;
+    // reset state when a display is disconnected
+    virtual void disconnectDisplay(HalDisplayId) = 0;
 
     // get the present fence received from the last call to present.
-    virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
+    virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
 
     // Get last release fence for the given layer
-    virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0;
+    virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
 
     // Set the output buffer and acquire fence for a virtual display.
-    // Returns INVALID_OPERATION if displayId is not a virtual display.
-    virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+    virtual status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& buffer) = 0;
 
     // After SurfaceFlinger has retrieved the release fences for all the frames,
     // it can call this to clear the shared pointers in the release fence map
-    virtual void clearReleaseFences(DisplayId displayId) = 0;
+    virtual void clearReleaseFences(HalDisplayId) = 0;
 
     // Fetches the HDR capabilities of the given display
-    virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
+    virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
 
-    virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
+    virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
 
     // Returns the available RenderIntent of the given display.
-    virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
-                                                           ui::ColorMode colorMode) const = 0;
+    virtual std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const = 0;
 
-    virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+    virtual mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) = 0;
 
     // Returns the attributes of the color sampling engine.
-    virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
-                                                           ui::PixelFormat* outFormat,
+    virtual status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
                                                            ui::Dataspace* outDataspace,
                                                            uint8_t* outComponentMask) = 0;
-    virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+    virtual status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled,
                                                       uint8_t componentMask,
                                                       uint64_t maxFrames) = 0;
-    virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
-                                               uint64_t timestamp,
+    virtual status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                                DisplayedFrameStats* outStats) = 0;
 
     // Sets the brightness of a display.
-    virtual std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) = 0;
+    virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
 
     // Events handling ---------------------------------------------------------
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
     // display), or std::nullopt if hotplug event was ignored.
     // This function is called from SurfaceFlinger.
-    virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
-                                                               hal::Connection connection) = 0;
+    virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId,
+                                                               hal::Connection) = 0;
 
-    virtual bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) = 0;
-    virtual void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) = 0;
+    // If true we'll update the DeviceProductInfo on subsequent hotplug connected events.
+    // TODO(b/157555476): Remove when the framework has proper support for headless mode
+    virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
 
-    virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
-    virtual bool isConnected(DisplayId displayId) const = 0;
+    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
+
+    virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0;
+    virtual bool isConnected(PhysicalDisplayId) const = 0;
 
     // Non-const because it can update configMap inside of mDisplayData
     virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
-            DisplayId displayId) const = 0;
+            PhysicalDisplayId) const = 0;
 
     virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
-            DisplayId displayId) const = 0;
-    virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
+            PhysicalDisplayId) const = 0;
+    virtual int getActiveConfigIndex(PhysicalDisplayId) const = 0;
 
-    virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
+    virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
 
-    virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
-                                        ui::RenderIntent renderIntent) = 0;
-
-    virtual bool isUsingVrComposer() const = 0;
+    virtual status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode mode,
+                                        ui::RenderIntent) = 0;
 
     // Composer 2.4
-    virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
-    virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
-    virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+    virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
+    virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
     virtual status_t setActiveConfigWithConstraints(
-            DisplayId displayId, size_t configId,
-            const hal::VsyncPeriodChangeConstraints& constraints,
+            PhysicalDisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
-    virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+    virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
     virtual status_t getSupportedContentTypes(
-            DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
-    virtual status_t setContentType(DisplayId displayId, hal::ContentType contentType) = 0;
+            PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0;
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
@@ -221,8 +219,8 @@
     virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
     virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
 
-    virtual std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const = 0;
-    virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+    virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
+    virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
 };
 
 namespace impl {
@@ -236,118 +234,112 @@
 
     void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
 
-    bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+    bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
                                       DisplayIdentificationData* outData) const override;
 
-    bool hasCapability(hal::Capability capability) const override;
-    bool hasDisplayCapability(DisplayId displayId,
-                              hal::DisplayCapability capability) const override;
+    bool hasCapability(hal::Capability) const override;
+    bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
-                                                    ui::PixelFormat* format) override;
+                                                    ui::PixelFormat*) override;
 
     // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
-    void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) override;
+    void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
 
     // Attempts to create a new layer on this display
-    HWC2::Layer* createLayer(DisplayId displayId) override;
+    HWC2::Layer* createLayer(HalDisplayId) override;
     // Destroy a previously created layer
-    void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+    void destroyLayer(HalDisplayId, HWC2::Layer*) override;
 
     status_t getDeviceCompositionChanges(
-            DisplayId, bool frameUsesClientComposition,
+            HalDisplayId, bool frameUsesClientComposition,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
-    status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
-                             const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
+    status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+                             const sp<GraphicBuffer>& target, ui::Dataspace) override;
 
     // Present layers to the display and read releaseFences.
-    status_t presentAndGetReleaseFences(DisplayId displayId) override;
+    status_t presentAndGetReleaseFences(HalDisplayId) override;
 
     // set power mode
-    status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override;
+    status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
 
     // Sets a color transform to be applied to the result of composition
-    status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
+    status_t setColorTransform(HalDisplayId, const mat4& transform) override;
 
-    // reset state when an external, non-virtual display is disconnected
-    void disconnectDisplay(DisplayId displayId) override;
+    // reset state when a display is disconnected
+    void disconnectDisplay(HalDisplayId) override;
 
     // get the present fence received from the last call to present.
-    sp<Fence> getPresentFence(DisplayId displayId) const override;
+    sp<Fence> getPresentFence(HalDisplayId) const override;
 
     // Get last release fence for the given layer
-    sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+    sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
 
     // Set the output buffer and acquire fence for a virtual display.
-    // Returns INVALID_OPERATION if displayId is not a virtual display.
-    status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+    status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& buffer) override;
 
     // After SurfaceFlinger has retrieved the release fences for all the frames,
     // it can call this to clear the shared pointers in the release fence map
-    void clearReleaseFences(DisplayId displayId) override;
+    void clearReleaseFences(HalDisplayId) override;
 
     // Fetches the HDR capabilities of the given display
-    status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+    status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
 
-    int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+    int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
 
     // Returns the available RenderIntent of the given display.
-    std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
-                                                   ui::ColorMode colorMode) const override;
+    std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const override;
 
-    mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+    mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) override;
 
     // Returns the attributes of the color sampling engine.
-    status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+    status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
                                                    ui::Dataspace* outDataspace,
                                                    uint8_t* outComponentMask) override;
-    status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
-                                              uint8_t componentMask, uint64_t maxFrames) override;
-    status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+    status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask,
+                                              uint64_t maxFrames) override;
+    status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
 
     // Events handling ---------------------------------------------------------
 
-    // Returns stable display ID (and display name on connection of new or previously disconnected
+    // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected
     // display), or std::nullopt if hotplug event was ignored.
-    std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
-                                                       hal::Connection connection) override;
+    std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
 
-    bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) override;
-    void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) override;
+    bool updatesDeviceProductInfoOnHotplugReconnect() const override;
 
-    nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
-    bool isConnected(DisplayId displayId) const override;
+    bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
+
+    nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override;
+    bool isConnected(PhysicalDisplayId) const override;
 
     // Non-const because it can update configMap inside of mDisplayData
     std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
-            DisplayId displayId) const override;
+            PhysicalDisplayId) const override;
 
-    std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
-            DisplayId displayId) const override;
-    int getActiveConfigIndex(DisplayId displayId) const override;
+    std::shared_ptr<const HWC2::Display::Config> getActiveConfig(PhysicalDisplayId) const override;
+    int getActiveConfigIndex(PhysicalDisplayId) const override;
 
-    std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+    std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
 
-    status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
-                                ui::RenderIntent renderIntent) override;
-
-    bool isUsingVrComposer() const override;
+    status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
 
     // Composer 2.4
-    DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
-    bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
-    nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
-    status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
-                                            const hal::VsyncPeriodChangeConstraints& constraints,
+    DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
+    bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
+    nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const override;
+    status_t setActiveConfigWithConstraints(PhysicalDisplayId, size_t configId,
+                                            const hal::VsyncPeriodChangeConstraints&,
                                             hal::VsyncPeriodChangeTimeline* outTimeline) override;
-    status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
-    status_t getSupportedContentTypes(DisplayId displayId, std::vector<hal::ContentType>*) override;
-    status_t setContentType(DisplayId displayId, hal::ContentType) override;
+    status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
+    status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
+    status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
 
@@ -364,17 +356,16 @@
         return mExternalHwcDisplayId;
     }
 
-    std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const override;
-    std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const override;
+    std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
+    std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
 
 private:
     // For unit tests
     friend TestableSurfaceFlinger;
 
-    std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId hwcDisplayId);
-    std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId hwcDisplayId);
-    bool shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
-                                    bool hasDisplayIdentificationData) const;
+    std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
+    std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
+    bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
 
     void loadCapabilities();
     void loadLayerMetadataSupport();
@@ -402,21 +393,21 @@
         nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
     };
 
-    std::unordered_map<DisplayId, DisplayData> mDisplayData;
+    std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
 
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<hal::Capability> mCapabilities;
     std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
     bool mRegisteredCallback = false;
 
-    std::unordered_map<hal::HWDisplayId, DisplayId> mPhysicalDisplayIdMap;
+    std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
     std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
     std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
-    std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
-    uint32_t mNextVirtualDisplayId = 0;
-    uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
+    RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
+
+    const bool mUpdateDeviceProductInfoOnHotplugReconnect;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1d8179c..4b4c050 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -85,6 +85,7 @@
 
     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
+        std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
             return;
@@ -108,6 +109,7 @@
     }
 
     if (mSendUpdateImminent.load()) {
+        std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
             return;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 34e63e7..95eb0e2 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -19,6 +19,8 @@
 #include <atomic>
 #include <unordered_set>
 
+#include <utils/Mutex.h>
+
 #include "../Scheduler/OneShotTimer.h"
 #include "DisplayIdentification.h"
 
@@ -56,10 +58,11 @@
     void notifyDisplayUpdateImminent() override;
 
 private:
-    HalWrapper* getPowerHal();
+    HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
+    bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
+    std::mutex mPowerHalMutex;
 
     std::atomic_bool mBootFinished = false;
-    bool mReconnectPowerHal = false;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index fba3261..247ee23 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -57,8 +57,7 @@
     }
 }
 
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
-                                             const std::optional<DisplayId>& displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
                                              const sp<IGraphicBufferProducer>& sink,
                                              const sp<IGraphicBufferProducer>& bqProducer,
                                              const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -125,7 +124,7 @@
 }
 
 status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -139,7 +138,7 @@
 }
 
 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -187,7 +186,7 @@
 }
 
 status_t VirtualDisplaySurface::advanceFrame() {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -219,9 +218,11 @@
             mFbProducerSlot, fbBuffer.get(),
             mOutputProducerSlot, outBuffer.get());
 
+    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+    LOG_FATAL_IF(!halDisplayId);
     // At this point we know the output buffer acquire fence,
     // so update HWC state with it.
-    mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer);
+    mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
@@ -230,7 +231,7 @@
         mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+        result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
                                       ui::Dataspace::UNKNOWN);
     }
 
@@ -238,7 +239,8 @@
 }
 
 void VirtualDisplaySurface::onFrameCommitted() {
-    if (!mDisplayId) {
+    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+    if (!halDisplayId) {
         return;
     }
 
@@ -246,7 +248,7 @@
             "Unexpected onFrameCommitted() in %s state", dbgStateStr());
     mDbgState = DBG_STATE_IDLE;
 
-    sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
+    sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
     if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
         // release the scratch buffer back to the pool
         Mutex::Autolock lock(mMutex);
@@ -301,7 +303,7 @@
 
 status_t VirtualDisplaySurface::requestBuffer(int pslot,
         sp<GraphicBuffer>* outBuf) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
@@ -323,7 +325,7 @@
 
 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
         PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
-    LOG_FATAL_IF(!mDisplayId);
+    LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
 
     status_t result =
             mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -369,7 +371,7 @@
                                               PixelFormat format, uint64_t usage,
                                               uint64_t* outBufferAge,
                                               FrameEventHistoryDelta* outTimestamps) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
                                                    outTimestamps);
     }
@@ -456,7 +458,7 @@
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
         const QueueBufferInput& input, QueueBufferOutput* output) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
@@ -514,7 +516,7 @@
 
 status_t VirtualDisplaySurface::cancelBuffer(int pslot,
         const sp<Fence>& fence) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
@@ -626,7 +628,7 @@
 }
 
 status_t VirtualDisplaySurface::refreshOutputBuffer() {
-    LOG_FATAL_IF(!mDisplayId);
+    LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
 
     if (mOutputProducerSlot >= 0) {
         mSource[SOURCE_SINK]->cancelBuffer(
@@ -645,7 +647,9 @@
     // until after GPU calls queueBuffer(). So here we just set the buffer
     // (for use in HWC prepare) but not the fence; we'll call this again with
     // the proper fence once we have it.
-    result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+    LOG_FATAL_IF(!halDisplayId);
+    result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
                                   mProducerBuffers[mOutputProducerSlot]);
 
     return result;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 3cbad8f..1974625 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -24,6 +24,7 @@
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <ui/DisplayId.h>
 
 #include "DisplayIdentification.h"
 
@@ -77,8 +78,7 @@
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
-    VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
-                          const sp<IGraphicBufferProducer>& sink,
+    VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
                           const sp<IGraphicBufferProducer>& bqProducer,
                           const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
 
@@ -86,7 +86,7 @@
     // DisplaySurface interface
     //
     virtual status_t beginFrame(bool mustRecompose);
-    virtual status_t prepareFrame(CompositionType compositionType);
+    virtual status_t prepareFrame(CompositionType);
     virtual status_t advanceFrame();
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
@@ -104,25 +104,22 @@
     virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual status_t setAsyncMode(bool async);
-    virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+    virtual status_t dequeueBuffer(int* pslot, sp<Fence>*, uint32_t w, uint32_t h, PixelFormat,
+                                   uint64_t usage, uint64_t* outBufferAge,
                                    FrameEventHistoryDelta* outTimestamps);
     virtual status_t detachBuffer(int slot);
-    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence);
-    virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
-    virtual status_t queueBuffer(int pslot,
-            const QueueBufferInput& input, QueueBufferOutput* output);
-    virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence);
+    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+    virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>&);
+    virtual status_t queueBuffer(int pslot, const QueueBufferInput&, QueueBufferOutput*);
+    virtual status_t cancelBuffer(int pslot, const sp<Fence>&);
     virtual int query(int what, int* value);
-    virtual status_t connect(const sp<IProducerListener>& listener,
-            int api, bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api, DisconnectMode mode);
+    virtual status_t connect(const sp<IProducerListener>&, int api, bool producerControlledByApp,
+                             QueueBufferOutput*);
+    virtual status_t disconnect(int api, DisconnectMode);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
-    virtual void allocateBuffers(uint32_t width, uint32_t height,
-            PixelFormat format, uint64_t usage);
+    virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat, uint64_t usage);
     virtual status_t allowAllocation(bool allow);
-    virtual status_t setGenerationNumber(uint32_t generationNumber);
+    virtual status_t setGenerationNumber(uint32_t);
     virtual String8 getConsumerName() const override;
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
@@ -135,10 +132,9 @@
     //
     // Utility methods
     //
-    static Source fbSourceForCompositionType(CompositionType type);
-    status_t dequeueBuffer(Source source, PixelFormat format, uint64_t usage,
-            int* sslot, sp<Fence>* fence);
-    void updateQueueBufferOutput(QueueBufferOutput&& qbo);
+    static Source fbSourceForCompositionType(CompositionType);
+    status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
+    void updateQueueBufferOutput(QueueBufferOutput&&);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
 
@@ -148,14 +144,14 @@
     // internally in the VirtualDisplaySurface. To minimize the number of times
     // a producer slot switches which source it comes from, we map source slot
     // numbers to producer slot numbers differently for each source.
-    static int mapSource2ProducerSlot(Source source, int sslot);
-    static int mapProducer2SourceSlot(Source source, int pslot);
+    static int mapSource2ProducerSlot(Source, int sslot);
+    static int mapProducer2SourceSlot(Source, int pslot);
 
     //
     // Immutable after construction
     //
     HWComposer& mHwc;
-    const std::optional<DisplayId> mDisplayId;
+    const VirtualDisplayId mDisplayId;
     const std::string mDisplayName;
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h
new file mode 100644
index 0000000..e7c69a8
--- /dev/null
+++ b/services/surfaceflinger/DisplayIdGenerator.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/DisplayId.h>
+
+#include <limits>
+#include <optional>
+#include <random>
+#include <unordered_set>
+
+#include <log/log.h>
+
+namespace android {
+
+template <typename T>
+class DisplayIdGenerator {
+public:
+    virtual std::optional<T> nextId() = 0;
+    virtual void markUnused(T id) = 0;
+
+protected:
+    ~DisplayIdGenerator() {}
+};
+
+template <typename T>
+class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> {
+public:
+    explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
+          : mMaxIdsCount(maxIdsCount) {}
+
+    std::optional<T> nextId() override {
+        if (mUsedIds.size() >= mMaxIdsCount) {
+            return std::nullopt;
+        }
+
+        constexpr int kMaxAttempts = 1000;
+
+        for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
+            const auto baseId = mDistribution(mGenerator);
+            const T id(baseId);
+            if (mUsedIds.count(id) == 0) {
+                mUsedIds.insert(id);
+                return id;
+            }
+        }
+
+        LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts);
+    }
+
+    void markUnused(T id) override { mUsedIds.erase(id); }
+
+private:
+    const size_t mMaxIdsCount;
+
+    std::unordered_set<T> mUsedIds;
+    std::default_random_engine mGenerator{std::random_device()()};
+    std::uniform_int_distribution<typename T::BaseId> mDistribution;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
new file mode 100644
index 0000000..20486e0
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayRenderArea.h"
+#include "DisplayDevice.h"
+
+namespace android {
+namespace {
+
+RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
+                                                 const DisplayDevice& display) {
+    if (!useIdentityTransform) {
+        return RenderArea::RotationFlags::ROT_0;
+    }
+
+    return ui::Transform::toRotationFlags(display.getOrientation());
+}
+
+} // namespace
+
+std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
+                                                      const Rect& sourceCrop, ui::Size reqSize,
+                                                      ui::Dataspace reqDataSpace,
+                                                      bool useIdentityTransform,
+                                                      bool allowSecureLayers) {
+    if (auto display = displayWeak.promote()) {
+        // Using new to access a private constructor.
+        return std::unique_ptr<DisplayRenderArea>(
+                new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
+                                      useIdentityTransform, allowSecureLayers));
+    }
+    return nullptr;
+}
+
+DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
+                                     ui::Size reqSize, ui::Dataspace reqDataSpace,
+                                     bool useIdentityTransform, bool allowSecureLayers)
+      : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(),
+                   allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
+        mDisplay(std::move(display)),
+        mSourceCrop(sourceCrop) {}
+
+const ui::Transform& DisplayRenderArea::getTransform() const {
+    return mTransform;
+}
+
+Rect DisplayRenderArea::getBounds() const {
+    return mDisplay->getBounds();
+}
+
+int DisplayRenderArea::getHeight() const {
+    return mDisplay->getHeight();
+}
+
+int DisplayRenderArea::getWidth() const {
+    return mDisplay->getWidth();
+}
+
+bool DisplayRenderArea::isSecure() const {
+    return mAllowSecureLayers && mDisplay->isSecure();
+}
+
+sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
+    return mDisplay;
+}
+
+bool DisplayRenderArea::needsFiltering() const {
+    // check if the projection from the logical render area
+    // to the physical render area requires filtering
+    const Rect& sourceCrop = getSourceCrop();
+    int width = sourceCrop.width();
+    int height = sourceCrop.height();
+    if (getRotationFlags() & ui::Transform::ROT_90) {
+        std::swap(width, height);
+    }
+    return width != getReqWidth() || height != getReqHeight();
+}
+
+Rect DisplayRenderArea::getSourceCrop() const {
+    // use the projected display viewport by default.
+    if (mSourceCrop.isEmpty()) {
+        return mDisplay->getLayerStackSpaceRect();
+    }
+
+    // Correct for the orientation when the screen capture request contained
+    // useIdentityTransform. This will cause the rotation flag to be non 0 since
+    // it needs to rotate based on the screen orientation to allow the screenshot
+    // to be taken in the ROT_0 orientation
+    const auto flags = getRotationFlags();
+    int width = mDisplay->getLayerStackSpaceRect().getWidth();
+    int height = mDisplay->getLayerStackSpaceRect().getHeight();
+    ui::Transform rotation;
+    rotation.set(flags, width, height);
+    return rotation.transform(mSourceCrop);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
new file mode 100644
index 0000000..3478fc1
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+
+class DisplayRenderArea : public RenderArea {
+public:
+    static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
+                                              ui::Size reqSize, ui::Dataspace,
+                                              bool useIdentityTransform,
+                                              bool allowSecureLayers = true);
+
+    const ui::Transform& getTransform() const override;
+    Rect getBounds() const override;
+    int getHeight() const override;
+    int getWidth() const override;
+    bool isSecure() const override;
+    sp<const DisplayDevice> getDisplayDevice() const override;
+    bool needsFiltering() const override;
+    Rect getSourceCrop() const override;
+
+private:
+    DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
+                      ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+
+    const sp<const DisplayDevice> mDisplay;
+    const Rect mSourceCrop;
+    const ui::Transform mTransform;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
new file mode 100644
index 0000000..e075d3e
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -0,0 +1,18 @@
+cc_library_static {
+    name: "libframetimeline",
+    defaults: ["surfaceflinger_defaults"],
+    srcs: [
+        "FrameTimeline.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.composer@2.4",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libgui",
+        "libtimestats",
+        "libui",
+        "libutils",
+    ],
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
new file mode 100644
index 0000000..bd87482
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FrameTimeline"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTimeline.h"
+#include <android-base/stringprintf.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cinttypes>
+#include <numeric>
+
+namespace android::frametimeline::impl {
+
+using base::StringAppendF;
+
+void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
+               const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "\t\t");
+    StringAppendF(&result, "    Start time\t\t|");
+    StringAppendF(&result, "    End time\t\t|");
+    StringAppendF(&result, "    Present time\n");
+    if (predictionState == PredictionState::Valid) {
+        // Dump the Predictions only if they are valid
+        StringAppendF(&result, "%s", indent.c_str());
+        StringAppendF(&result, "Expected\t|");
+        std::chrono::nanoseconds startTime(predictions.startTime - baseTime);
+        std::chrono::nanoseconds endTime(predictions.endTime - baseTime);
+        std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime);
+        StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n",
+                      std::chrono::duration<double, std::milli>(startTime).count(),
+                      std::chrono::duration<double, std::milli>(endTime).count(),
+                      std::chrono::duration<double, std::milli>(presentTime).count());
+    }
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Actual  \t|");
+
+    if (actuals.startTime == 0) {
+        StringAppendF(&result, "\t\tN/A\t|");
+    } else {
+        std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime));
+        StringAppendF(&result, "\t%10.2f\t|",
+                      std::chrono::duration<double, std::milli>(startTime).count());
+    }
+    if (actuals.endTime == 0) {
+        StringAppendF(&result, "\t\tN/A\t|");
+    } else {
+        std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
+        StringAppendF(&result, "\t%10.2f\t|",
+                      std::chrono::duration<double, std::milli>(endTime).count());
+    }
+    if (actuals.presentTime == 0) {
+        StringAppendF(&result, "\t\tN/A\n");
+    } else {
+        std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime));
+        StringAppendF(&result, "\t%10.2f\n",
+                      std::chrono::duration<double, std::milli>(presentTime).count());
+    }
+
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------\n");
+}
+
+std::string toString(PredictionState predictionState) {
+    switch (predictionState) {
+        case PredictionState::Valid:
+            return "Valid";
+        case PredictionState::Expired:
+            return "Expired";
+        case PredictionState::None:
+        default:
+            return "None";
+    }
+}
+
+std::string toString(TimeStats::JankType jankType) {
+    switch (jankType) {
+        case TimeStats::JankType::None:
+            return "None";
+        case TimeStats::JankType::Display:
+            return "Composer/Display - outside SF and App";
+        case TimeStats::JankType::SurfaceFlingerDeadlineMissed:
+            return "SurfaceFlinger Deadline Missed";
+        case TimeStats::JankType::AppDeadlineMissed:
+            return "App Deadline Missed";
+        case TimeStats::JankType::PredictionExpired:
+            return "Prediction Expired";
+        case TimeStats::JankType::SurfaceFlingerEarlyLatch:
+            return "SurfaceFlinger Early Latch";
+        default:
+            return "Unclassified";
+    }
+}
+
+std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
+    std::vector<std::string> jankInfo;
+
+    if (jankMetadata & EarlyStart) {
+        jankInfo.emplace_back("Early Start");
+    } else if (jankMetadata & LateStart) {
+        jankInfo.emplace_back("Late Start");
+    }
+
+    if (jankMetadata & EarlyFinish) {
+        jankInfo.emplace_back("Early Finish");
+    } else if (jankMetadata & LateFinish) {
+        jankInfo.emplace_back("Late Finish");
+    }
+
+    if (jankMetadata & EarlyPresent) {
+        jankInfo.emplace_back("Early Present");
+    } else if (jankMetadata & LatePresent) {
+        jankInfo.emplace_back("Late Present");
+    }
+    // TODO(b/169876734): add GPU composition metadata here
+
+    if (jankInfo.empty()) {
+        return "None";
+    }
+    return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+                           [](const std::string& l, const std::string& r) {
+                               return l.empty() ? r : l + ", " + r;
+                           });
+}
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    const int64_t assignedToken = mCurrentToken++;
+    mPredictions[assignedToken] = predictions;
+    mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
+    flushTokens(systemTime());
+    return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    flushTokens(systemTime());
+    auto predictionsIterator = mPredictions.find(token);
+    if (predictionsIterator != mPredictions.end()) {
+        return predictionsIterator->second;
+    }
+    return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+    for (size_t i = 0; i < mTokens.size(); i++) {
+        if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
+            mPredictions.erase(mTokens[i].first);
+            mTokens.erase(mTokens.begin() + static_cast<int>(i));
+            --i;
+        } else {
+            // Tokens are ordered by time. If i'th token is within the retention time, then the
+            // i+1'th token will also be within retention time.
+            break;
+        }
+    }
+}
+
+SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName,
+                           std::string debugName, PredictionState predictionState,
+                           frametimeline::TimelineItem&& predictions)
+      : mOwnerPid(ownerPid),
+        mOwnerUid(ownerUid),
+        mLayerName(std::move(layerName)),
+        mDebugName(std::move(debugName)),
+        mPresentState(PresentState::Unknown),
+        mPredictionState(predictionState),
+        mPredictions(predictions),
+        mActuals({0, 0, 0}),
+        mActualQueueTime(0),
+        mJankType(TimeStats::JankType::None),
+        mJankMetadata(0) {}
+
+void SurfaceFrame::setPresentState(PresentState state) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentState = state;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mPresentState;
+}
+
+TimelineItem SurfaceFrame::getActuals() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mActuals;
+}
+
+nsecs_t SurfaceFrame::getActualQueueTime() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mActualQueueTime;
+}
+
+void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.startTime = actualStartTime;
+}
+
+void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActualQueueTime = actualQueueTime;
+}
+void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+}
+
+void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.presentTime = presentTime;
+}
+
+void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mJankType = jankType;
+    mJankMetadata = jankMetadata;
+}
+
+TimeStats::JankType SurfaceFrame::getJankType() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mJankType;
+}
+
+nsecs_t SurfaceFrame::getBaseTime() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+    if (mPredictionState == PredictionState::Valid) {
+        baseTime = std::min(baseTime, mPredictions.startTime);
+    }
+    if (mActuals.startTime != 0) {
+        baseTime = std::min(baseTime, mActuals.startTime);
+    }
+    baseTime = std::min(baseTime, mActuals.endTime);
+    return baseTime;
+}
+
+std::string presentStateToString(SurfaceFrame::PresentState presentState) {
+    using PresentState = SurfaceFrame::PresentState;
+    switch (presentState) {
+        case PresentState::Presented:
+            return "Presented";
+        case PresentState::Dropped:
+            return "Dropped";
+        case PresentState::Unknown:
+        default:
+            return "Unknown";
+    }
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Layer - %s", mDebugName.c_str());
+    if (mJankType != TimeStats::JankType::None) {
+        // Easily identify a janky Surface Frame in the dump
+        StringAppendF(&result, " [*] ");
+    }
+    StringAppendF(&result, "\n");
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Jank Metadata: %s\n",
+                  jankMetadataBitmaskToString(mJankMetadata).c_str());
+    dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
+      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+        mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+        mTimeStats(std::move(timeStats)) {}
+
+FrameTimeline::DisplayFrame::DisplayFrame()
+      : surfaceFlingerPredictions(TimelineItem()),
+        surfaceFlingerActuals(TimelineItem()),
+        predictionState(PredictionState::None),
+        jankType(TimeStats::JankType::None),
+        jankMetadata(0) {
+    this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+        pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+        std::optional<int64_t> token) {
+    ATRACE_CALL();
+    if (!token) {
+        return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+                                                    std::move(debugName), PredictionState::None,
+                                                    TimelineItem());
+    }
+    std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
+    if (predictions) {
+        return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+                                                    std::move(debugName), PredictionState::Valid,
+                                                    std::move(*predictions));
+    }
+    return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+                                                std::move(debugName), PredictionState::Expired,
+                                                TimelineItem());
+}
+
+void FrameTimeline::addSurfaceFrame(
+        std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
+        SurfaceFrame::PresentState state) {
+    ATRACE_CALL();
+    surfaceFrame->setPresentState(state);
+    std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame(
+            static_cast<impl::SurfaceFrame*>(surfaceFrame.release()));
+    std::lock_guard<std::mutex> lock(mMutex);
+    mCurrentDisplayFrame->surfaceFrames.push_back(std::move(implSurfaceFrame));
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+    ATRACE_CALL();
+    const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!prediction) {
+        mCurrentDisplayFrame->predictionState = PredictionState::Expired;
+    } else {
+        mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
+        mCurrentDisplayFrame->predictionState = PredictionState::Valid;
+    }
+    mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+                                 const std::shared_ptr<FenceTime>& presentFence) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+    mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
+    flushPendingPresentFences();
+    finalizeCurrentDisplayFrame();
+}
+
+void FrameTimeline::flushPendingPresentFences() {
+    for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+        const auto& pendingPresentFence = mPendingPresentFences[i];
+        nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+        if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
+            signalTime = pendingPresentFence.first->getSignalTime();
+            if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+                continue;
+            }
+        }
+        if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+            int32_t totalJankReasons = TimeStats::JankType::None;
+            auto& displayFrame = pendingPresentFence.second;
+            displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+
+            // Jank Analysis for DisplayFrame
+            const auto& sfActuals = displayFrame->surfaceFlingerActuals;
+            const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
+            if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
+                displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
+                        ? LatePresent
+                        : EarlyPresent;
+            }
+            if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
+                if (sfActuals.endTime > sfPredictions.endTime) {
+                    displayFrame->jankMetadata |= LateFinish;
+                } else {
+                    displayFrame->jankMetadata |= EarlyFinish;
+                }
+
+                if ((displayFrame->jankMetadata & EarlyFinish) &&
+                    (displayFrame->jankMetadata & EarlyPresent)) {
+                    displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
+                } else if ((displayFrame->jankMetadata & LateFinish) &&
+                           (displayFrame->jankMetadata & LatePresent)) {
+                    displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed;
+                } else if (displayFrame->jankMetadata & EarlyPresent ||
+                           displayFrame->jankMetadata & LatePresent) {
+                    // Cases where SF finished early but frame was presented late and vice versa
+                    displayFrame->jankType = TimeStats::JankType::Display;
+                }
+            }
+
+            if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
+                displayFrame->jankMetadata |=
+                        sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
+            }
+
+            totalJankReasons |= displayFrame->jankType;
+
+            for (auto& surfaceFrame : displayFrame->surfaceFrames) {
+                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
+                    // Only presented SurfaceFrames need to be updated
+                    surfaceFrame->setActualPresentTime(signalTime);
+
+                    // Jank Analysis for SurfaceFrame
+                    const auto& predictionState = surfaceFrame->getPredictionState();
+                    if (predictionState == PredictionState::Expired) {
+                        // Jank analysis cannot be done on apps that don't use predictions
+                        surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
+                        continue;
+                    } else if (predictionState == PredictionState::Valid) {
+                        const auto& actuals = surfaceFrame->getActuals();
+                        const auto& predictions = surfaceFrame->getPredictions();
+                        int32_t jankMetadata = 0;
+                        TimeStats::JankType jankType = TimeStats::JankType::None;
+                        if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
+                            jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
+                                                                                  : EarlyFinish;
+                        }
+                        if (std::abs(actuals.presentTime - predictions.presentTime) >
+                            kPresentThreshold) {
+                            jankMetadata |= actuals.presentTime > predictions.presentTime
+                                    ? LatePresent
+                                    : EarlyPresent;
+                        }
+                        if (jankMetadata & EarlyPresent) {
+                            jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
+                        } else if (jankMetadata & LatePresent) {
+                            if (jankMetadata & EarlyFinish) {
+                                // TODO(b/169890654): Classify this properly
+                                jankType = TimeStats::JankType::Display;
+                            } else {
+                                jankType = TimeStats::JankType::AppDeadlineMissed;
+                            }
+                        }
+
+                        totalJankReasons |= jankType;
+                        mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
+                                                         surfaceFrame->getName(),
+                                                         jankType | displayFrame->jankType);
+                        surfaceFrame->setJankInfo(jankType, jankMetadata);
+                    }
+                }
+            }
+
+            mTimeStats->incrementJankyFrames(totalJankReasons);
+        }
+
+        mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+        --i;
+    }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+    while (mDisplayFrames.size() >= mMaxDisplayFrames) {
+        // We maintain only a fixed number of frames' data. Pop older frames
+        mDisplayFrames.pop_front();
+    }
+    mDisplayFrames.push_back(mCurrentDisplayFrame);
+    mCurrentDisplayFrame.reset();
+    mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+}
+
+nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
+    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+    if (displayFrame->predictionState == PredictionState::Valid) {
+        baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
+    }
+    baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
+    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+        nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
+        if (surfaceFrameBaseTime != 0) {
+            baseTime = std::min(baseTime, surfaceFrameBaseTime);
+        }
+    }
+    return baseTime;
+}
+
+void FrameTimeline::dumpDisplayFrame(std::string& result,
+                                     const std::shared_ptr<DisplayFrame>& displayFrame,
+                                     nsecs_t baseTime) {
+    if (displayFrame->jankType != TimeStats::JankType::None) {
+        // Easily identify a janky Display Frame in the dump
+        StringAppendF(&result, " [*] ");
+    }
+    StringAppendF(&result, "\n");
+    StringAppendF(&result, "Prediction State : %s\n",
+                  toString(displayFrame->predictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
+    StringAppendF(&result, "Jank Metadata: %s\n",
+                  jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
+    dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
+              "", displayFrame->predictionState, baseTime);
+    StringAppendF(&result, "\n");
+    std::string indent = "    "; // 4 spaces
+    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+        surfaceFrame->dump(result, indent, baseTime);
+    }
+    StringAppendF(&result, "\n");
+}
+void FrameTimeline::dumpAll(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+        dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+    }
+}
+
+void FrameTimeline::dumpJank(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+        const auto& displayFrame = mDisplayFrames[i];
+        if (displayFrame->jankType == TimeStats::JankType::None) {
+            // Check if any Surface Frame has been janky
+            bool isJanky = false;
+            for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+                if (surfaceFrame->getJankType() != TimeStats::JankType::None) {
+                    isJanky = true;
+                    break;
+                }
+            }
+            if (!isJanky) {
+                continue;
+            }
+        }
+        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+        dumpDisplayFrame(result, displayFrame, baseTime);
+    }
+}
+void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
+    ATRACE_CALL();
+    std::unordered_map<std::string, bool> argsMap;
+    for (size_t i = 0; i < args.size(); i++) {
+        argsMap[std::string(String8(args[i]).c_str())] = true;
+    }
+    if (argsMap.count("-jank")) {
+        dumpJank(result);
+    }
+    if (argsMap.count("-all")) {
+        dumpAll(result);
+    }
+}
+
+void FrameTimeline::setMaxDisplayFrames(uint32_t size) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    // The size can either increase or decrease, clear everything, to be consistent
+    mDisplayFrames.clear();
+    mPendingPresentFences.clear();
+    mMaxDisplayFrames = size;
+}
+
+void FrameTimeline::reset() {
+    setMaxDisplayFrames(kDefaultMaxDisplayFrames);
+}
+
+} // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
new file mode 100644
index 0000000..e61567e
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <../TimeStats/TimeStats.h>
+#include <gui/ISurfaceComposer.h>
+#include <ui/FenceTime.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+#include <deque>
+#include <mutex>
+
+namespace android::frametimeline {
+
+enum JankMetadata {
+    // Frame was presented earlier than expected
+    EarlyPresent = 0x1,
+    // Frame was presented later than expected
+    LatePresent = 0x2,
+    // App/SF started earlier than expected
+    EarlyStart = 0x4,
+    // App/SF started later than expected
+    LateStart = 0x8,
+    // App/SF finished work earlier than the deadline
+    EarlyFinish = 0x10,
+    // App/SF finished work later than the deadline
+    LateFinish = 0x20,
+    // SF was in GPU composition
+    GpuComposition = 0x40,
+};
+
+class FrameTimelineTest;
+
+/*
+ * Collection of timestamps that can be used for both predictions and actual times.
+ */
+struct TimelineItem {
+    TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
+                 const nsecs_t presentTime = 0)
+          : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+
+    nsecs_t startTime;
+    nsecs_t endTime;
+    nsecs_t presentTime;
+
+    bool operator==(const TimelineItem& other) const {
+        return startTime == other.startTime && endTime == other.endTime &&
+                presentTime == other.presentTime;
+    }
+
+    bool operator!=(const TimelineItem& other) const { return !(*this == other); }
+};
+
+/*
+ * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
+ * saves these predictions for a short period of time and returns the predictions for a given token,
+ * if it hasn't expired.
+ */
+class TokenManager {
+public:
+    virtual ~TokenManager() = default;
+
+    // Generates a token for the given set of predictions. Stores the predictions for 120ms and
+    // destroys it later.
+    virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+};
+
+enum class PredictionState {
+    Valid,   // Predictions obtained successfully from the TokenManager
+    Expired, // TokenManager no longer has the predictions
+    None,    // Predictions are either not present or didn't come from TokenManager
+};
+
+/*
+ * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
+ * from the app
+ */
+class SurfaceFrame {
+public:
+    enum class PresentState {
+        Presented, // Buffer was latched and presented by SurfaceFlinger
+        Dropped,   // Buffer was dropped by SurfaceFlinger
+        Unknown,   // Initial state, SurfaceFlinger hasn't seen this buffer yet
+    };
+
+    virtual ~SurfaceFrame() = default;
+
+    virtual TimelineItem getPredictions() const = 0;
+    virtual TimelineItem getActuals() const = 0;
+    virtual nsecs_t getActualQueueTime() const = 0;
+    virtual PresentState getPresentState() const = 0;
+    virtual PredictionState getPredictionState() const = 0;
+    virtual pid_t getOwnerPid() const = 0;
+
+    virtual void setPresentState(PresentState state) = 0;
+
+    // Actual timestamps of the app are set individually at different functions.
+    // Start time (if the app provides) and Queue time are accessible after queueing the frame,
+    // whereas Acquire Fence time is available only during latch.
+    virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
+    virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
+    virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+};
+
+/*
+ * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were
+ * presented
+ */
+class FrameTimeline {
+public:
+    virtual ~FrameTimeline() = default;
+    virtual TokenManager* getTokenManager() = 0;
+
+    // Create a new surface frame, set the predictions based on a token and return it to the caller.
+    // Sets the PredictionState of SurfaceFrame.
+    // Debug name is the human-readable debugging string for dumpsys.
+    virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
+            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+            std::optional<int64_t> token) = 0;
+
+    // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
+    // composited into one display frame.
+    virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame,
+                                 SurfaceFrame::PresentState state) = 0;
+
+    // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
+    // the token and sets the actualSfWakeTime for the current DisplayFrame.
+    virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0;
+
+    // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
+    // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
+    // that vsync.
+    virtual void setSfPresent(nsecs_t sfPresentTime,
+                              const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+    // Args:
+    // -jank : Dumps only the Display Frames that are either janky themselves
+    //         or contain janky Surface Frames.
+    // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within
+    virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
+
+    // Sets the max number of display frames that can be stored. Called by SF backdoor.
+    virtual void setMaxDisplayFrames(uint32_t size);
+
+    // Restores the max number of display frames to default. Called by SF backdoor.
+    virtual void reset() = 0;
+};
+
+namespace impl {
+
+using namespace std::chrono_literals;
+
+class TokenManager : public android::frametimeline::TokenManager {
+public:
+    TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
+    ~TokenManager() = default;
+
+    int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
+    std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+
+private:
+    // Friend class for testing
+    friend class android::frametimeline::FrameTimelineTest;
+
+    void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
+
+    std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
+    std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+    int64_t mCurrentToken GUARDED_BY(mMutex);
+    std::mutex mMutex;
+    static constexpr nsecs_t kMaxRetentionTime =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+};
+
+class SurfaceFrame : public android::frametimeline::SurfaceFrame {
+public:
+    SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+                 PredictionState predictionState, TimelineItem&& predictions);
+    ~SurfaceFrame() = default;
+
+    TimelineItem getPredictions() const override { return mPredictions; };
+    TimelineItem getActuals() const override;
+    nsecs_t getActualQueueTime() const override;
+    PresentState getPresentState() const override;
+    PredictionState getPredictionState() const override { return mPredictionState; };
+    pid_t getOwnerPid() const override { return mOwnerPid; };
+    TimeStats::JankType getJankType() const;
+    nsecs_t getBaseTime() const;
+    uid_t getOwnerUid() const { return mOwnerUid; };
+    const std::string& getName() const { return mLayerName; };
+
+    void setActualStartTime(nsecs_t actualStartTime) override;
+    void setActualQueueTime(nsecs_t actualQueueTime) override;
+    void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
+    void setPresentState(PresentState state) override;
+    void setActualPresentTime(nsecs_t presentTime);
+    void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata);
+
+    // All the timestamps are dumped relative to the baseTime
+    void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
+
+private:
+    const pid_t mOwnerPid;
+    const uid_t mOwnerUid;
+    const std::string mLayerName;
+    const std::string mDebugName;
+    PresentState mPresentState GUARDED_BY(mMutex);
+    const PredictionState mPredictionState;
+    const TimelineItem mPredictions;
+    TimelineItem mActuals GUARDED_BY(mMutex);
+    nsecs_t mActualQueueTime GUARDED_BY(mMutex);
+    mutable std::mutex mMutex;
+    TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
+    int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
+};
+
+class FrameTimeline : public android::frametimeline::FrameTimeline {
+public:
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+    ~FrameTimeline() = default;
+
+    frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
+    std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
+            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+            std::optional<int64_t> token) override;
+    void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
+                         SurfaceFrame::PresentState state) override;
+    void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+    void setSfPresent(nsecs_t sfPresentTime,
+                      const std::shared_ptr<FenceTime>& presentFence) override;
+    void parseArgs(const Vector<String16>& args, std::string& result) override;
+    void setMaxDisplayFrames(uint32_t size) override;
+    void reset() override;
+
+private:
+    // Friend class for testing
+    friend class android::frametimeline::FrameTimelineTest;
+
+    /*
+     * DisplayFrame should be used only internally within FrameTimeline.
+     */
+    struct DisplayFrame {
+        DisplayFrame();
+
+        /* Usage of TimelineItem w.r.t SurfaceFlinger
+         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+         * endTime      Time when SurfaceFlinger sends a composited frame to Display
+         * presentTime  Time when the composited frame was presented on screen
+         */
+        TimelineItem surfaceFlingerPredictions;
+        TimelineItem surfaceFlingerActuals;
+
+        // Collection of predictions and actual values sent over by Layers
+        std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
+
+        PredictionState predictionState;
+        TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank
+        int32_t jankMetadata = 0x0;         // Additional details about the jank
+    };
+
+    void flushPendingPresentFences() REQUIRES(mMutex);
+    void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+    // BaseTime is the smallest timestamp in a DisplayFrame.
+    // Used for dumping all timestamps relative to the oldest, making it easy to read.
+    nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
+    void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
+                          nsecs_t baseTime) REQUIRES(mMutex);
+    void dumpAll(std::string& result);
+    void dumpJank(std::string& result);
+
+    // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
+    std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
+    std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
+            mPendingPresentFences GUARDED_BY(mMutex);
+    std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
+    TokenManager mTokenManager;
+    std::mutex mMutex;
+    uint32_t mMaxDisplayFrames;
+    std::shared_ptr<TimeStats> mTimeStats;
+    static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
+    // The initial container size for the vector<SurfaceFrames> inside display frame. Although this
+    // number doesn't represent any bounds on the number of surface frames that can go in a display
+    // frame, this is a good starting size for the vector so that we can avoid the internal vector
+    // resizing that happens with push_back.
+    static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+    // compared to prediction, we don't treat it as a jank.
+    static constexpr nsecs_t kPresentThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    static constexpr nsecs_t kDeadlineThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    static constexpr nsecs_t kSFStartThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
+};
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTracer/OWNERS b/services/surfaceflinger/FrameTracer/OWNERS
new file mode 100644
index 0000000..e4f5d45
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/OWNERS
@@ -0,0 +1 @@
+adsrini@google.com
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0b32402..1911a0a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -39,6 +39,7 @@
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <math.h>
+#include <private/android_filesystem_config.h>
 #include <renderengine/RenderEngine.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -61,18 +62,22 @@
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
 #include "EffectLayer.h"
+#include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
 #include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
+#include "input/InputWindow.h"
 
 #define DEBUG_RESIZE 0
 
 namespace android {
 
 using base::StringAppendF;
+using namespace android::flag_operators;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 std::atomic<int32_t> Layer::sSequence{1};
 
@@ -80,11 +85,14 @@
       : mFlinger(args.flinger),
         mName(args.name),
         mClientRef(args.client),
-        mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
+        mWindowType(static_cast<InputWindowInfo::Type>(
+                args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
     if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
+    if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
+        layerFlags |= layer_state_t::eLayerSkipScreenshot;
 
     mCurrentState.active_legacy.w = args.w;
     mCurrentState.active_legacy.h = args.h;
@@ -118,6 +126,8 @@
     mCurrentState.shadowRadius = 0.f;
     mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+    mCurrentState.frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+    mCurrentState.postTime = -1;
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -136,6 +146,16 @@
 
     mCallingPid = args.callingPid;
     mCallingUid = args.callingUid;
+
+    if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
+        // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
+        mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+        mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid);
+    } else {
+        // A create layer request from a non system request cannot specify the owner uid
+        mOwnerUid = mCallingUid;
+        mOwnerPid = mCallingPid;
+    }
 }
 
 void Layer::onFirstRef() {
@@ -229,13 +249,23 @@
     mFlinger->markLayerPendingRemovalLocked(this);
 }
 
+sp<Layer> Layer::getRootLayer() {
+    sp<Layer> parent = getParent();
+    if (parent == nullptr) {
+        return this;
+    }
+    return parent->getRootLayer();
+}
+
 void Layer::onRemovedFromCurrentState() {
-    auto layersInTree = getLayersInTree(LayerVector::StateSet::Current);
+    // Use the root layer since we want to maintain the hierarchy for the entire subtree.
+    auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
     std::sort(layersInTree.begin(), layersInTree.end());
-    for (const auto& layer : layersInTree) {
+
+    traverse(LayerVector::StateSet::Current, [&](Layer* layer) {
         layer->removeFromCurrentState();
         layer->removeRelativeZ(layersInTree);
-    }
+    });
 }
 
 void Layer::addToCurrentState() {
@@ -454,6 +484,7 @@
     compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
     compositionState->alpha = alpha;
     compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    compositionState->blurRegions = drawingState.blurRegions;
 }
 
 void Layer::prepareGeometryCompositionState() {
@@ -482,9 +513,6 @@
     compositionState->geomUsesSourceCrop = usesSourceCrop();
     compositionState->isSecure = isSecure();
 
-    compositionState->type = type;
-    compositionState->appId = appId;
-
     compositionState->metadata.clear();
     const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
     for (const auto& [key, mandatory] : supportedMetadata) {
@@ -525,7 +553,8 @@
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+    if (isHdrY410() || usesRoundedCorners || drawShadows() ||
+        getDrawingState().blurRegions.size() > 0) {
         compositionState->forceClientComposition = true;
     }
 }
@@ -598,7 +627,7 @@
 // ---------------------------------------------------------------------------
 
 std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+        compositionengine::LayerFE::ClientCompositionTargetSettings& /* targetSettings */) {
     if (!getCompositionState()) {
         return {};
     }
@@ -608,11 +637,7 @@
 
     compositionengine::LayerFE::LayerSettings layerSettings;
     layerSettings.geometry.boundaries = bounds;
-    if (targetSettings.useIdentityTransform) {
-        layerSettings.geometry.positionTransform = mat4();
-    } else {
-        layerSettings.geometry.positionTransform = getTransform().asMatrix4();
-    }
+    layerSettings.geometry.positionTransform = getTransform().asMatrix4();
 
     if (hasColorTransform()) {
         layerSettings.colorTransform = getColorTransform();
@@ -625,13 +650,14 @@
     layerSettings.alpha = alpha;
     layerSettings.sourceDataspace = getDataSpace();
     layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+    layerSettings.blurRegions = getBlurRegions();
     return layerSettings;
 }
 
 std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
-        const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+        const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect,
         ui::Dataspace outputDataspace) {
-    renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+    renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect);
     if (shadow.length <= 0.f) {
         return {};
     }
@@ -757,7 +783,12 @@
 
 bool Layer::isSecure() const {
     const State& s(mDrawingState);
-    return (s.flags & layer_state_t::eLayerSecure);
+    if (s.flags & layer_state_t::eLayerSecure) {
+        return true;
+    }
+
+    const auto p = mDrawingParent.promote();
+    return (p != nullptr) ? p->isSecure() : false;
 }
 
 // ----------------------------------------------------------------------------
@@ -783,10 +814,10 @@
             // to be applied as per normal (no synchronization).
             mCurrentState.barrierLayer_legacy = nullptr;
         } else {
-            auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy, this);
+            auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.barrierFrameNumber, this);
             if (barrierLayer->addSyncPoint(syncPoint)) {
                 std::stringstream ss;
-                ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
+                ss << "Adding sync point " << mCurrentState.barrierFrameNumber;
                 ATRACE_NAME(ss.str().c_str());
                 mRemoteSyncPoints.push_back(std::move(syncPoint));
             } else {
@@ -806,8 +837,8 @@
 
 void Layer::popPendingState(State* stateToCommit) {
     ATRACE_CALL();
-    *stateToCommit = mPendingStates[0];
 
+    *stateToCommit = mPendingStates[0];
     mPendingStates.removeAt(0);
     ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
@@ -820,14 +851,14 @@
                 // If we don't have a sync point for this, apply it anyway. It
                 // will be visually wrong, but it should keep us from getting
                 // into too much trouble.
-                ALOGE("[%s] No local sync point found", getDebugName());
+                ALOGV("[%s] No local sync point found", getDebugName());
                 popPendingState(stateToCommit);
                 stateUpdateAvailable = true;
                 continue;
             }
 
             if (mRemoteSyncPoints.front()->getFrameNumber() !=
-                mPendingStates[0].frameNumber_legacy) {
+                mPendingStates[0].barrierFrameNumber) {
                 ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
 
                 // Signal our end of the sync point and then dispose of it
@@ -856,13 +887,31 @@
     }
 
     // If we still have pending updates, we need to ensure SurfaceFlinger
-    // will keep calling doTransaction, and so we set the transaction flags.
+    // will keep calling doTransaction, and so we force a traversal.
     // However, our pending states won't clear until a frame is available,
-    // and so there is no need to specifically trigger a wakeup. Rather
-    // we set the flags and wait for something else to wake us up.
+    // and so there is no need to specifically trigger a wakeup.
     if (!mPendingStates.empty()) {
         setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTransactionFlagsNoWake(eTraversalNeeded);
+        mFlinger->setTraversalNeeded();
+    }
+
+    if (stateUpdateAvailable) {
+        const auto vsyncId =
+                stateToCommit->frameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID
+                ? std::nullopt
+                : std::make_optional(stateToCommit->frameTimelineVsyncId);
+
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
+                                                                     mName, mTransactionName,
+                                                                     vsyncId);
+        surfaceFrame->setActualQueueTime(stateToCommit->postTime);
+        // For transactions we set the acquire fence time to the post time as we
+        // don't have a buffer. For BufferStateLayer it is overridden in
+        // BufferStateLayer::applyPendingStates
+        surfaceFrame->setAcquireFenceTime(stateToCommit->postTime);
+
+        mSurfaceFrame = std::move(surfaceFrame);
     }
 
     mCurrentState.modified = false;
@@ -984,8 +1033,7 @@
         this->contentDirty = true;
 
         // we may use linear filtering, if the matrix scales us
-        const uint8_t type = getActiveTransform(c).getType();
-        mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
+        mNeedsFiltering = getActiveTransform(c).needsBilinearFiltering();
     }
 
     if (mCurrentState.inputInfoChanged) {
@@ -993,6 +1041,12 @@
         mCurrentState.inputInfoChanged = false;
     }
 
+    // Add the callbacks from the drawing state into the current state. This is so when the current
+    // state gets copied to drawing, we don't lose the callback handles that are still in drawing.
+    for (auto& handle : s.callbackHandles) {
+        c.callbackHandles.push_back(handle);
+    }
+
     // Commit the transaction
     commitTransaction(c);
     mPendingStatesSnapshot = mPendingStates;
@@ -1003,6 +1057,7 @@
 
 void Layer::commitTransaction(const State& stateToCommit) {
     mDrawingState = stateToCommit;
+    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mSurfaceFrame), PresentState::Presented);
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -1237,6 +1292,14 @@
     return true;
 }
 
+bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
+    mCurrentState.sequence++;
+    mCurrentState.blurRegions = blurRegions;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setFlags(uint8_t flags, uint8_t mask) {
     const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
     if (mCurrentState.flags == newFlags) return false;
@@ -1258,13 +1321,6 @@
     return true;
 }
 
-bool Layer::setOverrideScalingMode(int32_t scalingMode) {
-    if (scalingMode == mOverrideScalingMode) return false;
-    mOverrideScalingMode = scalingMode;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
 bool Layer::setMetadata(const LayerMetadata& data) {
     if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
     mCurrentState.sequence++;
@@ -1316,6 +1372,10 @@
     return Layer::PRIORITY_UNSET;
 }
 
+bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
+    return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
+};
+
 uint32_t Layer::getLayerStack() const {
     auto p = mDrawingParent.promote();
     if (p == nullptr) {
@@ -1415,6 +1475,13 @@
     return true;
 }
 
+void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime) {
+    mCurrentState.frameTimelineVsyncId = frameTimelineVsyncId;
+    mCurrentState.postTime = postTime;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+}
+
 Layer::FrameRate Layer::getFrameRateForLayerTree() const {
     const auto frameRate = getDrawingState().frameRate;
     if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
@@ -1432,14 +1499,21 @@
 
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
     ATRACE_CALL();
+    if (mLayerDetached) {
+        // If the layer is detached, then we don't defer this transaction since we will not
+        // commit the pending state while the layer is detached. Adding sync points may cause
+        // the barrier layer to wait for the states to be committed before dequeuing a buffer.
+        return;
+    }
+
     mCurrentState.barrierLayer_legacy = barrierLayer;
-    mCurrentState.frameNumber_legacy = frameNumber;
+    mCurrentState.barrierFrameNumber = frameNumber;
     // We don't set eTransactionNeeded, because just receiving a deferral
     // request without any other state updates shouldn't actually induce a delay
     mCurrentState.modified = true;
     pushPendingState();
     mCurrentState.barrierLayer_legacy = nullptr;
-    mCurrentState.frameNumber_legacy = 0;
+    mCurrentState.barrierFrameNumber = 0;
     mCurrentState.modified = false;
 }
 
@@ -1501,7 +1575,7 @@
     LayerDebugInfo info;
     const State& ds = getDrawingState();
     info.mName = getName();
-    sp<Layer> parent = getParent();
+    sp<Layer> parent = mDrawingParent.promote();
     info.mParentName = parent ? parent->getName() : "none"s;
     info.mType = getType();
     info.mTransparentRegion = ds.activeTransparentRegion_legacy;
@@ -1549,7 +1623,7 @@
     result.append("-------------------------------");
     result.append("-------------------------------");
     result.append("-------------------------------");
-    result.append("---------\n");
+    result.append("-------------------\n");
     result.append(" Layer name\n");
     result.append("           Z | ");
     result.append(" Window Type | ");
@@ -1557,12 +1631,12 @@
     result.append(" Transform | ");
     result.append("  Disp Frame (LTRB) | ");
     result.append("         Source Crop (LTRB) | ");
-    result.append("    Frame Rate (Explicit)\n");
+    result.append("    Frame Rate (Explicit) [Focused]\n");
     result.append("-------------------------------");
     result.append("-------------------------------");
     result.append("-------------------------------");
     result.append("-------------------------------");
-    result.append("---------\n");
+    result.append("-------------------\n");
 }
 
 std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
@@ -1613,17 +1687,20 @@
                   crop.bottom);
     if (layerState.frameRate.rate != 0 ||
         layerState.frameRate.type != FrameRateCompatibility::Default) {
-        StringAppendF(&result, "% 6.2ffps %15s\n", layerState.frameRate.rate,
+        StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate,
                       frameRateCompatibilityString(layerState.frameRate.type).c_str());
     } else {
-        StringAppendF(&result, "\n");
+        StringAppendF(&result, "                         ");
     }
 
+    const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
+    StringAppendF(&result, "    [%s]\n", focused ? "*" : " ");
+
     result.append("- - - - - - - - - - - - - - - - ");
     result.append("- - - - - - - - - - - - - - - - ");
     result.append("- - - - - - - - - - - - - - - - ");
     result.append("- - - - - - - - - - - - - - - - ");
-    result.append("- - -\n");
+    result.append("- - - - - - - -\n");
 }
 
 void Layer::dumpFrameStats(std::string& result) const {
@@ -1650,8 +1727,8 @@
 }
 
 void Layer::dumpCallingUidPid(std::string& result) const {
-    StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
-                  mCallingPid, mCallingUid);
+    StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
+                  getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
 }
 
 void Layer::onDisconnect() {
@@ -1666,7 +1743,7 @@
                                      FrameEventHistoryDelta* outDelta) {
     if (newTimestamps) {
         mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
-                                          getName().c_str(), newTimestamps->postedTime);
+                                          getName().c_str(), mOwnerUid, newTimestamps->postedTime);
         mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
                                               newTimestamps->acquireFence);
     }
@@ -1804,7 +1881,7 @@
         onRemovedFromCurrentState();
     }
 
-    if (callSetTransactionFlags || attachChildren()) {
+    if (attachChildren() || callSetTransactionFlags) {
         setTransactionFlags(eTransactionNeeded);
     }
     return true;
@@ -1832,9 +1909,9 @@
         if (client != nullptr && parentClient != client) {
             if (child->mLayerDetached) {
                 child->mLayerDetached = false;
+                child->attachChildren();
                 changed = true;
             }
-            changed |= child->attachChildren();
         }
     }
 
@@ -2110,6 +2187,10 @@
     return getDrawingState().backgroundBlurRadius;
 }
 
+const std::vector<BlurRegion>& Layer::getBlurRegions() const {
+    return getDrawingState().blurRegions;
+}
+
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
@@ -2134,12 +2215,12 @@
             : RoundedCornerState();
 }
 
-renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const {
     renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
 
     // Shift the spot light x-position to the middle of the display and then
     // offset it by casting layer's screen pos.
-    state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+    state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
     state.lightPos.y -= mScreenBounds.top;
 
     state.length = mEffectiveShadowRadius;
@@ -2179,7 +2260,7 @@
 }
 
 LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
-                                const DisplayDevice* display) const {
+                                const DisplayDevice* display) {
     LayerProto* layerProto = layersProto.add_layers();
     writeToProtoDrawingState(layerProto, traceFlags, display);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
@@ -2200,8 +2281,8 @@
 }
 
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
-                                     const DisplayDevice* display) const {
-    ui::Transform transform = getTransform();
+                                     const DisplayDevice* display) {
+    const ui::Transform transform = getTransform();
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
         for (const auto& pendingState : mPendingStatesSnapshot) {
@@ -2209,7 +2290,7 @@
             if (barrierLayer != nullptr) {
                 BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
                 barrierLayerProto->set_id(barrierLayer->sequence);
-                barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
+                barrierLayerProto->set_frame_number(pendingState.barrierFrameNumber);
             }
         }
 
@@ -2257,7 +2338,7 @@
 }
 
 void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
-                                    uint32_t traceFlags) const {
+                                    uint32_t traceFlags) {
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
     const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
@@ -2324,10 +2405,19 @@
         }
 
         layerInfo->set_is_relative_of(state.isRelativeOf);
+
+        layerInfo->set_owner_uid(mOwnerUid);
     }
 
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
-        LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop,
+        InputWindowInfo info;
+        if (useDrawing) {
+            info = fillInputInfo();
+        } else {
+            info = state.inputInfo;
+        }
+
+        LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
                                        [&]() { return layerInfo->mutable_input_window_info(); });
     }
 
@@ -2344,6 +2434,15 @@
 }
 
 InputWindowInfo Layer::fillInputInfo() {
+    if (!hasInputInfo()) {
+        mDrawingState.inputInfo.name = getName();
+        mDrawingState.inputInfo.ownerUid = mCallingUid;
+        mDrawingState.inputInfo.ownerPid = mCallingPid;
+        mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+        mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+        mDrawingState.inputInfo.displayId = getLayerStack();
+    }
+
     InputWindowInfo info = mDrawingState.inputInfo;
     info.id = sequence;
 
@@ -2352,17 +2451,8 @@
     }
 
     ui::Transform t = getTransform();
-    const float xScale = t.sx();
-    const float yScale = t.sy();
     int32_t xSurfaceInset = info.surfaceInset;
     int32_t ySurfaceInset = info.surfaceInset;
-    if (xScale != 1.0f || yScale != 1.0f) {
-        info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f;
-        info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f;
-        info.touchableRegion.scaleSelf(xScale, yScale);
-        xSurfaceInset = std::round(xSurfaceInset * xScale);
-        ySurfaceInset = std::round(ySurfaceInset * yScale);
-    }
 
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
@@ -2372,24 +2462,83 @@
     if (!layerBounds.isValid()) {
         layerBounds = getCroppedBufferSize(getDrawingState());
     }
-    layerBounds = t.transform(layerBounds);
+
+    const float xScale = t.getScaleX();
+    const float yScale = t.getScaleY();
+    if (xScale != 1.0f || yScale != 1.0f) {
+        xSurfaceInset = std::round(xSurfaceInset * xScale);
+        ySurfaceInset = std::round(ySurfaceInset * yScale);
+    }
+
+    // Transform the layer bounds from layer coordinate space to display coordinate space.
+    Rect transformedLayerBounds = t.transform(layerBounds);
 
     // clamp inset to layer bounds
-    xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
-    ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
+    xSurfaceInset = (xSurfaceInset >= 0)
+            ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2)
+            : 0;
+    ySurfaceInset = (ySurfaceInset >= 0)
+            ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2)
+            : 0;
 
-    layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
+    // inset while protecting from overflow TODO(b/161235021): What is going wrong
+    // in the overflow scenario?
+    {
+    int32_t tmp;
+    if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
+        transformedLayerBounds.left = tmp;
+    if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp))
+        transformedLayerBounds.right = tmp;
+    if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp))
+        transformedLayerBounds.top = tmp;
+    if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp))
+        transformedLayerBounds.bottom = tmp;
+    }
 
-    // Input coordinate should match the layer bounds.
-    info.frameLeft = layerBounds.left;
-    info.frameTop = layerBounds.top;
-    info.frameRight = layerBounds.right;
-    info.frameBottom = layerBounds.bottom;
+    // Compute the correct transform to send to input. This will allow it to transform the
+    // input coordinates from display space into window space. Therefore, it needs to use the
+    // final layer frame to create the inverse transform. Since surface insets are added later,
+    // along with the overflow, the best way to ensure we get the correct transform is to use
+    // the final frame calculated.
+    // 1. Take the original transform set on the window and get the inverse transform. This is
+    //    used to get the final bounds in display space (ignorning the transform). Apply the
+    //    inverse transform on the layerBounds to get the untransformed frame (in layer space)
+    // 2. Take the top and left of the untransformed frame to get the real position on screen.
+    //    Apply the layer transform on top/left so it includes any scale or rotation. These will
+    //    be the new translation values for the transform.
+    // 3. Update the translation of the original transform to the new translation values.
+    // 4. Send the inverse transform to input so the coordinates can be transformed back into
+    //    window space.
+    ui::Transform inverseTransform = t.inverse();
+    Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds);
+    vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top);
+    ui::Transform inputTransform(t);
+    inputTransform.set(translation.x, translation.y);
+    info.transform = inputTransform.inverse();
+
+    // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
+    // The frame should be the area the user sees on screen since it's used for occlusion
+    // detection.
+    Rect screenBounds = Rect{mScreenBounds};
+    transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
+    info.frameLeft = transformedLayerBounds.left;
+    info.frameTop = transformedLayerBounds.top;
+    info.frameRight = transformedLayerBounds.right;
+    info.frameBottom = transformedLayerBounds.bottom;
 
     // Position the touchable region relative to frame screen location and restrict it to frame
     // bounds.
-    info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
-    info.visible = canReceiveInput();
+    info.touchableRegion = inputTransform.transform(info.touchableRegion);
+    // For compatibility reasons we let layers which can receive input
+    // receive input before they have actually submitted a buffer. Because
+    // of this we use canReceiveInput instead of isVisible to check the
+    // policy-visibility, ignoring the buffer state. However for layers with
+    // hasInputInfo()==false we can use the real visibility state.
+    // We are just using these layers for occlusion detection in
+    // InputDispatcher, and obviously if they aren't visible they can't occlude
+    // anything.
+    info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+    info.alpha = getAlpha();
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
@@ -2425,7 +2574,7 @@
     return mDrawingParent.promote()->getClonedRoot();
 }
 
-bool Layer::hasInput() const {
+bool Layer::hasInputInfo() const {
     return mDrawingState.inputInfo.token != nullptr;
 }
 
@@ -2534,7 +2683,7 @@
     }
     // Cloned layers shouldn't handle watch outside since their z order is not determined by
     // WM or the client.
-    mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+    mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
 }
 
 void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2589,6 +2738,16 @@
     }
 }
 
+bool Layer::getPrimaryDisplayOnly() const {
+    const State& s(mDrawingState);
+    if (s.flags & layer_state_t::eLayerSkipScreenshot) {
+        return true;
+    }
+
+    sp<Layer> parent = mDrawingParent.promote();
+    return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3fa935f..b1ab9ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -26,6 +26,7 @@
 #include <renderengine/Mesh.h>
 #include <renderengine/Texture.h>
 #include <sys/types.h>
+#include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
@@ -55,8 +56,6 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
 class Client;
 class Colorizer;
 class DisplayDevice;
@@ -73,7 +72,9 @@
 class SurfaceInterceptor;
 }
 
-// ---------------------------------------------------------------------------
+namespace frametimeline {
+class SurfaceFrame;
+} // namespace frametimeline
 
 struct LayerCreationArgs {
     LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
@@ -94,17 +95,18 @@
 
 class Layer : public virtual RefBase, compositionengine::LayerFE {
     static std::atomic<int32_t> sSequence;
+    // The following constants represent priority of the window. SF uses this information when
+    // deciding which window has a priority when deciding about the refresh rate of the screen.
+    // Priority 0 is considered the highest priority. -1 means that the priority is unset.
     static constexpr int32_t PRIORITY_UNSET = -1;
+    // Windows that are in focus and voted for the preferred mode ID
+    static constexpr int32_t PRIORITY_FOCUSED_WITH_MODE = 0;
+    // // Windows that are in focus, but have not requested a specific mode ID.
+    static constexpr int32_t PRIORITY_FOCUSED_WITHOUT_MODE = 1;
+    // Windows that are not in focus, but voted for a specific mode ID.
+    static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
 
 public:
-    mutable bool contentDirty{false};
-    Region surfaceDamageRegion;
-
-    // Layer serial number.  This gives layers an explicit ordering, so we
-    // have a stable sort order when their layer stack and Z-order are
-    // the same.
-    int32_t sequence{sSequence++};
-
     enum { // flags for doTransaction()
         eDontUpdateGeometryState = 0x00000001,
         eVisibleRegion = 0x00000002,
@@ -190,7 +192,7 @@
         // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
         wp<Layer> barrierLayer_legacy;
-        uint64_t frameNumber_legacy;
+        uint64_t barrierFrameNumber;
 
         // the transparentRegion hint is a bit special, it's latched only
         // when we receive a buffer -- this is because it's "content"
@@ -254,6 +256,9 @@
         // be rendered around the layer.
         float shadowRadius;
 
+        // Layer regions that are made of custom materials, like frosted glass
+        std::vector<BlurRegion> blurRegions;
+
         // Priority of the layer assigned by Window Manager.
         int32_t frameRateSelectionPriority;
 
@@ -270,19 +275,64 @@
         // a buffer of a different size. ui::Transform::ROT_INVALID means the
         // a fixed transform hint is not set.
         ui::Transform::RotationFlags fixedTransformHint;
+
+        // The vsync id that was used to start the transaction
+        int64_t frameTimelineVsyncId;
+
+        // When the transaction was posted
+        nsecs_t postTime;
+    };
+
+    /*
+     * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
+     * is called.
+     */
+    class LayerCleaner {
+        sp<SurfaceFlinger> mFlinger;
+        sp<Layer> mLayer;
+
+    protected:
+        ~LayerCleaner() {
+            // destroy client resources
+            mFlinger->onHandleDestroyed(mLayer);
+        }
+
+    public:
+        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+              : mFlinger(flinger), mLayer(layer) {}
+    };
+
+    /*
+     * The layer handle is just a BBinder object passed to the client
+     * (remote process) -- we don't keep any reference on our side such that
+     * the dtor is called when the remote side let go of its reference.
+     *
+     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
+     * this layer when the handle is destroyed.
+     */
+    class Handle : public BBinder, public LayerCleaner {
+    public:
+        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+              : LayerCleaner(flinger, layer), owner(layer) {}
+
+        wp<Layer> owner;
     };
 
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
-    void onFirstRef() override;
+    static bool isLayerFocusedBasedOnPriority(int32_t priority);
+    static void miniDumpHeader(std::string& result);
+    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
 
-    int getWindowType() const { return mWindowType; }
+    // Provide unique string for each class type in the Layer hierarchy
+    virtual const char* getType() const = 0;
 
-    void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
-    bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
+    // true if this layer is visible, false otherwise
+    virtual bool isVisible() const = 0;
 
-    // ------------------------------------------------------------------------
+    virtual sp<Layer> createClone() = 0;
+
     // Geometry setting functions.
     //
     // The following group of functions are used to specify the layers
@@ -295,7 +345,6 @@
     //
     // The first set of geometry functions are controlled by the scaling mode, described
     // in window.h. The scaling mode may be set by the client, as it submits buffers.
-    // This value may be overriden through SurfaceControl, with setOverrideScalingMode.
     //
     // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
     // matrix updates will not be applied while a resize is pending
@@ -346,6 +395,7 @@
     // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
     // is specified in pixels.
     virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
+    virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setFlags(uint8_t flags, uint8_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
@@ -353,15 +403,10 @@
     virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
                                               uint64_t frameNumber);
     virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
-    virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
     virtual bool setMetadata(const LayerMetadata& data);
-    bool reparentChildren(const sp<IBinder>& newParentHandle);
-    void reparentChildren(const sp<Layer>& newParent);
-    virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+    virtual void setChildrenDrawingParent(const sp<Layer>&);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
     virtual bool detachChildren();
-    bool attachChildren();
-    bool isLayerDetached() const { return mLayerDetached; }
     virtual bool setColorTransform(const mat4& matrix);
     virtual mat4 getColorTransform() const;
     virtual bool hasColorTransform() const;
@@ -374,7 +419,7 @@
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
                            nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           const client_cache_t& /*clientCacheId*/) {
+                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -394,22 +439,13 @@
     }
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
-    bool setShadowRadius(float shadowRadius);
     virtual bool setFrameRateSelectionPriority(int32_t priority);
     virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
     //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
     //  rate priority from its parent.
     virtual int32_t getFrameRateSelectionPriority() const;
-
     virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
 
-    // Before color management is introduced, contents on Android have to be
-    // desaturated in order to match what they appears like visually.
-    // With color management, these contents will appear desaturated, thus
-    // needed to be saturated so that they match what they are designed for
-    // visually.
-    bool isLegacyDataSpace() const;
-
     virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
     virtual compositionengine::LayerFECompositionState* editCompositionState();
 
@@ -419,6 +455,196 @@
     virtual void useSurfaceDamage() {}
     virtual void useEmptyDamage() {}
 
+    /*
+     * isOpaque - true if this surface is opaque
+     *
+     * This takes into account the buffer format (i.e. whether or not the
+     * pixel format includes an alpha channel) and the "opaque" flag set
+     * on the layer.  It does not examine the current plane alpha value.
+     */
+    virtual bool isOpaque(const Layer::State&) const { return false; }
+
+    /*
+     * Returns whether this layer can receive input.
+     */
+    virtual bool canReceiveInput() const;
+
+    /*
+     * isProtected - true if the layer may contain protected contents in the
+     * GRALLOC_USAGE_PROTECTED sense.
+     */
+    virtual bool isProtected() const { return false; }
+
+    /*
+     * isFixedSize - true if content has a fixed size
+     */
+    virtual bool isFixedSize() const { return true; }
+
+    /*
+     * usesSourceCrop - true if content should use a source crop
+     */
+    virtual bool usesSourceCrop() const { return false; }
+
+    // Most layers aren't created from the main thread, and therefore need to
+    // grab the SF state lock to access HWC, but ContainerLayer does, so we need
+    // to avoid grabbing the lock again to avoid deadlock
+    virtual bool isCreatedFromMainThread() const { return false; }
+
+    virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
+    virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
+    virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
+    virtual ui::Transform getActiveTransform(const Layer::State& s) const {
+        return s.active_legacy.transform;
+    }
+    virtual Region getActiveTransparentRegion(const Layer::State& s) const {
+        return s.activeTransparentRegion_legacy;
+    }
+    virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+    virtual bool needsFiltering(const DisplayDevice*) const { return false; }
+
+    // True if this layer requires filtering
+    // This method is distinct from needsFiltering() in how the filter
+    // requirement is computed. needsFiltering() compares displayFrame and crop,
+    // where as this method transforms the displayFrame to layer-stack space
+    // first. This method should be used if there is no physical display to
+    // project onto when taking screenshots, as the filtering requirements are
+    // different.
+    // If the parent transform needs to be undone when capturing the layer, then
+    // the inverse parent transform is also required.
+    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
+        return false;
+    }
+
+    virtual void updateCloneBufferInfo(){};
+
+    virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
+
+    virtual bool isHdrY410() const { return false; }
+
+    virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
+
+    virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
+
+    /*
+     * called after composition.
+     * returns true if the layer latched a new buffer this frame.
+     */
+    virtual bool onPostComposition(const DisplayDevice*,
+                                   const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                                   const std::shared_ptr<FenceTime>& /*presentFence*/,
+                                   const CompositorTiming&) {
+        return false;
+    }
+
+    // If a buffer was replaced this frame, release the former buffer
+    virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
+
+    virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                                           const CompositorTiming& /*compositorTiming*/) {}
+
+    /*
+     * latchBuffer - called each time the screen is redrawn and returns whether
+     * the visible regions need to be recomputed (this is a fairly heavy
+     * operation, so this should be set only if needed). Typically this is used
+     * to figure out if the content or size of a surface has changed.
+     */
+    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                             nsecs_t /*expectedPresentTime*/) {
+        return false;
+    }
+
+    virtual bool isBufferLatched() const { return false; }
+
+    virtual void latchAndReleaseBuffer() {}
+
+    /*
+     * returns the rectangle that crops the content of the layer and scales it
+     * to the layer's size.
+     */
+    virtual Rect getBufferCrop() const { return Rect(); }
+
+    /*
+     * Returns the transform applied to the buffer.
+     */
+    virtual uint32_t getBufferTransform() const { return 0; }
+
+    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+
+    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+
+    /*
+     * Returns if a frame is ready
+     */
+    virtual bool hasReadyFrame() const { return false; }
+
+    virtual int32_t getQueuedFrameCount() const { return 0; }
+
+    virtual void pushPendingState();
+
+    /**
+     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     */
+    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
+    /**
+     * Returns the source bounds. If the bounds are not defined, it is inferred from the
+     * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+     * For the root layer, this is the display viewport size.
+     */
+    virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+        return parentBounds;
+    }
+    virtual FrameRate getFrameRateForLayerTree() const;
+
+    virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
+        return {};
+    }
+
+    virtual bool getTransformToDisplayInverse() const { return false; }
+
+    // Returns how rounded corners should be drawn for this layer.
+    // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+    // corner definition and converting it into current layer's coordinates.
+    // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+    // ignored.
+    virtual RoundedCornerState getRoundedCornerState() const;
+
+    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
+    virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
+    /**
+     * Return whether this layer needs an input info. For most layer types
+     * this is only true if they explicitly set an input-info but BufferLayer
+     * overrides this so we can generate input-info for Buffered layers that don't
+     * have them (for input occlusion detection checks).
+     */
+    virtual bool needsInputInfo() const { return hasInputInfo(); }
+
+    // Implements RefBase.
+    void onFirstRef() override;
+
+    // implements compositionengine::LayerFE
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t) override;
+    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
+    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+    const char* getDebugName() const override;
+
+    bool reparentChildren(const sp<IBinder>& newParentHandle);
+    void reparentChildren(const sp<Layer>& newParent);
+    bool attachChildren();
+    bool isLayerDetached() const { return mLayerDetached; }
+    bool setShadowRadius(float shadowRadius);
+
+    // Before color management is introduced, contents on Android have to be
+    // desaturated in order to match what they appears like visually.
+    // With color management, these contents will appear desaturated, thus
+    // needed to be saturated so that they match what they are designed for
+    // visually.
+    bool isLegacyDataSpace() const;
+
     uint32_t getTransactionFlags() const { return mTransactionFlags; }
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags);
@@ -426,9 +652,7 @@
     // Deprecated, please use compositionengine::Output::belongsInOutput()
     // instead.
     // TODO(lpique): Move the remaining callers (screencap) to the new function.
-    bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
-        return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
-    }
+    bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
 
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
@@ -456,33 +680,14 @@
     // only used within a single layer.
     uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
 
-    // -----------------------------------------------------------------------
-    // Virtuals
-
-    // Provide unique string for each class type in the Layer hierarchy
-    virtual const char* getType() const = 0;
-
-    /*
-     * isOpaque - true if this surface is opaque
-     *
-     * This takes into account the buffer format (i.e. whether or not the
-     * pixel format includes an alpha channel) and the "opaque" flag set
-     * on the layer.  It does not examine the current plane alpha value.
-     */
-    virtual bool isOpaque(const Layer::State&) const { return false; }
-
     /*
      * isSecure - true if this surface is secure, that is if it prevents
-     * screenshots or VNC servers.
+     * screenshots or VNC servers. A surface can be set to be secure by the
+     * application, being secure doesn't mean the surface has DRM contents.
      */
     bool isSecure() const;
 
     /*
-     * isVisible - true if this layer is visible, false otherwise
-     */
-    virtual bool isVisible() const = 0;
-
-    /*
      * isHiddenByPolicy - true if this layer has been forced invisible.
      * just because this is false, doesn't mean isVisible() is true.
      * For example if this layer has no active buffer, it may not be hidden by
@@ -490,147 +695,26 @@
      */
     bool isHiddenByPolicy() const;
 
-    /*
-     * Returns whether this layer can receive input.
-     */
-    virtual bool canReceiveInput() const;
-
-    /*
-     * isProtected - true if the layer may contain protected content in the
-     * GRALLOC_USAGE_PROTECTED sense.
-     */
-    virtual bool isProtected() const { return false; }
-
-    /*
-     * isFixedSize - true if content has a fixed size
-     */
-    virtual bool isFixedSize() const { return true; }
-
-    /*
-     * usesSourceCrop - true if content should use a source crop
-     */
-    virtual bool usesSourceCrop() const { return false; }
-
-    // Most layers aren't created from the main thread, and therefore need to
-    // grab the SF state lock to access HWC, but ContainerLayer does, so we need
-    // to avoid grabbing the lock again to avoid deadlock
-    virtual bool isCreatedFromMainThread() const { return false; }
-
     bool isRemovedFromCurrentState() const;
 
-    LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags,
-                             const DisplayDevice*) const;
+    LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
     // thread.
-    void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
-                                  const DisplayDevice*) const;
+    void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*);
     // Write drawing or current state. If writing current state, the caller should hold the
     // external mStateLock. If writing drawing state, this function should be called on the
     // main or tracing thread.
-    void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
-                                 uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
+                                 uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
 
-    virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
-    virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
-    virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
-    virtual ui::Transform getActiveTransform(const Layer::State& s) const {
-        return s.active_legacy.transform;
-    }
-    virtual Region getActiveTransparentRegion(const Layer::State& s) const {
-        return s.activeTransparentRegion_legacy;
-    }
-    virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
-    virtual bool needsFiltering(const DisplayDevice*) const { return false; }
-    // True if this layer requires filtering
-    // This method is distinct from needsFiltering() in how the filter
-    // requirement is computed. needsFiltering() compares displayFrame and crop,
-    // where as this method transforms the displayFrame to layer-stack space
-    // first. This method should be used if there is no physical display to
-    // project onto when taking screenshots, as the filtering requirements are
-    // different.
-    // If the parent transform needs to be undone when capturing the layer, then
-    // the inverse parent transform is also required.
-    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
-        return false;
-    }
+    InputWindowInfo::Type getWindowType() const { return mWindowType; }
 
-    // This layer is not a clone, but it's the parent to the cloned hierarchy. The
-    // variable mClonedChild represents the top layer that will be cloned so this
-    // layer will be the parent of mClonedChild.
-    // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
-    // if the real layer is destroyed, then the clone layer will also be destroyed.
-    sp<Layer> mClonedChild;
+    bool getPrimaryDisplayOnly() const;
 
-    virtual sp<Layer> createClone() = 0;
     void updateMirrorInfo();
-    virtual void updateCloneBufferInfo(){};
 
-protected:
-    sp<compositionengine::LayerFE> asLayerFE() const;
-    sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
-    bool isClone() { return mClonedFrom != nullptr; }
-    bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
-
-    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-
-    void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-    void updateClonedChildren(const sp<Layer>& mirrorRoot,
-                              std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-    void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-    void addChildToDrawing(const sp<Layer>& layer);
-    void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&);
-    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
-            const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport,
-            ui::Dataspace outputDataspace);
-    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
-    // the settings clears the content with a solid black fill.
-    void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const;
-
-public:
-    /*
-     * compositionengine::LayerFE overrides
-     */
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    bool onPreComposition(nsecs_t) override;
-    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
-    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-    const char* getDebugName() const override;
-
-protected:
-    void prepareBasicGeometryCompositionState();
-    void prepareGeometryCompositionState();
-    virtual void preparePerFrameCompositionState();
-    void prepareCursorCompositionState();
-
-public:
-    virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
-
-    virtual bool isHdrY410() const { return false; }
-
-    virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-
-    /*
-     * called after composition.
-     * returns true if the layer latched a new buffer this frame.
-     */
-    virtual bool onPostComposition(const DisplayDevice*,
-                                   const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                                   const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming&) {
-        return false;
-    }
-
-    // If a buffer was replaced this frame, release the former buffer
-    virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
-
-    virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                                           const CompositorTiming& /*compositorTiming*/) {}
     /*
      * doTransaction - process the transaction. This is a good place to figure
      * out which attributes of the surface have changed.
@@ -638,21 +722,6 @@
     uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
-     * latchBuffer - called each time the screen is redrawn and returns whether
-     * the visible regions need to be recomputed (this is a fairly heavy
-     * operation, so this should be set only if needed). Typically this is used
-     * to figure out if the content or size of a surface has changed.
-     */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
-                             nsecs_t /*expectedPresentTime*/) {
-        return false;
-    }
-
-    virtual bool isBufferLatched() const { return false; }
-
-    virtual void latchAndReleaseBuffer() {}
-
-    /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
      */
@@ -679,36 +748,12 @@
      */
     void updateTransformHint(ui::Transform::RotationFlags);
 
-    /*
-     * returns the rectangle that crops the content of the layer and scales it
-     * to the layer's size.
-     */
-    virtual Rect getBufferCrop() const { return Rect(); }
-
-    /*
-     * Returns the transform applied to the buffer.
-     */
-    virtual uint32_t getBufferTransform() const { return 0; }
-
-    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
-
-    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
-
-    /*
-     * Returns if a frame is ready
-     */
-    virtual bool hasReadyFrame() const { return false; }
-
-    virtual int32_t getQueuedFrameCount() const { return 0; }
-
-    // -----------------------------------------------------------------------
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
 
     LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
 
-    static void miniDumpHeader(std::string& result);
     void miniDump(std::string& result, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
     void dumpFrameEvents(std::string& result);
@@ -716,17 +761,10 @@
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
-
-    virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
-        return {};
-    }
-
     void onDisconnect();
     void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
                                   FrameEventHistoryDelta* outDelta);
 
-    virtual bool getTransformToDisplayInverse() const { return false; }
-
     ui::Transform getTransform() const;
 
     // Returns the Alpha of the Surface, accounting for the Alpha
@@ -743,14 +781,7 @@
     // is ready to acquire a buffer.
     ui::Transform::RotationFlags getFixedTransformHint() const;
 
-    // Returns how rounded corners should be drawn for this layer.
-    // This will traverse the hierarchy until it reaches its root, finding topmost rounded
-    // corner definition and converting it into current layer's coordinates.
-    // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
-    // ignored.
-    virtual RoundedCornerState getRoundedCornerState() const;
-
-    renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+    renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
 
     /**
      * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
@@ -760,17 +791,15 @@
      * the scene state, but it's also more efficient than traverseInZOrder and so useful for
      * book-keeping.
      */
-    void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
-    void traverseInReverseZOrder(LayerVector::StateSet stateSet,
-                                 const LayerVector::Visitor& visitor);
-    void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+    void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
+    void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+    void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
 
     /**
      * Traverse only children in z order, ignoring relative layers that are not children of the
      * parent.
      */
-    void traverseChildrenInZOrder(LayerVector::StateSet stateSet,
-                                  const LayerVector::Visitor& visitor);
+    void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
 
     size_t getChildrenCount() const;
 
@@ -782,7 +811,7 @@
     // the current state, but should not be called anywhere else!
     LayerVector& getCurrentChildren() { return mCurrentChildren; }
 
-    void addChild(const sp<Layer>& layer);
+    void addChild(const sp<Layer>&);
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
     ssize_t removeChild(const sp<Layer>& layer);
@@ -796,23 +825,7 @@
     // Copy the current list of children to the drawing state. Called by
     // SurfaceFlinger to complete a transaction.
     void commitChildList();
-    int32_t getZ(LayerVector::StateSet stateSet) const;
-    virtual void pushPendingState();
-
-    /**
-     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
-     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
-     */
-    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
-
-    /**
-     * Returns the source bounds. If the bounds are not defined, it is inferred from the
-     * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
-     * For the root layer, this is the display viewport size.
-     */
-    virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
-        return parentBounds;
-    }
+    int32_t getZ(LayerVector::StateSet) const;
 
     /**
      * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
@@ -822,53 +835,46 @@
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
-    bool setFrameRate(FrameRate frameRate);
-    virtual FrameRate getFrameRateForLayerTree() const;
-    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
+    bool setFrameRate(FrameRate);
+
+    virtual void setFrameTimelineVsyncForBuffer(int64_t /*frameTimelineVsyncId*/) {}
+    void setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime);
+
+    // Creates a new handle each time, so we only expect
+    // this to be called once.
+    sp<IBinder> getHandle();
+    const std::string& getName() const { return mName; }
+    bool getPremultipledAlpha() const;
+    void setInputInfo(const InputWindowInfo& info);
+
+    InputWindowInfo fillInputInfo();
+    /**
+     * Returns whether this layer has an explicitly set input-info.
+     */
+    bool hasInputInfo() const;
+
+    uid_t getOwnerUid() { return mOwnerUid; }
+
+    pid_t getOwnerPid() { return mOwnerPid; }
+
+    // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+    // variable mClonedChild represents the top layer that will be cloned so this
+    // layer will be the parent of mClonedChild.
+    // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+    // if the real layer is destroyed, then the clone layer will also be destroyed.
+    sp<Layer> mClonedChild;
+
+    mutable bool contentDirty{false};
+    Region surfaceDamageRegion;
+
+    // Layer serial number.  This gives layers an explicit ordering, so we
+    // have a stable sort order when their layer stack and Z-order are
+    // the same.
+    int32_t sequence{sSequence++};
+
+    bool mPendingHWCDestroy{false};
 
 protected:
-    // constant
-    sp<SurfaceFlinger> mFlinger;
-    /*
-     * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
-     * is called.
-     */
-    class LayerCleaner {
-        sp<SurfaceFlinger> mFlinger;
-        sp<Layer> mLayer;
-
-    protected:
-        ~LayerCleaner() {
-            // destroy client resources
-            mFlinger->onHandleDestroyed(mLayer);
-        }
-
-    public:
-        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : mFlinger(flinger), mLayer(layer) {}
-    };
-
-    friend class impl::SurfaceInterceptor;
-
-    // For unit tests
-    friend class TestableSurfaceFlinger;
-    friend class RefreshRateSelectionTest;
-    friend class SetFrameRateTest;
-
-    virtual void commitTransaction(const State& stateToCommit);
-
-    uint32_t getEffectiveUsage(uint32_t usage) const;
-
-    /**
-     * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
-     * crop coordinates, transforming them into layer space.
-     */
-    void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
-    void setParent(const sp<Layer>& layer);
-    LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
-    void addZOrderRelative(const wp<Layer>& relative);
-    void removeZOrderRelative(const wp<Layer>& relative);
-
     class SyncPoint {
     public:
         explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer)
@@ -896,6 +902,63 @@
         wp<Layer> mRequestedSyncLayer;
     };
 
+    friend class impl::SurfaceInterceptor;
+
+    // For unit tests
+    friend class TestableSurfaceFlinger;
+    friend class RefreshRateSelectionTest;
+    friend class SetFrameRateTest;
+
+    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
+            const LayerFE::LayerSettings&, const Rect& layerStackRect,
+            ui::Dataspace outputDataspace);
+    virtual void preparePerFrameCompositionState();
+    virtual void commitTransaction(const State& stateToCommit);
+    virtual bool applyPendingStates(State* stateToCommit);
+    virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
+
+    // Returns mCurrentScaling mode (originating from the
+    // Client) or mOverrideScalingMode mode (originating from
+    // the Surface Controller) if set.
+    virtual uint32_t getEffectiveScalingMode() const { return 0; }
+
+    sp<compositionengine::LayerFE> asLayerFE() const;
+    sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+    bool isClone() { return mClonedFrom != nullptr; }
+    bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+    void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedChildren(const sp<Layer>& mirrorRoot,
+                              std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void addChildToDrawing(const sp<Layer>&);
+    void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+
+    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+    // the settings clears the content with a solid black fill.
+    void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+
+    void prepareBasicGeometryCompositionState();
+    void prepareGeometryCompositionState();
+    void prepareCursorCompositionState();
+
+    uint32_t getEffectiveUsage(uint32_t usage) const;
+
+    /**
+     * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+     * crop coordinates, transforming them into layer space.
+     */
+    void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
+    void setParent(const sp<Layer>&);
+    LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
+    void addZOrderRelative(const wp<Layer>& relative);
+    void removeZOrderRelative(const wp<Layer>& relative);
+    compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+    bool usingRelativeZ(LayerVector::StateSet) const;
+
     // SyncPoints which will be signaled when the correct frame is at the head
     // of the queue and dropped after the frame has been latched. Protected by
     // mLocalSyncPointMutex.
@@ -910,56 +973,14 @@
     bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
 
     void popPendingState(State* stateToCommit);
-    virtual bool applyPendingStates(State* stateToCommit);
-    virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
 
-    // Returns mCurrentScaling mode (originating from the
-    // Client) or mOverrideScalingMode mode (originating from
-    // the Surface Controller) if set.
-    virtual uint32_t getEffectiveScalingMode() const { return 0; }
-
-public:
-    /*
-     * The layer handle is just a BBinder object passed to the client
-     * (remote process) -- we don't keep any reference on our side such that
-     * the dtor is called when the remote side let go of its reference.
-     *
-     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
-     * this layer when the handle is destroyed.
-     */
-    class Handle : public BBinder, public LayerCleaner {
-    public:
-        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : LayerCleaner(flinger, layer), owner(layer) {}
-
-        wp<Layer> owner;
-    };
-
-    // Creates a new handle each time, so we only expect
-    // this to be called once.
-    sp<IBinder> getHandle();
-    const std::string& getName() const { return mName; }
-    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
-    virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
-    bool getPremultipledAlpha() const;
-
-    bool mPendingHWCDestroy{false};
-    void setInputInfo(const InputWindowInfo& info);
-
-    InputWindowInfo fillInputInfo();
-    bool hasInput() const;
-
-protected:
-    compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
-
-    bool usingRelativeZ(LayerVector::StateSet stateSet) const;
+    // constant
+    sp<SurfaceFlinger> mFlinger;
 
     bool mPremultipliedAlpha{true};
     const std::string mName;
     const std::string mTransactionName{"TX - " + mName};
 
-    bool mPrimaryDisplayOnly = false;
-
     // These are only accessed by the main thread or the tracing thread.
     State mDrawingState;
     // Store a copy of the pending state so that the drawing thread can access the
@@ -988,7 +1009,6 @@
     bool mIsActiveBufferUpdatedForGpu = true;
 
     // We encode unset as -1.
-    int32_t mOverrideScalingMode{-1};
     std::atomic<uint64_t> mCurrentFrameNumber{0};
     // Whether filtering is needed b/c of the drawingstate
     bool mNeedsFiltering{false};
@@ -1020,11 +1040,25 @@
     bool mChildrenChanged{false};
 
     // Window types from WindowManager.LayoutParams
-    const int mWindowType;
+    const InputWindowInfo::Type mWindowType;
+
+    // Can only be accessed with the SF state lock held.
+    std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+
+    // The owner of the layer. If created from a non system process, it will be the calling uid.
+    // If created from a system process, the value can be passed in.
+    uid_t mOwnerUid;
+
+    // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+    // If created from a system process, the value can be passed in.
+    pid_t mOwnerPid;
 
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
+    // Returns true if the layer can draw shadows on its border.
+    virtual bool canDrawShadows() const { return true; }
+
     Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
     Region getVisibleRegion(const DisplayDevice*) const;
 
@@ -1032,21 +1066,31 @@
      * Returns an unsorted vector of all layers that are part of this tree.
      * That includes the current layer and all its descendants.
      */
-    std::vector<Layer*> getLayersInTree(LayerVector::StateSet stateSet);
+    std::vector<Layer*> getLayersInTree(LayerVector::StateSet);
     /**
      * Traverses layers that are part of this tree in the correct z order.
      * layersInTree must be sorted before calling this method.
      */
     void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
-                                       LayerVector::StateSet stateSet,
-                                       const LayerVector::Visitor& visitor);
-    LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
+                                       LayerVector::StateSet, const LayerVector::Visitor&);
+    LayerVector makeChildrenTraversalList(LayerVector::StateSet,
                                           const std::vector<Layer*>& layersInTree);
 
     void updateTreeHasFrameRateVote();
+    void setZOrderRelativeOf(const wp<Layer>& relativeOf);
+    void removeRemoteSyncPoints();
+
+    // Find the root of the cloned hierarchy, this means the first non cloned parent.
+    // This will return null if first non cloned parent is not found.
+    sp<Layer> getClonedRoot();
+
+    // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
+    // null.
+    sp<Layer> getRootLayer();
 
     // Cached properties computed from drawing state
-    // Effective transform taking into account parent transforms and any parent scaling.
+    // Effective transform taking into account parent transforms and any parent scaling, which is
+    // a transform from the current layer coordinate space to display(screen) coordinate space.
     ui::Transform mEffectiveTransform;
 
     // Bounds of the layer before any transformation is applied and before it has been cropped
@@ -1060,12 +1104,8 @@
     // Layer bounds in screen space.
     FloatRect mScreenBounds;
 
-    void setZOrderRelativeOf(const wp<Layer>& relativeOf);
-
     bool mGetHandleCalled = false;
 
-    void removeRemoteSyncPoints();
-
     // Tracks the process and user id of the caller when creating this layer
     // to help debugging.
     pid_t mCallingPid;
@@ -1082,12 +1122,8 @@
     // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
     float mEffectiveShadowRadius = 0.f;
 
-    // Returns true if the layer can draw shadows on its border.
-    virtual bool canDrawShadows() const { return true; }
-
-    // Find the root of the cloned hierarchy, this means the first non cloned parent.
-    // This will return null if first non cloned parent is not found.
-    sp<Layer> getClonedRoot();
+    // A list of regions on this layer that should have blurs.
+    const std::vector<BlurRegion>& getBlurRegions() const;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0fe1421..59fad9b 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -131,8 +131,12 @@
     }
 
     InputWindowInfoProto* proto = getInputWindowInfoProto();
-    proto->set_layout_params_flags(inputInfo.layoutParamsFlags);
-    proto->set_layout_params_type(inputInfo.layoutParamsType);
+    proto->set_layout_params_flags(inputInfo.flags.get());
+    using U = std::underlying_type_t<InputWindowInfo::Type>;
+    // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+    // are re-enabled.
+    static_assert(std::is_same_v<U, int32_t>);
+    proto->set_layout_params_type(static_cast<U>(inputInfo.type));
 
     LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
                                     inputInfo.frameBottom},
@@ -142,13 +146,11 @@
 
     proto->set_surface_inset(inputInfo.surfaceInset);
     proto->set_visible(inputInfo.visible);
-    proto->set_can_receive_keys(inputInfo.canReceiveKeys);
-    proto->set_has_focus(inputInfo.hasFocus);
+    proto->set_focusable(inputInfo.focusable);
     proto->set_has_wallpaper(inputInfo.hasWallpaper);
 
     proto->set_global_scale_factor(inputInfo.globalScaleFactor);
-    proto->set_window_x_scale(inputInfo.windowXScale);
-    proto->set_window_y_scale(inputInfo.windowYScale);
+    LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform());
     proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
     auto cropLayer = touchableRegionBounds.promote();
     if (cropLayer != nullptr) {
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index e6c8654..053b7f7 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -29,13 +29,12 @@
 
 LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
                              bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
-                             int32_t overrideScalingMode, bool transformToDisplayInverse)
+                             bool transformToDisplayInverse)
       : mFront(front),
         mCurrent(current),
         mRecomputeVisibleRegions(recomputeVisibleRegions),
         mStickyTransformSet(stickySet),
         mName(name),
-        mOverrideScalingMode(overrideScalingMode),
         mTransformToDisplayInverse(transformToDisplayInverse) {}
 
 bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
@@ -59,7 +58,7 @@
         }
     }
 
-    int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+    int actualScalingMode = item.mScalingMode;
     bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
     if (mFront.active_legacy != mFront.requested_legacy) {
         if (isFixedSize ||
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index fb5c750..4981f45 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -24,7 +24,7 @@
 class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
 public:
     LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
-                  bool stickySet, const std::string& name, int32_t overrideScalingMode,
+                  bool stickySet, const std::string& name,
                   bool transformToDisplayInverse);
 
     virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
@@ -35,7 +35,6 @@
     bool& mRecomputeVisibleRegions;
     const bool mStickyTransformSet;
     const std::string& mName;
-    const int32_t mOverrideScalingMode;
     const bool mTransformToDisplayInverse;
 };
 
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
new file mode 100644
index 0000000..e84508f
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "LayerRenderArea.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+struct ReparentForDrawing {
+    const sp<Layer>& oldParent;
+
+    ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+                       const Rect& drawingBounds)
+          : oldParent(oldParent) {
+        // Compute and cache the bounds for the new parent layer.
+        newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+                                 0.f /* shadowRadius */);
+        oldParent->setChildrenDrawingParent(newParent);
+    }
+    ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+};
+
+} // namespace
+
+LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
+                                 ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
+                                 const Rect& layerStackRect, bool allowSecureLayers)
+      : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers),
+        mLayer(std::move(layer)),
+        mCrop(crop),
+        mFlinger(flinger),
+        mChildrenOnly(childrenOnly) {}
+
+const ui::Transform& LayerRenderArea::getTransform() const {
+    return mTransform;
+}
+
+Rect LayerRenderArea::getBounds() const {
+    return mLayer->getBufferSize(mLayer->getDrawingState());
+}
+
+int LayerRenderArea::getHeight() const {
+    return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
+}
+
+int LayerRenderArea::getWidth() const {
+    return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+}
+
+bool LayerRenderArea::isSecure() const {
+    return mAllowSecureLayers;
+}
+
+bool LayerRenderArea::needsFiltering() const {
+    return mNeedsFiltering;
+}
+
+sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
+    return nullptr;
+}
+
+Rect LayerRenderArea::getSourceCrop() const {
+    if (mCrop.isEmpty()) {
+        return getBounds();
+    } else {
+        return mCrop;
+    }
+}
+
+void LayerRenderArea::render(std::function<void()> drawLayers) {
+    using namespace std::string_literals;
+
+    const Rect sourceCrop = getSourceCrop();
+    // no need to check rotation because there is none
+    mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+
+    if (!mChildrenOnly) {
+        mTransform = mLayer->getTransform().inverse();
+        drawLayers();
+    } else {
+        uint32_t w = static_cast<uint32_t>(getWidth());
+        uint32_t h = static_cast<uint32_t>(getHeight());
+        // In the "childrenOnly" case we reparent the children to a screenshot
+        // layer which has no properties set and which does not draw.
+        sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
+                {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+
+        ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
+        drawLayers();
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
new file mode 100644
index 0000000..6a90694
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+class Layer;
+class SurfaceFlinger;
+
+class LayerRenderArea : public RenderArea {
+public:
+    LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
+                    ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect,
+                    bool allowSecureLayers);
+
+    const ui::Transform& getTransform() const override;
+    Rect getBounds() const override;
+    int getHeight() const override;
+    int getWidth() const override;
+    bool isSecure() const override;
+    bool needsFiltering() const override;
+    sp<const DisplayDevice> getDisplayDevice() const override;
+    Rect getSourceCrop() const override;
+
+    void render(std::function<void()> drawLayers) override;
+
+private:
+    const sp<Layer> mLayer;
+    const Rect mCrop;
+
+    ui::Transform mTransform;
+    bool mNeedsFiltering = false;
+
+    SurfaceFlinger& mFlinger;
+    const bool mChildrenOnly;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f602412..f676d5b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -106,52 +106,83 @@
         drawSegment(Segment::Buttom, left, color, buffer, pixels);
 }
 
-sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
-                                                                     const half4& color) {
-    if (number < 0 || number > 1000) return nullptr;
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
+        int number, const half4& color, bool showSpinner) {
+    if (number < 0 || number > 1000) return {};
 
     const auto hundreds = number / 100;
     const auto tens = (number / 10) % 10;
     const auto ones = number % 10;
 
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                              GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
-                                      GRALLOC_USAGE_HW_TEXTURE,
-                              "RefreshRateOverlayBuffer");
-    uint8_t* pixels;
-    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
-    // Clear buffer content
-    drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
-    int left = 0;
-    if (hundreds != 0) {
-        drawDigit(hundreds, left, color, buffer, pixels);
+    std::vector<sp<GraphicBuffer>> buffers;
+    const auto loopCount = showSpinner ? 6 : 1;
+    for (int i = 0; i < loopCount; i++) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                  GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+                                          GRALLOC_USAGE_HW_TEXTURE,
+                                  "RefreshRateOverlayBuffer");
+        uint8_t* pixels;
+        buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+        // Clear buffer content
+        drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+        int left = 0;
+        if (hundreds != 0) {
+            drawDigit(hundreds, left, color, buffer, pixels);
+        }
         left += DIGIT_WIDTH + DIGIT_SPACE;
-    }
 
-    if (tens != 0) {
-        drawDigit(tens, left, color, buffer, pixels);
+        if (tens != 0) {
+            drawDigit(tens, left, color, buffer, pixels);
+        }
         left += DIGIT_WIDTH + DIGIT_SPACE;
-    }
 
-    drawDigit(ones, left, color, buffer, pixels);
-    buffer->unlock();
-    return buffer;
+        drawDigit(ones, left, color, buffer, pixels);
+        left += DIGIT_WIDTH + DIGIT_SPACE;
+
+        if (showSpinner) {
+            switch (i) {
+                case 0:
+                    drawSegment(Segment::Upper, left, color, buffer, pixels);
+                    break;
+                case 1:
+                    drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+                    break;
+                case 2:
+                    drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+                    break;
+                case 3:
+                    drawSegment(Segment::Buttom, left, color, buffer, pixels);
+                    break;
+                case 4:
+                    drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+                    break;
+                case 5:
+                    drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+                    break;
+            }
+        }
+
+        buffer->unlock();
+        buffers.emplace_back(buffer);
+    }
+    return buffers;
 }
 
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
-      : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
+      : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
     createLayer();
     primeCache();
 }
 
 bool RefreshRateOverlay::createLayer() {
+    int32_t layerId;
     const status_t ret =
             mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
                                  SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
                                  PIXEL_FORMAT_RGBA_8888,
                                  ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
-                                 &mIBinder, &mGbp, nullptr);
+                                 &mIBinder, &mGbp, nullptr, &layerId);
     if (ret) {
         ALOGE("failed to create buffer state layer");
         return false;
@@ -176,7 +207,7 @@
     if (allRefreshRates.size() == 1) {
         auto fps = allRefreshRates.begin()->second->getFps();
         half4 color = {LOW_FPS_COLOR, ALPHA};
-        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
         return;
     }
 
@@ -196,12 +227,12 @@
         color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
         color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
         color.a = ALPHA;
-        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
     }
 }
 
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
-    Rect frame(viewport.width >> 3, viewport.height >> 5);
+    Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
     frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
     mLayer->setFrame(frame);
 
@@ -209,8 +240,22 @@
 }
 
 void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
-    auto buffer = mBufferCache[refreshRate.getFps()];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+    mCurrentFps = refreshRate.getFps();
+    auto buffer = mBufferCache[*mCurrentFps][mFrame];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
+
+    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+void RefreshRateOverlay::onInvalidate() {
+    if (!mCurrentFps.has_value()) return;
+
+    const auto& buffers = mBufferCache[*mCurrentFps];
+    mFrame = (mFrame + 1) % buffers.size();
+    auto buffer = buffers[mFrame];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 35c8020..1a8938f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -38,15 +38,17 @@
 
 class RefreshRateOverlay {
 public:
-    explicit RefreshRateOverlay(SurfaceFlinger&);
+    RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
 
     void setViewport(ui::Size);
     void changeRefreshRate(const RefreshRate&);
+    void onInvalidate();
 
 private:
     class SevenSegmentDrawer {
     public:
-        static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+        static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
+                                                         bool showSpinner);
         static uint32_t getHeight() { return BUFFER_HEIGHT; }
         static uint32_t getWidth() { return BUFFER_WIDTH; }
 
@@ -65,7 +67,7 @@
         static constexpr uint32_t DIGIT_SPACE = 16;
         static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
         static constexpr uint32_t BUFFER_WIDTH =
-                3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+                4 * DIGIT_WIDTH + 3 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit|Space|Spinner
     };
 
     bool createLayer();
@@ -77,11 +79,14 @@
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
-
+    std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache;
+    std::optional<int> mCurrentFps;
+    int mFrame = 0;
     static constexpr float ALPHA = 0.8f;
     const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
     const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+
+    const bool mShowSpinner;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 27353d8..b7b7e46 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -29,14 +29,17 @@
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <cutils/properties.h>
 #include <gui/IRegionSamplingListener.h>
+#include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Trace.h>
 
 #include <string>
 
 #include "DisplayDevice.h"
+#include "DisplayRenderArea.h"
 #include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Promise.h"
+#include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
@@ -59,7 +62,7 @@
 
 constexpr auto timeForRegionSampling = 5000000ns;
 constexpr auto maxRegionSamplingSkips = 10;
-constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingWorkDuration = 3ms;
 constexpr auto defaultRegionSamplingPeriod = 100ms;
 constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
 // TODO: (b/127403193) duration to string conversion could probably be constexpr
@@ -71,9 +74,9 @@
 RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
     char value[PROPERTY_VALUE_MAX] = {};
 
-    property_get("debug.sf.region_sampling_offset_ns", value,
-                 toNsString(defaultRegionSamplingOffset).c_str());
-    int const samplingOffsetNsRaw = atoi(value);
+    property_get("debug.sf.region_sampling_duration_ns", value,
+                 toNsString(defaultRegionSamplingWorkDuration).c_str());
+    int const samplingDurationNsRaw = atoi(value);
 
     property_get("debug.sf.region_sampling_period_ns", value,
                  toNsString(defaultRegionSamplingPeriod).c_str());
@@ -85,22 +88,26 @@
 
     if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
         ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
-        mSamplingOffset = defaultRegionSamplingOffset;
+        mSamplingDuration = defaultRegionSamplingWorkDuration;
         mSamplingPeriod = defaultRegionSamplingPeriod;
         mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
     } else {
-        mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+        mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw);
         mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
         mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
     }
 }
 
-struct SamplingOffsetCallback : DispSync::Callback {
+struct SamplingOffsetCallback : VSyncSource::Callback {
     SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
-                           std::chrono::nanoseconds targetSamplingOffset)
+                           std::chrono::nanoseconds targetSamplingWorkDuration)
           : mRegionSamplingThread(samplingThread),
-            mScheduler(scheduler),
-            mTargetSamplingOffset(targetSamplingOffset) {}
+            mTargetSamplingWorkDuration(targetSamplingWorkDuration),
+            mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
+                                                             0ns,
+                                                             /*traceVsync=*/false)) {
+        mVSyncSource->setCallback(this);
+    }
 
     ~SamplingOffsetCallback() { stopVsyncListener(); }
 
@@ -112,8 +119,7 @@
         if (mVsyncListening) return;
 
         mPhaseIntervalSetting = Phase::ZERO;
-        mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
-                                                         mLastCallbackTime);
+        mVSyncSource->setVSyncEnabled(true);
         mVsyncListening = true;
     }
 
@@ -126,23 +132,24 @@
     void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
         if (!mVsyncListening) return;
 
-        mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
+        mVSyncSource->setVSyncEnabled(false);
         mVsyncListening = false;
     }
 
-    void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
+    void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
+                      nsecs_t /*deadlineTimestamp*/) final {
         std::unique_lock<decltype(mMutex)> lock(mMutex);
 
         if (mPhaseIntervalSetting == Phase::ZERO) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
             mPhaseIntervalSetting = Phase::SAMPLING;
-            mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
+            mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
             return;
         }
 
         if (mPhaseIntervalSetting == Phase::SAMPLING) {
             mPhaseIntervalSetting = Phase::ZERO;
-            mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
+            mVSyncSource->setDuration(0ns, 0ns);
             stopVsyncListenerLocked();
             lock.unlock();
             mRegionSamplingThread.notifySamplingOffset();
@@ -151,16 +158,15 @@
     }
 
     RegionSamplingThread& mRegionSamplingThread;
-    Scheduler& mScheduler;
-    const std::chrono::nanoseconds mTargetSamplingOffset;
+    const std::chrono::nanoseconds mTargetSamplingWorkDuration;
     mutable std::mutex mMutex;
-    nsecs_t mLastCallbackTime = 0;
     enum class Phase {
         ZERO,
         SAMPLING
     } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
             = Phase::ZERO;
     bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+    std::unique_ptr<VSyncSource> mVSyncSource;
 };
 
 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
@@ -168,11 +174,12 @@
       : mFlinger(flinger),
         mScheduler(scheduler),
         mTunables(tunables),
-        mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
-                           mTunables.mSamplingTimerTimeout),
-                   [] {}, [this] { checkForStaleLuma(); }),
+        mIdleTimer(
+                std::chrono::duration_cast<std::chrono::milliseconds>(
+                        mTunables.mSamplingTimerTimeout),
+                [] {}, [this] { checkForStaleLuma(); }),
         mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
-                                                                tunables.mSamplingOffset)),
+                                                                tunables.mSamplingDuration)),
         lastSampleTime(0ns) {
     mThread = std::thread([this]() { threadMain(); });
     pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
@@ -181,7 +188,7 @@
 
 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
       : RegionSamplingThread(flinger, scheduler,
-                             TimingTunables{defaultRegionSamplingOffset,
+                             TimingTunables{defaultRegionSamplingWorkDuration,
                                             defaultRegionSamplingPeriod,
                                             defaultRegionSamplingTimerTimeout}) {}
 
@@ -243,7 +250,7 @@
         // until the next vsync deadline, defer this sampling work
         // to a later frame, when hopefully there will be more time.
         DisplayStatInfo stats;
-        mScheduler.getDisplayStatInfo(&stats);
+        mScheduler.getDisplayStatInfo(&stats, systemTime());
         if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
             mDiscardedFrames++;
@@ -336,8 +343,20 @@
         return;
     }
 
-    const auto device = mFlinger.getDefaultDisplayDevice();
-    const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
+    wp<const DisplayDevice> displayWeak;
+
+    ui::LayerStack layerStack;
+    ui::Transform::RotationFlags orientation;
+    ui::Size displaySize;
+
+    {
+        // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
+        const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
+        displayWeak = display;
+        layerStack = display->getLayerStack();
+        orientation = ui::Transform::toRotationFlags(display->getOrientation());
+        displaySize = display->getSize();
+    }
 
     std::vector<RegionSamplingThread::Descriptor> descriptors;
     Region sampleRegion;
@@ -346,20 +365,18 @@
         descriptors.emplace_back(descriptor);
     }
 
-    const Rect sampledArea = sampleRegion.bounds();
-
     auto dx = 0;
     auto dy = 0;
     switch (orientation) {
         case ui::Transform::ROT_90:
-            dx = device->getWidth();
+            dx = displaySize.getWidth();
             break;
         case ui::Transform::ROT_180:
-            dx = device->getWidth();
-            dy = device->getHeight();
+            dx = displaySize.getWidth();
+            dy = displaySize.getHeight();
             break;
         case ui::Transform::ROT_270:
-            dy = device->getHeight();
+            dy = displaySize.getHeight();
             break;
         default:
             break;
@@ -368,8 +385,14 @@
     ui::Transform t(orientation);
     auto screencapRegion = t.transform(sampleRegion);
     screencapRegion = screencapRegion.translate(dx, dy);
-    DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
-                                 sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+    const Rect sampledBounds = sampleRegion.bounds();
+
+    SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] {
+        return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
+                                         sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
+                                         orientation);
+    });
 
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
 
@@ -393,9 +416,9 @@
             constexpr bool roundOutwards = true;
             Rect transformed = transform.transform(bounds, roundOutwards);
 
-            // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+            // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
             Rect ignore;
-            if (!transformed.intersect(sampledArea, &ignore)) return;
+            if (!transformed.intersect(sampledBounds, &ignore)) return;
 
             // If the layer doesn't intersect a sampling area, skip capturing it
             bool intersectsAnyArea = false;
@@ -411,22 +434,24 @@
                   bounds.top, bounds.right, bounds.bottom);
             visitor(layer);
         };
-        mFlinger.traverseLayersInDisplay(device, filterVisitor);
+        mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
     };
 
     sp<GraphicBuffer> buffer = nullptr;
-    if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
-        mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+    if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
+        mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
         buffer = mCachedBuffer;
     } else {
-        const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
-        buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+        const uint32_t usage =
+                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+        buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
                                    PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
     }
 
-    bool ignored;
-    mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
-                                 true /* regionSampling */, ignored);
+    const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+    mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+                                 true /* regionSampling */, captureListener);
+    ScreenCaptureResults captureResults = captureListener->waitForResults();
 
     std::vector<Descriptor> activeDescriptors;
     for (const auto& descriptor : descriptors) {
@@ -437,7 +462,7 @@
 
     ALOGV("Sampling %zu descriptors", activeDescriptors.size());
     std::vector<float> lumas =
-            sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
+            sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
     if (lumas.size() != activeDescriptors.size()) {
         ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
               activeDescriptors.size());
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index b9b7a3c..0defdb3 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -43,10 +43,10 @@
 class RegionSamplingThread : public IBinder::DeathRecipient {
 public:
     struct TimingTunables {
-        // debug.sf.sampling_offset_ns
-        // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
-        // at which the sampling should start.
-        std::chrono::nanoseconds mSamplingOffset;
+        // debug.sf.sampling_duration_ns
+        // When asynchronously collecting sample, the duration, at which the sampling should start
+        // before a vsync
+        std::chrono::nanoseconds mSamplingDuration;
         // debug.sf.sampling_period_ns
         // This is the maximum amount of time the luma recieving client
         // should have to wait for a new luma value after a frame is updated. The inverse of this is
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 6b0455a..c9f7f46 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -23,15 +23,15 @@
 
     static float getCaptureFillValue(CaptureFill captureFill);
 
-    RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
-               ui::Dataspace reqDataSpace, const Rect& displayViewport,
+    RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
+               const Rect& layerStackRect, bool allowSecureLayers = false,
                RotationFlags rotation = ui::Transform::ROT_0)
-          : mReqWidth(reqWidth),
-            mReqHeight(reqHeight),
+          : mAllowSecureLayers(allowSecureLayers),
+            mReqSize(reqSize),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
             mRotationFlags(rotation),
-            mDisplayViewport(displayViewport) {}
+            mLayerStackSpaceRect(layerStackRect) {}
 
     virtual ~RenderArea() = default;
 
@@ -70,8 +70,8 @@
     RotationFlags getRotationFlags() const { return mRotationFlags; }
 
     // Returns the size of the physical render area.
-    int getReqWidth() const { return static_cast<int>(mReqWidth); }
-    int getReqHeight() const { return static_cast<int>(mReqHeight); }
+    int getReqWidth() const { return mReqSize.width; }
+    int getReqHeight() const { return mReqSize.height; }
 
     // Returns the composition data space of the render area.
     ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
@@ -83,15 +83,17 @@
     virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
 
     // Returns the source display viewport.
-    const Rect& getDisplayViewport() const { return mDisplayViewport; }
+    const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
+
+protected:
+    const bool mAllowSecureLayers;
 
 private:
-    const uint32_t mReqWidth;
-    const uint32_t mReqHeight;
+    const ui::Size mReqSize;
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
     const RotationFlags mRotationFlags;
-    const Rect mDisplayViewport;
+    const Rect mLayerStackSpaceRect;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
deleted file mode 100644
index ff91bf7..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-// This is needed for stdint.h to define INT64_MAX in C++
-#define __STDC_LIMIT_MACROS
-
-#include <math.h>
-
-#include <algorithm>
-
-#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <utils/Thread.h>
-#include <utils/Trace.h>
-
-#include <ui/FenceTime.h>
-
-#include "DispSync.h"
-#include "EventLog/EventLog.h"
-#include "SurfaceFlinger.h"
-
-using android::base::StringAppendF;
-using std::max;
-using std::min;
-
-namespace android {
-
-DispSync::~DispSync() = default;
-DispSync::Callback::~Callback() = default;
-
-namespace impl {
-
-// Setting this to true adds a zero-phase tracer for correlating with hardware
-// vsync events
-static const bool kEnableZeroPhaseTracer = false;
-
-// This is the threshold used to determine when hardware vsync events are
-// needed to re-synchronize the software vsync model with the hardware.  The
-// error metric used is the mean of the squared difference between each
-// present time and the nearest software-predicted vsync.
-static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared
-
-#undef LOG_TAG
-#define LOG_TAG "DispSyncThread"
-class DispSyncThread : public Thread {
-public:
-    DispSyncThread(const char* name, bool showTraceDetailedInfo)
-          : mName(name),
-            mStop(false),
-            mModelLocked("DispSync:ModelLocked", false),
-            mPeriod(0),
-            mPhase(0),
-            mReferenceTime(0),
-            mWakeupLatency(0),
-            mFrameNumber(0),
-            mTraceDetailedInfo(showTraceDetailedInfo) {}
-
-    virtual ~DispSyncThread() {}
-
-    void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-
-        mPhase = phase;
-        const bool referenceTimeChanged = mReferenceTime != referenceTime;
-        mReferenceTime = referenceTime;
-        if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
-            // Inflate the reference time to be the most recent predicted
-            // vsync before the current time.
-            const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-            const nsecs_t baseTime = now - mReferenceTime;
-            const nsecs_t numOldPeriods = baseTime / mPeriod;
-            mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
-        }
-        mPeriod = period;
-        if (!mModelLocked && referenceTimeChanged) {
-            for (auto& eventListener : mEventListeners) {
-                eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase;
-                // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets
-                // are used) we treat it as like it happened in previous period.
-                if (eventListener.mLastEventTime > mReferenceTime) {
-                    eventListener.mLastEventTime -= mPeriod;
-                }
-            }
-        }
-        if (mTraceDetailedInfo) {
-            ATRACE_INT64("DispSync:Period", mPeriod);
-            ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
-            ATRACE_INT64("DispSync:Reference Time", mReferenceTime);
-        }
-        ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
-              " mReferenceTime = %" PRId64,
-              mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime));
-        mCond.signal();
-    }
-
-    void stop() {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-        mStop = true;
-        mCond.signal();
-    }
-
-    void lockModel() {
-        Mutex::Autolock lock(mMutex);
-        mModelLocked = true;
-    }
-
-    void unlockModel() {
-        Mutex::Autolock lock(mMutex);
-        mModelLocked = false;
-    }
-
-    virtual bool threadLoop() {
-        status_t err;
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
-        while (true) {
-            std::vector<CallbackInvocation> callbackInvocations;
-
-            nsecs_t targetTime = 0;
-
-            { // Scope for lock
-                Mutex::Autolock lock(mMutex);
-
-                if (mTraceDetailedInfo) {
-                    ATRACE_INT64("DispSync:Frame", mFrameNumber);
-                }
-                ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
-                ++mFrameNumber;
-
-                if (mStop) {
-                    return false;
-                }
-
-                if (mPeriod == 0) {
-                    err = mCond.wait(mMutex);
-                    if (err != NO_ERROR) {
-                        ALOGE("error waiting for new events: %s (%d)", strerror(-err), err);
-                        return false;
-                    }
-                    continue;
-                }
-
-                targetTime = computeNextEventTimeLocked(now);
-
-                bool isWakeup = false;
-
-                if (now < targetTime) {
-                    if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
-
-                    if (targetTime == INT64_MAX) {
-                        ALOGV("[%s] Waiting forever", mName);
-                        err = mCond.wait(mMutex);
-                    } else {
-                        ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime));
-                        err = mCond.waitRelative(mMutex, targetTime - now);
-                    }
-
-                    if (err == TIMED_OUT) {
-                        isWakeup = true;
-                    } else if (err != NO_ERROR) {
-                        ALOGE("error waiting for next event: %s (%d)", strerror(-err), err);
-                        return false;
-                    }
-                }
-
-                now = systemTime(SYSTEM_TIME_MONOTONIC);
-
-                // Don't correct by more than 1.5 ms
-                static const nsecs_t kMaxWakeupLatency = us2ns(1500);
-
-                if (isWakeup) {
-                    mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
-                    mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
-                    if (mTraceDetailedInfo) {
-                        ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
-                        ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
-                    }
-                }
-
-                callbackInvocations =
-                        gatherCallbackInvocationsLocked(now, computeNextRefreshLocked(0, now));
-            }
-
-            if (callbackInvocations.size() > 0) {
-                fireCallbackInvocations(callbackInvocations);
-            }
-        }
-
-        return false;
-    }
-
-    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
-                              nsecs_t lastCallbackTime) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-
-        for (size_t i = 0; i < mEventListeners.size(); i++) {
-            if (mEventListeners[i].mCallback == callback) {
-                return BAD_VALUE;
-            }
-        }
-
-        EventListener listener;
-        listener.mName = name;
-        listener.mPhase = phase;
-        listener.mCallback = callback;
-
-        // We want to allow the firstmost future event to fire without
-        // allowing any past events to fire. To do this extrapolate from
-        // mReferenceTime the most recent hardware vsync, and pin the
-        // last event time there.
-        const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        if (mPeriod != 0) {
-            const nsecs_t baseTime = now - mReferenceTime;
-            const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
-            const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
-            const nsecs_t phaseCorrection = mPhase + listener.mPhase;
-            const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
-            if (predictedLastEventTime >= now) {
-                // Make sure that the last event time does not exceed the current time.
-                // If it would, then back the last event time by a period.
-                listener.mLastEventTime = predictedLastEventTime - mPeriod;
-            } else {
-                listener.mLastEventTime = predictedLastEventTime;
-            }
-        } else {
-            listener.mLastEventTime = now + mPhase - mWakeupLatency;
-        }
-
-        if (lastCallbackTime <= 0) {
-            // If there is no prior callback time, try to infer one based on the
-            // logical last event time.
-            listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency;
-        } else {
-            listener.mLastCallbackTime = lastCallbackTime;
-        }
-
-        mEventListeners.push_back(listener);
-
-        mCond.signal();
-
-        return NO_ERROR;
-    }
-
-    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-
-        for (std::vector<EventListener>::iterator it = mEventListeners.begin();
-             it != mEventListeners.end(); ++it) {
-            if (it->mCallback == callback) {
-                *outLastCallback = it->mLastCallbackTime;
-                mEventListeners.erase(it);
-                mCond.signal();
-                return NO_ERROR;
-            }
-        }
-
-        return BAD_VALUE;
-    }
-
-    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-
-        for (auto& eventListener : mEventListeners) {
-            if (eventListener.mCallback == callback) {
-                const nsecs_t oldPhase = eventListener.mPhase;
-                eventListener.mPhase = phase;
-
-                // Pretend that the last time this event was handled at the same frame but with the
-                // new offset to allow for a seamless offset change without double-firing or
-                // skipping.
-                nsecs_t diff = oldPhase - phase;
-                eventListener.mLastEventTime -= diff;
-                eventListener.mLastCallbackTime -= diff;
-                mCond.signal();
-                return NO_ERROR;
-            }
-        }
-        return BAD_VALUE;
-    }
-
-    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const {
-        Mutex::Autolock lock(mMutex);
-        return computeNextRefreshLocked(periodOffset, now);
-    }
-
-private:
-    struct EventListener {
-        const char* mName;
-        nsecs_t mPhase;
-        nsecs_t mLastEventTime;
-        nsecs_t mLastCallbackTime;
-        DispSync::Callback* mCallback;
-    };
-
-    struct CallbackInvocation {
-        DispSync::Callback* mCallback;
-        nsecs_t mEventTime;
-        nsecs_t mExpectedVSyncTime;
-    };
-
-    nsecs_t computeNextEventTimeLocked(nsecs_t now) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        ALOGV("[%s] computeNextEventTimeLocked", mName);
-        nsecs_t nextEventTime = INT64_MAX;
-        for (size_t i = 0; i < mEventListeners.size(); i++) {
-            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now);
-
-            if (t < nextEventTime) {
-                nextEventTime = t;
-            }
-        }
-
-        ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
-        return nextEventTime;
-    }
-
-    // Sanity check that the duration is close enough in length to a period without
-    // falling into double-rate vsyncs.
-    bool isCloseToPeriod(nsecs_t duration) {
-        // Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
-        return duration < (3 * mPeriod) / 5;
-    }
-
-    std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now,
-                                                                    nsecs_t expectedVSyncTime) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
-
-        std::vector<CallbackInvocation> callbackInvocations;
-        nsecs_t onePeriodAgo = now - mPeriod;
-
-        for (auto& eventListener : mEventListeners) {
-            nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
-
-            if (t < now) {
-                if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
-                    eventListener.mLastEventTime = t;
-                    ALOGV("[%s] [%s] Skipping event due to model error", mName,
-                          eventListener.mName);
-                    continue;
-                }
-
-                CallbackInvocation ci;
-                ci.mCallback = eventListener.mCallback;
-                ci.mEventTime = t;
-                ci.mExpectedVSyncTime = expectedVSyncTime;
-                if (eventListener.mPhase < 0) {
-                    ci.mExpectedVSyncTime += mPeriod;
-                }
-                ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName,
-                      t - eventListener.mLastEventTime);
-                callbackInvocations.push_back(ci);
-                eventListener.mLastEventTime = t;
-                eventListener.mLastCallbackTime = now;
-            }
-        }
-
-        return callbackInvocations;
-    }
-
-    nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName,
-              ns2us(baseTime));
-
-        nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
-        ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
-        if (baseTime < lastEventTime) {
-            baseTime = lastEventTime;
-            ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime));
-        }
-
-        baseTime -= mReferenceTime;
-        ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
-        nsecs_t phase = mPhase + listener.mPhase;
-        ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
-        baseTime -= phase;
-        ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
-
-        // If our previous time is before the reference (because the reference
-        // has since been updated), the division by mPeriod will truncate
-        // towards zero instead of computing the floor. Since in all cases
-        // before the reference we want the next time to be effectively now, we
-        // set baseTime to -mPeriod so that numPeriods will be -1.
-        // When we add 1 and the phase, we will be at the correct event time for
-        // this period.
-        if (baseTime < 0) {
-            ALOGV("[%s] Correcting negative baseTime", mName);
-            baseTime = -mPeriod;
-        }
-
-        nsecs_t numPeriods = baseTime / mPeriod;
-        ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
-        nsecs_t t = (numPeriods + 1) * mPeriod + phase;
-        ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
-        t += mReferenceTime;
-        ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
-
-        // Check that it's been slightly more than half a period since the last
-        // event so that we don't accidentally fall into double-rate vsyncs
-        if (isCloseToPeriod(t - listener.mLastEventTime)) {
-            t += mPeriod;
-            ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
-        }
-
-        t -= mWakeupLatency;
-        ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
-
-        return t;
-    }
-
-    void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
-        if (mTraceDetailedInfo) ATRACE_CALL();
-        for (size_t i = 0; i < callbacks.size(); i++) {
-            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime,
-                                                    callbacks[i].mExpectedVSyncTime);
-        }
-    }
-
-    nsecs_t computeNextRefreshLocked(int periodOffset, nsecs_t now) const {
-        nsecs_t phase = mReferenceTime + mPhase;
-        if (mPeriod == 0) {
-            return 0;
-        }
-        return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
-    }
-
-    const char* const mName;
-
-    bool mStop;
-    TracedOrdinal<bool> mModelLocked;
-
-    nsecs_t mPeriod;
-    nsecs_t mPhase;
-    nsecs_t mReferenceTime;
-    nsecs_t mWakeupLatency;
-
-    int64_t mFrameNumber;
-
-    std::vector<EventListener> mEventListeners;
-
-    mutable Mutex mMutex;
-    Condition mCond;
-
-    // Flag to turn on logging in systrace.
-    const bool mTraceDetailedInfo;
-};
-
-#undef LOG_TAG
-#define LOG_TAG "DispSync"
-
-class ZeroPhaseTracer : public DispSync::Callback {
-public:
-    ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {}
-
-    virtual void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) {
-        mParity = !mParity;
-    }
-
-private:
-    TracedOrdinal<bool> mParity;
-};
-
-DispSync::DispSync(const char* name, bool hasSyncFramework)
-      : mName(name), mIgnorePresentFences(!hasSyncFramework) {
-    // This flag offers the ability to turn on systrace logging from the shell.
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.dispsync_trace_detailed_info", value, "0");
-    mTraceDetailedInfo = atoi(value);
-
-    mThread = new DispSyncThread(name, mTraceDetailedInfo);
-    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
-
-    // set DispSync to SCHED_FIFO to minimize jitter
-    struct sched_param param = {0};
-    param.sched_priority = 2;
-    if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, &param) != 0) {
-        ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
-    }
-
-    beginResync();
-
-    if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
-        mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
-        addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
-    }
-}
-
-DispSync::~DispSync() {
-    mThread->stop();
-    mThread->requestExitAndWait();
-}
-
-void DispSync::reset() {
-    Mutex::Autolock lock(mMutex);
-    resetLocked();
-}
-
-void DispSync::resetLocked() {
-    mPhase = 0;
-    const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
-    // Keep the most recent sample, when we resync to hardware we'll overwrite this
-    // with a more accurate signal
-    if (mResyncSamples[lastSampleIdx] != 0) {
-        mReferenceTime = mResyncSamples[lastSampleIdx];
-    }
-    mModelUpdated = false;
-    for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
-        mResyncSamples[i] = 0;
-    }
-    mNumResyncSamples = 0;
-    mFirstResyncSample = 0;
-    mNumResyncSamplesSincePresent = 0;
-    mThread->unlockModel();
-    resetErrorLocked();
-}
-
-bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
-    Mutex::Autolock lock(mMutex);
-
-    if (mIgnorePresentFences) {
-        return true;
-    }
-
-    mPresentFences[mPresentSampleOffset] = fenceTime;
-    mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
-    mNumResyncSamplesSincePresent = 0;
-
-    updateErrorLocked();
-
-    return !mModelUpdated || mError > kErrorThreshold;
-}
-
-void DispSync::beginResync() {
-    Mutex::Autolock lock(mMutex);
-    ALOGV("[%s] beginResync", mName);
-    resetLocked();
-}
-
-bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
-                               bool* periodFlushed) {
-    Mutex::Autolock lock(mMutex);
-
-    ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
-
-    *periodFlushed = false;
-    const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
-    mResyncSamples[idx] = timestamp;
-    if (mNumResyncSamples == 0) {
-        mPhase = 0;
-        ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
-              "mReferenceTime = %" PRId64,
-              mName, ns2us(mPeriod), ns2us(timestamp));
-    } else if (mPendingPeriod > 0) {
-        // mNumResyncSamples > 0, so priorIdx won't overflow
-        const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
-        const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
-
-        const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
-        if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) {
-            // Either the observed vsync is closer to the pending period, (and
-            // thus we detected a period change), or the period change will
-            // no-op. In either case, reset the model and flush the pending
-            // period.
-            resetLocked();
-            mIntendedPeriod = mPendingPeriod;
-            mPeriod = mPendingPeriod;
-            mPendingPeriod = 0;
-            if (mTraceDetailedInfo) {
-                ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
-                ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
-            }
-            *periodFlushed = true;
-        }
-    }
-    // Always update the reference time with the most recent timestamp.
-    mReferenceTime = timestamp;
-    mThread->updateModel(mPeriod, mPhase, mReferenceTime);
-
-    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
-        mNumResyncSamples++;
-    } else {
-        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
-    }
-
-    updateModelLocked();
-
-    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
-        resetErrorLocked();
-    }
-
-    if (mIgnorePresentFences) {
-        // If we're ignoring the present fences we have no way to know whether
-        // or not we're synchronized with the HW vsyncs, so we just request
-        // that the HW vsync events be turned on.
-        return true;
-    }
-
-    // Check against kErrorThreshold / 2 to add some hysteresis before having to
-    // resync again
-    bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
-    ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
-    if (modelLocked) {
-        *periodFlushed = true;
-        mThread->lockModel();
-    }
-    return !modelLocked;
-}
-
-void DispSync::endResync() {
-    mThread->lockModel();
-}
-
-status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback,
-                                    nsecs_t lastCallbackTime) {
-    Mutex::Autolock lock(mMutex);
-    return mThread->addEventListener(name, phase, callback, lastCallbackTime);
-}
-
-status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) {
-    Mutex::Autolock lock(mMutex);
-    return mThread->removeEventListener(callback, outLastCallbackTime);
-}
-
-status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
-    Mutex::Autolock lock(mMutex);
-    return mThread->changePhaseOffset(callback, phase);
-}
-
-void DispSync::setPeriod(nsecs_t period) {
-    Mutex::Autolock lock(mMutex);
-
-    const bool pendingPeriodShouldChange =
-            period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0);
-
-    if (pendingPeriodShouldChange) {
-        mPendingPeriod = period;
-    }
-    if (mTraceDetailedInfo) {
-        ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
-        ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
-    }
-}
-
-nsecs_t DispSync::getPeriod() {
-    // lock mutex as mPeriod changes multiple times in updateModelLocked
-    Mutex::Autolock lock(mMutex);
-    return mPeriod;
-}
-
-void DispSync::updateModelLocked() {
-    ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
-    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
-        ALOGV("[%s] Computing...", mName);
-        nsecs_t durationSum = 0;
-        nsecs_t minDuration = INT64_MAX;
-        nsecs_t maxDuration = 0;
-        // We skip the first 2 samples because the first vsync duration on some
-        // devices may be much more inaccurate than on other devices, e.g. due
-        // to delays in ramping up from a power collapse. By doing so this
-        // actually increases the accuracy of the DispSync model even though
-        // we're effectively relying on fewer sample points.
-        static constexpr size_t numSamplesSkipped = 2;
-        for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
-            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
-            size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
-            nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
-            durationSum += duration;
-            minDuration = min(minDuration, duration);
-            maxDuration = max(maxDuration, duration);
-        }
-
-        // Exclude the min and max from the average
-        durationSum -= minDuration + maxDuration;
-        mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2);
-
-        ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
-
-        double sampleAvgX = 0;
-        double sampleAvgY = 0;
-        double scale = 2.0 * M_PI / double(mPeriod);
-        for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
-            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
-            nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
-            double samplePhase = double(sample % mPeriod) * scale;
-            sampleAvgX += cos(samplePhase);
-            sampleAvgY += sin(samplePhase);
-        }
-
-        sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped);
-        sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped);
-
-        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
-
-        ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
-
-        if (mPhase < -(mPeriod / 2)) {
-            mPhase += mPeriod;
-            ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
-        }
-
-        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
-        mModelUpdated = true;
-    }
-}
-
-void DispSync::updateErrorLocked() {
-    if (!mModelUpdated) {
-        return;
-    }
-
-    int numErrSamples = 0;
-    nsecs_t sqErrSum = 0;
-
-    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
-        // Only check for the cached value of signal time to avoid unecessary
-        // syscalls. It is the responsibility of the DispSync owner to
-        // call getSignalTime() periodically so the cache is updated when the
-        // fence signals.
-        nsecs_t time = mPresentFences[i]->getCachedSignalTime();
-        if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) {
-            continue;
-        }
-
-        nsecs_t sample = time - mReferenceTime;
-        if (sample <= mPhase) {
-            continue;
-        }
-
-        nsecs_t sampleErr = (sample - mPhase) % mPeriod;
-        if (sampleErr > mPeriod / 2) {
-            sampleErr -= mPeriod;
-        }
-        sqErrSum += sampleErr * sampleErr;
-        numErrSamples++;
-    }
-
-    if (numErrSamples > 0) {
-        mError = sqErrSum / numErrSamples;
-        mZeroErrSamplesCount = 0;
-    } else {
-        mError = 0;
-        // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam.
-        mZeroErrSamplesCount++;
-        ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0,
-                 "No present times for model error.");
-    }
-
-    if (mTraceDetailedInfo) {
-        ATRACE_INT64("DispSync:Error", mError);
-    }
-}
-
-void DispSync::resetErrorLocked() {
-    mPresentSampleOffset = 0;
-    mError = 0;
-    mZeroErrSamplesCount = 0;
-    if (mTraceDetailedInfo) {
-        ATRACE_INT64("DispSync:Error", mError);
-    }
-    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
-        mPresentFences[i] = FenceTime::NO_FENCE;
-    }
-}
-
-nsecs_t DispSync::computeNextRefresh(int periodOffset, nsecs_t now) const {
-    Mutex::Autolock lock(mMutex);
-    nsecs_t phase = mReferenceTime + mPhase;
-    if (mPeriod == 0) {
-        return 0;
-    }
-    return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
-}
-
-void DispSync::setIgnorePresentFences(bool ignore) {
-    Mutex::Autolock lock(mMutex);
-    if (mIgnorePresentFences != ignore) {
-        mIgnorePresentFences = ignore;
-        resetLocked();
-    }
-}
-
-void DispSync::dump(std::string& result) const {
-    Mutex::Autolock lock(mMutex);
-    StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
-    StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod);
-    StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase);
-    StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
-    StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n",
-                  mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
-    StringAppendF(&result, "mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples,
-                  MAX_RESYNC_SAMPLES);
-
-    result.append("mResyncSamples:\n");
-    nsecs_t previous = -1;
-    for (size_t i = 0; i < mNumResyncSamples; i++) {
-        size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
-        nsecs_t sampleTime = mResyncSamples[idx];
-        if (i == 0) {
-            StringAppendF(&result, "  %" PRId64 "\n", sampleTime);
-        } else {
-            StringAppendF(&result, "  %" PRId64 " (+%" PRId64 ")\n", sampleTime,
-                          sampleTime - previous);
-        }
-        previous = sampleTime;
-    }
-
-    StringAppendF(&result, "mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    previous = Fence::SIGNAL_TIME_INVALID;
-    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
-        size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
-        nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
-        if (presentTime == Fence::SIGNAL_TIME_PENDING) {
-            StringAppendF(&result, "  [unsignaled fence]\n");
-        } else if (presentTime == Fence::SIGNAL_TIME_INVALID) {
-            StringAppendF(&result, "  [invalid fence]\n");
-        } else if (previous == Fence::SIGNAL_TIME_PENDING ||
-                   previous == Fence::SIGNAL_TIME_INVALID) {
-            StringAppendF(&result, "  %" PRId64 "  (%.3f ms ago)\n", presentTime,
-                          (now - presentTime) / 1000000.0);
-        } else {
-            StringAppendF(&result, "  %" PRId64 " (+%" PRId64 " / %.3f)  (%.3f ms ago)\n",
-                          presentTime, presentTime - previous,
-                          (presentTime - previous) / (double)mPeriod,
-                          (now - presentTime) / 1000000.0);
-        }
-        previous = presentTime;
-    }
-
-    StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now);
-}
-
-nsecs_t DispSync::expectedPresentTime(nsecs_t now) {
-    // The HWC doesn't currently have a way to report additional latency.
-    // Assume that whatever we submit now will appear right after the flip.
-    // For a smart panel this might be 1.  This is expressed in frames,
-    // rather than time, because we expect to have a constant frame delay
-    // regardless of the refresh rate.
-    const uint32_t hwcLatency = 0;
-
-    // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
-    return mThread->computeNextRefresh(hwcLatency, now);
-}
-
-} // namespace impl
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
deleted file mode 100644
index 832f08e..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h>
-
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <ui/FenceTime.h>
-
-#include <memory>
-
-namespace android {
-
-class FenceTime;
-
-class DispSync {
-public:
-    class Callback {
-    public:
-        Callback() = default;
-        virtual ~Callback();
-        virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
-
-    protected:
-        Callback(Callback const&) = delete;
-        Callback& operator=(Callback const&) = delete;
-    };
-
-    DispSync() = default;
-    virtual ~DispSync();
-
-    virtual void reset() = 0;
-    virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
-    virtual void beginResync() = 0;
-    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                                 bool* periodFlushed) = 0;
-    virtual void endResync() = 0;
-    virtual void setPeriod(nsecs_t period) = 0;
-    virtual nsecs_t getPeriod() = 0;
-    virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
-                                      nsecs_t lastCallbackTime) = 0;
-    virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
-    virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
-    virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
-    virtual void setIgnorePresentFences(bool ignore) = 0;
-    virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
-
-    virtual void dump(std::string& result) const = 0;
-
-protected:
-    DispSync(DispSync const&) = delete;
-    DispSync& operator=(DispSync const&) = delete;
-};
-
-namespace impl {
-
-class DispSyncThread;
-
-// DispSync maintains a model of the periodic hardware-based vsync events of a
-// display and uses that model to execute period callbacks at specific phase
-// offsets from the hardware vsync events.  The model is constructed by
-// feeding consecutive hardware event timestamps to the DispSync object via
-// the addResyncSample method.
-//
-// The model is validated using timestamps from Fence objects that are passed
-// to the DispSync object via the addPresentFence method.  These fence
-// timestamps should correspond to a hardware vsync event, but they need not
-// be consecutive hardware vsync times.  If this method determines that the
-// current model accurately represents the hardware event times it will return
-// false to indicate that a resynchronization (via addResyncSample) is not
-// needed.
-class DispSync : public android::DispSync {
-public:
-    // hasSyncFramework specifies whether the platform supports present fences.
-    DispSync(const char* name, bool hasSyncFramework);
-    ~DispSync() override;
-
-    // reset clears the resync samples and error value.
-    void reset() override;
-
-    // addPresentFence adds a fence for use in validating the current vsync
-    // event model.  The fence need not be signaled at the time
-    // addPresentFence is called.  When the fence does signal, its timestamp
-    // should correspond to a hardware vsync event.  Unlike the
-    // addResyncSample method, the timestamps of consecutive fences need not
-    // correspond to consecutive hardware vsync events.
-    //
-    // This method should be called with the retire fence from each HWComposer
-    // set call that affects the display.
-    bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override;
-
-    // The beginResync, addResyncSample, and endResync methods are used to re-
-    // synchronize the DispSync's model to the hardware vsync events.  The re-
-    // synchronization process involves first calling beginResync, then
-    // calling addResyncSample with a sequence of consecutive hardware vsync
-    // event timestamps, and finally calling endResync when addResyncSample
-    // indicates that no more samples are needed by returning false.
-    //
-    // This resynchronization process should be performed whenever the display
-    // is turned on (i.e. once immediately after it's turned on) and whenever
-    // addPresentFence returns true indicating that the model has drifted away
-    // from the hardware vsync events.
-    void beginResync() override;
-    // Adds a vsync sample to the dispsync model. The timestamp is the time
-    // of the vsync event that fired. periodFlushed will return true if the
-    // vsync period was detected to have changed to mPendingPeriod.
-    //
-    // This method will return true if more vsync samples are needed to lock
-    // down the DispSync model, and false otherwise.
-    // periodFlushed will be set to true if mPendingPeriod is flushed to
-    // mIntendedPeriod, and false otherwise.
-    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                         bool* periodFlushed) override;
-    void endResync() override;
-
-    // The setPeriod method sets the vsync event model's period to a specific
-    // value. This should be used to prime the model when a display is first
-    // turned on, or when a refresh rate change is requested.
-    void setPeriod(nsecs_t period) override;
-
-    // The getPeriod method returns the current vsync period.
-    nsecs_t getPeriod() override;
-
-    // addEventListener registers a callback to be called repeatedly at the
-    // given phase offset from the hardware vsync events.  The callback is
-    // called from a separate thread and it should return reasonably quickly
-    // (i.e. within a few hundred microseconds).
-    // If the callback was previously registered, and the last clock time the
-    // callback was invoked was known to the caller (e.g. via removeEventListener),
-    // then the caller may pass that through to lastCallbackTime, so that
-    // callbacks do not accidentally double-fire if they are unregistered and
-    // reregistered in rapid succession.
-    status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
-                              nsecs_t lastCallbackTime) override;
-
-    // removeEventListener removes an already-registered event callback.  Once
-    // this method returns that callback will no longer be called by the
-    // DispSync object.
-    // outLastCallbackTime will contain the last time that the callback was invoked.
-    // If the caller wishes to reregister the same callback, they should pass the
-    // callback time back into lastCallbackTime (see addEventListener).
-    status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override;
-
-    // changePhaseOffset changes the phase offset of an already-registered event callback. The
-    // method will make sure that there is no skipping or double-firing on the listener per frame,
-    // even when changing the offsets multiple times.
-    status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
-
-    // computeNextRefresh computes when the next refresh is expected to begin.
-    // The periodOffset value can be used to move forward or backward; an
-    // offset of zero is the next refresh, -1 is the previous refresh, 1 is
-    // the refresh after next. etc.
-    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const override;
-
-    // In certain situations the present fences aren't a good indicator of vsync
-    // time, e.g. when vr flinger is active, or simply aren't available,
-    // e.g. when the sync framework isn't present. Use this method to toggle
-    // whether or not DispSync ignores present fences. If present fences are
-    // ignored, DispSync will always ask for hardware vsync events by returning
-    // true from addPresentFence() and addResyncSample().
-    void setIgnorePresentFences(bool ignore) override;
-
-    // Determine the expected present time when a buffer acquired now will be displayed.
-    nsecs_t expectedPresentTime(nsecs_t now);
-
-    // dump appends human-readable debug info to the result string.
-    void dump(std::string& result) const override;
-
-private:
-    void updateModelLocked();
-    void updateErrorLocked();
-    void resetLocked();
-    void resetErrorLocked();
-
-    enum { MAX_RESYNC_SAMPLES = 32 };
-    enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
-    enum { NUM_PRESENT_SAMPLES = 8 };
-    enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
-    enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 };
-
-    const char* const mName;
-
-    // mPeriod is the computed period of the modeled vsync events in
-    // nanoseconds.
-    nsecs_t mPeriod;
-
-    // mIntendedPeriod is the intended period of the modeled vsync events in
-    // nanoseconds. Under ideal conditions this should be similar if not the
-    // same as mPeriod, plus or minus an observed error.
-    nsecs_t mIntendedPeriod = 0;
-
-    // mPendingPeriod is the proposed period change in nanoseconds.
-    // If mPendingPeriod differs from mPeriod and is nonzero, it will
-    // be flushed to mPeriod when we detect that the hardware switched
-    // vsync frequency.
-    nsecs_t mPendingPeriod = 0;
-
-    // mPhase is the phase offset of the modeled vsync events.  It is the
-    // number of nanoseconds from time 0 to the first vsync event.
-    nsecs_t mPhase;
-
-    // mReferenceTime is the reference time of the modeled vsync events.
-    // It is the nanosecond timestamp of the first vsync event after a resync.
-    nsecs_t mReferenceTime;
-
-    // mError is the computed model error.  It is based on the difference
-    // between the estimated vsync event times and those observed in the
-    // mPresentFences array.
-    nsecs_t mError;
-
-    // mZeroErrSamplesCount keeps track of how many times in a row there were
-    // zero timestamps available in the mPresentFences array.
-    // Used to sanity check that we are able to calculate the model error.
-    size_t mZeroErrSamplesCount;
-
-    // Whether we have updated the vsync event model since the last resync.
-    bool mModelUpdated;
-
-    // These member variables are the state used during the resynchronization
-    // process to store information about the hardware vsync event times used
-    // to compute the model.
-    nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
-    size_t mFirstResyncSample = 0;
-    size_t mNumResyncSamples = 0;
-    int mNumResyncSamplesSincePresent;
-
-    // These member variables store information about the present fences used
-    // to validate the currently computed model.
-    std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE};
-    size_t mPresentSampleOffset;
-
-    // mThread is the thread from which all the callbacks are called.
-    sp<DispSyncThread> mThread;
-
-    // mMutex is used to protect access to all member variables.
-    mutable Mutex mMutex;
-
-    // Ignore present (retire) fences if the device doesn't have support for the
-    // sync framework
-    bool mIgnorePresentFences;
-
-    std::unique_ptr<Callback> mZeroPhaseTracer;
-
-    // Flag to turn on logging in systrace.
-    bool mTraceDetailedInfo = false;
-};
-
-} // namespace impl
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 8752b66..ce5c31a 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "DispSyncSource.h"
@@ -26,37 +22,129 @@
 #include <utils/Trace.h>
 #include <mutex>
 
-#include "DispSync.h"
 #include "EventThread.h"
+#include "VsyncController.h"
 
-namespace android {
+namespace android::scheduler {
 using base::StringAppendF;
+using namespace std::chrono_literals;
 
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+class CallbackRepeater {
+public:
+    CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
+                     std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
+                     std::chrono::nanoseconds notBefore)
+          : mName(name),
+            mCallback(cb),
+            mRegistration(dispatch,
+                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+                                    std::placeholders::_2, std::placeholders::_3),
+                          mName),
+            mStarted(false),
+            mWorkDuration(workDuration),
+            mReadyDuration(readyDuration),
+            mLastCallTime(notBefore) {}
+
+    ~CallbackRepeater() {
+        std::lock_guard lock(mMutex);
+        mRegistration.cancel();
+    }
+
+    void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
+        std::lock_guard lock(mMutex);
+        mStarted = true;
+        mWorkDuration = workDuration;
+        mReadyDuration = readyDuration;
+
+        auto const scheduleResult =
+                mRegistration.schedule({.workDuration = mWorkDuration.count(),
+                                        .readyDuration = mReadyDuration.count(),
+                                        .earliestVsync = mLastCallTime.count()});
+        LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
+                            "Error scheduling callback: rc %X", scheduleResult);
+    }
+
+    void stop() {
+        std::lock_guard lock(mMutex);
+        LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
+        mStarted = false;
+        mRegistration.cancel();
+    }
+
+    void dump(std::string& result) const {
+        std::lock_guard lock(mMutex);
+        const auto relativeLastCallTime =
+                mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
+        StringAppendF(&result, "\t%s: ", mName.c_str());
+        StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
+                      mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
+        StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
+                      mStarted ? "running" : "stopped");
+    }
+
+private:
+    void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
+        {
+            std::lock_guard lock(mMutex);
+            mLastCallTime = std::chrono::nanoseconds(vsyncTime);
+        }
+
+        mCallback(vsyncTime, wakeupTime, readyTime);
+
+        {
+            std::lock_guard lock(mMutex);
+            if (!mStarted) {
+                return;
+            }
+            auto const scheduleResult =
+                    mRegistration.schedule({.workDuration = mWorkDuration.count(),
+                                            .readyDuration = mReadyDuration.count(),
+                                            .earliestVsync = vsyncTime});
+            LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
+                                "Error rescheduling callback: rc %X", scheduleResult);
+        }
+    }
+
+    const std::string mName;
+    scheduler::VSyncDispatch::Callback mCallback;
+
+    mutable std::mutex mMutex;
+    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+    bool mStarted GUARDED_BY(mMutex) = false;
+    std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
+    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
+    std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
+};
+
+DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch,
+                               std::chrono::nanoseconds workDuration,
+                               std::chrono::nanoseconds readyDuration, bool traceVsync,
                                const char* name)
       : mName(name),
         mValue(base::StringPrintf("VSYNC-%s", name), 0),
         mTraceVsync(traceVsync),
         mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
-        mDispSync(dispSync),
-        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
+        mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
+        mReadyDuration(readyDuration) {
+    mCallbackRepeater =
+            std::make_unique<CallbackRepeater>(vSyncDispatch,
+                                               std::bind(&DispSyncSource::onVsyncCallback, this,
+                                                         std::placeholders::_1,
+                                                         std::placeholders::_2,
+                                                         std::placeholders::_3),
+                                               name, workDuration, readyDuration,
+                                               std::chrono::steady_clock::now().time_since_epoch());
+}
+
+DispSyncSource::~DispSyncSource() = default;
 
 void DispSyncSource::setVSyncEnabled(bool enable) {
     std::lock_guard lock(mVsyncMutex);
     if (enable) {
-        status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
-                                                   static_cast<DispSync::Callback*>(this),
-                                                   mLastCallbackTime);
-        if (err != NO_ERROR) {
-            ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
-        }
+        mCallbackRepeater->start(mWorkDuration, mReadyDuration);
         // ATRACE_INT(mVsyncOnLabel.c_str(), 1);
     } else {
-        status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this),
-                                                      &mLastCallbackTime);
-        if (err != NO_ERROR) {
-            ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
-        }
+        mCallbackRepeater->stop();
         // ATRACE_INT(mVsyncOnLabel.c_str(), 0);
     }
     mEnabled = enable;
@@ -67,32 +155,22 @@
     mCallback = callback;
 }
 
-void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
+void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
+                                 std::chrono::nanoseconds readyDuration) {
     std::lock_guard lock(mVsyncMutex);
-    const nsecs_t period = mDispSync->getPeriod();
-
-    // Normalize phaseOffset to [-period, period)
-    const int numPeriods = phaseOffset / period;
-    phaseOffset -= numPeriods * period;
-    if (mPhaseOffset == phaseOffset) {
-        return;
-    }
-
-    mPhaseOffset = phaseOffset;
+    mWorkDuration = workDuration;
+    mReadyDuration = readyDuration;
 
     // If we're not enabled, we don't need to mess with the listeners
     if (!mEnabled) {
         return;
     }
 
-    status_t err =
-            mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
-    if (err != NO_ERROR) {
-        ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
-    }
+    mCallbackRepeater->start(mWorkDuration, mReadyDuration);
 }
 
-void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
+                                     nsecs_t readyTime) {
     VSyncSource::Callback* callback;
     {
         std::lock_guard lock(mCallbackMutex);
@@ -104,17 +182,13 @@
     }
 
     if (callback != nullptr) {
-        callback->onVSyncEvent(when, expectedVSyncTimestamp);
+        callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime);
     }
 }
 
 void DispSyncSource::dump(std::string& result) const {
     std::lock_guard lock(mVsyncMutex);
     StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
-    mDispSync->dump(result);
 }
 
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 2aee3f6..2fce235 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -18,45 +18,47 @@
 #include <mutex>
 #include <string>
 
-#include "DispSync.h"
 #include "EventThread.h"
 #include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
 
-namespace android {
+namespace android::scheduler {
+class CallbackRepeater;
 
-class DispSyncSource final : public VSyncSource, private DispSync::Callback {
+class DispSyncSource final : public VSyncSource {
 public:
-    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+    DispSyncSource(VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration,
+                   std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name);
 
-    ~DispSyncSource() override = default;
+    ~DispSyncSource() override;
 
     // The following methods are implementation of VSyncSource.
     const char* getName() const override { return mName; }
     void setVSyncEnabled(bool enable) override;
     void setCallback(VSyncSource::Callback* callback) override;
-    void setPhaseOffset(nsecs_t phaseOffset) override;
+    void setDuration(std::chrono::nanoseconds workDuration,
+                     std::chrono::nanoseconds readyDuration) override;
 
     void dump(std::string&) const override;
 
 private:
-    // The following method is the implementation of the DispSync::Callback.
-    void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+    void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
 
     const char* const mName;
     TracedOrdinal<int> mValue;
 
     const bool mTraceVsync;
     const std::string mVsyncOnLabel;
-    nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
 
-    DispSync* mDispSync;
+    std::unique_ptr<CallbackRepeater> mCallbackRepeater;
 
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
     mutable std::mutex mVsyncMutex;
-    TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
+    TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex);
+    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex);
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
deleted file mode 100644
index 7f9db9c..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/resource.h>
-
-#include <cutils/sched_policy.h>
-#include <log/log.h>
-#include <system/thread_defs.h>
-
-#include "EventControlThread.h"
-
-namespace android {
-
-EventControlThread::~EventControlThread() = default;
-
-namespace impl {
-
-EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
-      : mSetVSyncEnabled(std::move(function)) {
-    pthread_setname_np(mThread.native_handle(), "EventControlThread");
-
-    pid_t tid = pthread_gettid_np(mThread.native_handle());
-    setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
-    set_sched_policy(tid, SP_FOREGROUND);
-}
-
-EventControlThread::~EventControlThread() {
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mKeepRunning = false;
-        mCondition.notify_all();
-    }
-    mThread.join();
-}
-
-void EventControlThread::setVsyncEnabled(bool enabled) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mVsyncEnabled = enabled;
-    mCondition.notify_all();
-}
-
-// Unfortunately std::unique_lock gives warnings with -Wthread-safety
-void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
-    auto keepRunning = true;
-    auto currentVsyncEnabled = false;
-
-    while (keepRunning) {
-        mSetVSyncEnabled(currentVsyncEnabled);
-
-        std::unique_lock<std::mutex> lock(mMutex);
-        mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
-            return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
-        });
-        currentVsyncEnabled = mVsyncEnabled;
-        keepRunning = mKeepRunning;
-    }
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
deleted file mode 100644
index cafae53..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 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 <condition_variable>
-#include <cstddef>
-#include <functional>
-#include <mutex>
-#include <thread>
-
-#include <android-base/thread_annotations.h>
-
-namespace android {
-
-class EventControlThread {
-public:
-    virtual ~EventControlThread();
-
-    virtual void setVsyncEnabled(bool enabled) = 0;
-};
-
-namespace impl {
-
-class EventControlThread final : public android::EventControlThread {
-public:
-    using SetVSyncEnabledFunction = std::function<void(bool)>;
-
-    explicit EventControlThread(SetVSyncEnabledFunction function);
-    ~EventControlThread();
-
-    // EventControlThread implementation
-    void setVsyncEnabled(bool enabled) override;
-
-private:
-    void threadMain();
-
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-
-    const SetVSyncEnabledFunction mSetVSyncEnabled;
-    bool mVsyncEnabled GUARDED_BY(mMutex) = false;
-    bool mKeepRunning GUARDED_BY(mMutex) = true;
-
-    // Must be last so that everything is initialized before the thread starts.
-    std::thread mThread{&EventControlThread::threadMain, this};
-};
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index cee36a1..bf5be47 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@
 
 #include <android-base/stringprintf.h>
 
+#include <binder/IPCThreadState.h>
+
 #include <cutils/compiler.h>
 #include <cutils/sched_policy.h>
 
@@ -40,6 +42,7 @@
 #include <utils/Trace.h>
 
 #include "EventThread.h"
+#include "FrameTimeline.h"
 #include "HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
@@ -61,6 +64,8 @@
             return "VSyncRequest::None";
         case VSyncRequest::Single:
             return "VSyncRequest::Single";
+        case VSyncRequest::SingleSuppressCallback:
+            return "VSyncRequest::SingleSuppressCallback";
         default:
             return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
     }
@@ -74,18 +79,16 @@
 std::string toString(const DisplayEventReceiver::Event& event) {
     switch (event.header.type) {
         case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
-            return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}",
-                                event.header.displayId,
+            return StringPrintf("Hotplug{displayId=%s, %s}",
+                                to_string(event.header.displayId).c_str(),
                                 event.hotplug.connected ? "connected" : "disconnected");
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-            return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                                ", count=%u, expectedVSyncTimestamp=%" PRId64 "}",
-                                event.header.displayId, event.vsync.count,
+            return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}",
+                                to_string(event.header.displayId).c_str(), event.vsync.count,
                                 event.vsync.expectedVSyncTimestamp);
         case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-            return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                                ", configId=%u}",
-                                event.header.displayId, event.config.configId);
+            return StringPrintf("ConfigChanged{displayId=%s, configId=%u}",
+                                to_string(event.header.displayId).c_str(), event.config.configId);
         default:
             return "Event{}";
     }
@@ -100,11 +103,14 @@
 }
 
 DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
-                                      uint32_t count, nsecs_t expectedVSyncTimestamp) {
+                                      uint32_t count, nsecs_t expectedVSyncTimestamp,
+                                      nsecs_t deadlineTimestamp, int64_t vsyncId) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
     event.vsync.count = count;
     event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+    event.vsync.deadlineTimestamp = deadlineTimestamp;
+    event.vsync.vsyncId = vsyncId;
     return event;
 }
 
@@ -119,11 +125,12 @@
 
 } // namespace
 
-EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
                                              ResyncCallback resyncCallback,
                                              ISurfaceComposer::ConfigChanged configChanged)
       : resyncCallback(std::move(resyncCallback)),
         mConfigChanged(configChanged),
+        mOwnerUid(callingUid),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -139,6 +146,7 @@
 
 status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
     outChannel->setReceiveFd(mChannel.moveReceiveFd());
+    outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
     return NO_ERROR;
 }
 
@@ -152,11 +160,6 @@
     mEventThread->requestNextVsync(this);
 }
 
-void EventThreadConnection::requestLatestConfig() {
-    ATRACE_NAME("requestLatestConfig");
-    mEventThread->requestLatestConfig(this);
-}
-
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
     ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
@@ -169,9 +172,13 @@
 namespace impl {
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
-                         InterceptVSyncsCallback interceptVSyncsCallback)
+                         android::frametimeline::TokenManager* tokenManager,
+                         InterceptVSyncsCallback interceptVSyncsCallback,
+                         ThrottleVsyncCallback throttleVsyncCallback)
       : mVSyncSource(std::move(vsyncSource)),
+        mTokenManager(tokenManager),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+        mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
         mThreadName(mVSyncSource->getName()) {
     mVSyncSource->setCallback(this);
 
@@ -206,15 +213,17 @@
     mThread.join();
 }
 
-void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+void EventThread::setDuration(std::chrono::nanoseconds workDuration,
+                              std::chrono::nanoseconds readyDuration) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mVSyncSource->setPhaseOffset(phaseOffset);
+    mVSyncSource->setDuration(workDuration, readyDuration);
 }
 
 sp<EventThreadConnection> EventThread::createEventConnection(
         ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
-    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
-                                     configChanged);
+    return new EventThreadConnection(const_cast<EventThread*>(this),
+                                     IPCThreadState::self()->getCallingUid(),
+                                     std::move(resyncCallback), configChanged);
 }
 
 status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -266,31 +275,11 @@
     if (connection->vsyncRequest == VSyncRequest::None) {
         connection->vsyncRequest = VSyncRequest::Single;
         mCondition.notify_all();
+    } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
+        connection->vsyncRequest = VSyncRequest::Single;
     }
 }
 
-void EventThread::requestLatestConfig(const sp<EventThreadConnection>& connection) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (connection->mForcedConfigChangeDispatch) {
-        return;
-    }
-    connection->mForcedConfigChangeDispatch = true;
-    auto pendingConfigChange =
-            std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents),
-                         [&](const DisplayEventReceiver::Event& event) {
-                             return event.header.type ==
-                                     DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED;
-                         });
-
-    // If we didn't find a pending config change event, then push out the
-    // latest one we've ever seen.
-    if (pendingConfigChange == std::end(mPendingEvents)) {
-        mPendingEvents.push_back(mLastConfigChangeEvent);
-    }
-
-    mCondition.notify_all();
-}
-
 void EventThread::onScreenReleased() {
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mVSyncState || mVSyncState->synthetic) {
@@ -311,12 +300,21 @@
     mCondition.notify_all();
 }
 
-void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) {
+void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+                               nsecs_t deadlineTimestamp) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
+    const int64_t vsyncId = [&] {
+        if (mTokenManager != nullptr) {
+            return mTokenManager->generateTokenForPredictions(
+                    {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+        }
+        return static_cast<int64_t>(0);
+    }();
+
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       expectedVSyncTimestamp));
+                                       expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
     mCondition.notify_all();
 }
 
@@ -366,9 +364,6 @@
                         mInterceptVSyncsCallback(event->header.timestamp);
                     }
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-                    mLastConfigChangeEvent = *event;
-                    break;
             }
         }
 
@@ -381,10 +376,6 @@
                 vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
 
                 if (event && shouldConsumeEvent(*event, connection)) {
-                    if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
-                        connection->mForcedConfigChangeDispatch) {
-                        connection->mForcedConfigChangeDispatch = false;
-                    }
                     consumers.push_back(connection);
                 }
 
@@ -445,9 +436,12 @@
 
                 LOG_FATAL_IF(!mVSyncState);
                 const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
-                const auto expectedVSyncTime = now + timeout.count();
+                const auto deadlineTimestamp = now + timeout.count();
+                const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
+                // TODO(b/162890590): use TokenManager to populate vsyncId
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
-                                                   ++mVSyncState->count, expectedVSyncTime));
+                                                   ++mVSyncState->count, expectedVSyncTime,
+                                                   deadlineTimestamp, /*vsyncId=*/0));
             }
         }
     }
@@ -455,26 +449,42 @@
 
 bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
                                      const sp<EventThreadConnection>& connection) const {
+    const auto throttleVsync = [&] {
+        return mThrottleVsyncCallback &&
+                mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+    };
+
     switch (event.header.type) {
         case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
             return true;
 
         case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
-            const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch;
-            return oneTimeDispatch ||
-                    connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+            return connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
         }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
             switch (connection->vsyncRequest) {
                 case VSyncRequest::None:
                     return false;
-                case VSyncRequest::Single:
+                case VSyncRequest::SingleSuppressCallback:
                     connection->vsyncRequest = VSyncRequest::None;
+                    return false;
+                case VSyncRequest::Single: {
+                    if (throttleVsync()) {
+                        return false;
+                    }
+                    connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
                     return true;
+                }
                 case VSyncRequest::Periodic:
+                    if (throttleVsync()) {
+                        return false;
+                    }
                     return true;
                 default:
+                    // We don't throttle vsync if the app set a vsync request rate
+                    // since there is no easy way to do that and this is a very
+                    // rare case
                     return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
             }
 
@@ -508,8 +518,8 @@
 
     StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
     if (mVSyncState) {
-        StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n",
-                      mVSyncState->displayId, mVSyncState->count,
+        StringAppendF(&result, "{displayId=%s, count=%u%s}\n",
+                      to_string(mVSyncState->displayId).c_str(), mVSyncState->count,
                       mVSyncState->synthetic ? ", synthetic" : "");
     } else {
         StringAppendF(&result, "none\n");
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 64acbd7..2e2d989 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -41,13 +41,20 @@
 class EventThreadTest;
 class SurfaceFlinger;
 
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
 // ---------------------------------------------------------------------------
 
 using ResyncCallback = std::function<void()>;
 
 enum class VSyncRequest {
-    None = -1,
-    Single = 0,
+    None = -2,
+    // Single wakes up for the next two frames to avoid scheduler overhead
+    Single = -1,
+    // SingleSuppressCallback only wakes up for the next frame
+    SingleSuppressCallback = 0,
     Periodic = 1,
     // Subsequent values are periods.
 };
@@ -57,7 +64,8 @@
     class Callback {
     public:
         virtual ~Callback() {}
-        virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
+        virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+                                  nsecs_t deadlineTimestamp) = 0;
     };
 
     virtual ~VSyncSource() {}
@@ -65,14 +73,15 @@
     virtual const char* getName() const = 0;
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
-    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+    virtual void setDuration(std::chrono::nanoseconds workDuration,
+                             std::chrono::nanoseconds readyDuration) = 0;
 
     virtual void dump(std::string& result) const = 0;
 };
 
 class EventThreadConnection : public BnDisplayEventConnection {
 public:
-    EventThreadConnection(EventThread*, ResyncCallback,
+    EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
                           ISurfaceComposer::ConfigChanged configChanged);
     virtual ~EventThreadConnection();
 
@@ -81,19 +90,15 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
-    void requestLatestConfig() override; // asynchronous
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    ISurfaceComposer::ConfigChanged mConfigChanged =
+    const ISurfaceComposer::ConfigChanged mConfigChanged =
             ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
-    // Store whether we need to force dispatching a config change separately -
-    // if mConfigChanged ever changes before the config change is dispatched
-    // then we still need to propagate an initial config to the app if we
-    // haven't already.
-    bool mForcedConfigChangeDispatch = false;
+
+    const uid_t mOwnerUid;
 
 private:
     virtual void onFirstRef();
@@ -122,17 +127,14 @@
 
     virtual void dump(std::string& result) const = 0;
 
-    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+    virtual void setDuration(std::chrono::nanoseconds workDuration,
+                             std::chrono::nanoseconds readyDuration) = 0;
 
     virtual status_t registerDisplayEventConnection(
             const sp<EventThreadConnection>& connection) = 0;
     virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
     // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
     virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
-    // Dispatches the most recent configuration
-    // Usage of this method assumes that only the primary internal display
-    // supports multiple display configurations.
-    virtual void requestLatestConfig(const sp<EventThreadConnection>& connection) = 0;
 
     // Retrieves the number of event connections tracked by this EventThread.
     virtual size_t getEventThreadConnectionCount() = 0;
@@ -143,8 +145,10 @@
 class EventThread : public android::EventThread, private VSyncSource::Callback {
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+    using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
 
-    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
+    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
+                ThrottleVsyncCallback);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -153,7 +157,6 @@
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
     void requestNextVsync(const sp<EventThreadConnection>& connection) override;
-    void requestLatestConfig(const sp<EventThreadConnection>& connection) override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
@@ -168,7 +171,8 @@
 
     void dump(std::string& result) const override;
 
-    void setPhaseOffset(nsecs_t phaseOffset) override;
+    void setDuration(std::chrono::nanoseconds workDuration,
+                     std::chrono::nanoseconds readyDuration) override;
 
     size_t getEventThreadConnectionCount() override;
 
@@ -188,11 +192,14 @@
             REQUIRES(mMutex);
 
     // Implements VSyncSource::Callback
-    void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) override;
+    void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+                      nsecs_t deadlineTimestamp) override;
 
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
+    frametimeline::TokenManager* const mTokenManager;
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
+    const ThrottleVsyncCallback mThrottleVsyncCallback;
     const char* const mThreadName;
 
     std::thread mThread;
@@ -201,7 +208,6 @@
 
     std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
     std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
-    DisplayEventReceiver::Event mLastConfigChangeEvent GUARDED_BY(mMutex);
 
     // VSYNC state of connected display.
     struct VSyncState {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 975c9db..016b076 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -35,16 +35,17 @@
         mCallback = callback;
     }
 
-    void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+    void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+                           nsecs_t deadlineTimestamp) {
         std::lock_guard<std::mutex> lock(mCallbackMutex);
         if (mCallback) {
-            mCallback->onVSyncEvent(when, expectedVSyncTimestamp);
+            mCallback->onVSyncEvent(when, expectedVSyncTimestamp, deadlineTimestamp);
         }
     }
 
     const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
-    void setPhaseOffset(nsecs_t) override {}
+    void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
     void dump(std::string&) const override {}
 
 private:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 2925109..36433c2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -20,6 +20,7 @@
 
 #include "LayerHistory.h"
 
+#include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
@@ -109,6 +110,8 @@
         auto layer = weakLayer.promote();
         // Only use the layer if the reference still exists.
         if (layer || CC_UNLIKELY(mTraceEnabled)) {
+            const auto layerFocused =
+                    Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
             // Check if frame rate was set on layer.
             const auto frameRate = layer->getFrameRateForLayerTree();
             if (frameRate.rate > 0.f) {
@@ -122,11 +125,12 @@
                             return LayerVoteType::NoVote;
                     }
                 }();
-                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
+                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
+                                   layerFocused});
             } else if (recent) {
                 summary.push_back({layer->getName(), LayerVoteType::Heuristic,
                                    info->getRefreshRate(now),
-                                   /* weight */ 1.0f});
+                                   /* weight */ 1.0f, layerFocused});
             }
 
             if (CC_UNLIKELY(mTraceEnabled)) {
@@ -180,4 +184,11 @@
 
     mActiveLayersEnd = 0;
 }
+
+std::string LayerHistory::dump() const {
+    std::lock_guard lock(mLock);
+    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
+                              mActiveLayersEnd);
+}
+
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index acd76b0..128699b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -22,6 +22,7 @@
 
 #include <memory>
 #include <mutex>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -71,6 +72,7 @@
     virtual Summary summarize(nsecs_t now) = 0;
 
     virtual void clear() = 0;
+    virtual std::string dump() const = 0;
 };
 
 namespace impl {
@@ -97,6 +99,7 @@
     android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
 
     void clear() override;
+    std::string dump() const override;
 
 private:
     friend class android::scheduler::LayerHistoryTest;
@@ -136,7 +139,7 @@
 
 class LayerHistoryV2 : public android::scheduler::LayerHistory {
 public:
-    LayerHistoryV2();
+    LayerHistoryV2(const scheduler::RefreshRateConfigs&);
     virtual ~LayerHistoryV2();
 
     // Layers are unregistered when the weak reference expires.
@@ -155,6 +158,7 @@
     android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
 
     void clear() override;
+    std::string dump() const override;
 
 private:
     friend android::scheduler::LayerHistoryTestV2;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 316000c..37e67e1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -20,6 +20,7 @@
 
 #include "LayerHistory.h"
 
+#include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
@@ -31,9 +32,8 @@
 #include <utility>
 
 #include "../Layer.h"
-#include "SchedulerUtils.h"
-
 #include "LayerInfoV2.h"
+#include "SchedulerUtils.h"
 
 namespace android::scheduler::impl {
 
@@ -58,37 +58,32 @@
     return atoi(value);
 }
 
-void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
+void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
+           int fps) {
     const auto layer = weak.promote();
     if (!layer) return;
 
-    const auto makeTag = [layer](LayerHistory::LayerVoteType vote) {
-        return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName();
+    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
     };
 
-    const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote);
-    const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic);
-    const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault);
-    const auto explicitExactOrMultipleVoteTag =
-            makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple);
-    const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min);
-    const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max);
-
-    ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
-    ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
-    ATRACE_INT(explicitDefaultVoteTag.c_str(),
-               type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
-    ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
-               type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
-    ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
-    ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+    traceType(LayerHistory::LayerVoteType::NoVote, 1);
+    traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+    traceType(LayerHistory::LayerVoteType::Min, 1);
+    traceType(LayerHistory::LayerVoteType::Max, 1);
 
     ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
 }
 } // namespace
 
-LayerHistoryV2::LayerHistoryV2()
-      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+    LayerInfoV2::setTraceEnabled(mTraceEnabled);
+    LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
+}
+
 LayerHistoryV2::~LayerHistoryV2() = default;
 
 void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
@@ -130,9 +125,10 @@
             continue;
         }
 
-        // TODO(b/144307188): This needs to be plugged into layer summary as
-        //  an additional parameter.
-        ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
+        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+        const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
+              frameRateSelectionPriority, layerFocused ? "" : "not");
 
         const auto [type, refreshRate] = info->getRefreshRate(now);
         // Skip NoVote layer as those don't have any requirements
@@ -148,10 +144,10 @@
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        summary.push_back({strong->getName(), type, refreshRate, weight});
+        summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(layer, type, static_cast<int>(std::round(refreshRate)));
+            trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
         }
     }
 
@@ -190,7 +186,7 @@
         }
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
         }
 
         info->onLayerInactive(now);
@@ -217,4 +213,11 @@
         info->clearHistory(systemTime());
     }
 }
+
+std::string LayerHistoryV2::dump() const {
+    std::lock_guard lock(mLock);
+    return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
+                              mActiveLayersEnd);
+}
+
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index cb81ca2..820624b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -98,9 +98,9 @@
                 return false;
             }
 
-            // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+            // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
             if (mElements.size() < HISTORY_SIZE &&
-                mElements.back() - mElements.front() < HISTORY_TIME.count()) {
+                mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
                 return false;
             }
 
@@ -124,7 +124,7 @@
 
     private:
         std::deque<nsecs_t> mElements;
-        static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
     };
 
     friend class LayerHistoryTest;
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 82da007..44f20d0 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -15,24 +15,31 @@
  */
 
 // #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "LayerInfoV2.h"
 
 #include <algorithm>
 #include <utility>
 
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
 #undef LOG_TAG
 #define LOG_TAG "LayerInfoV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 namespace android::scheduler {
 
+const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
+bool LayerInfoV2::sTraceEnabled = false;
+
 LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
                          LayerHistory::LayerVoteType defaultVote)
       : mName(name),
         mHighRefreshRatePeriod(highRefreshRatePeriod),
         mDefaultVote(defaultVote),
-        mLayerVote({defaultVote, 0.0f}) {}
+        mLayerVote({defaultVote, 0.0f}),
+        mRefreshRateHistory(name) {}
 
 void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
                                      LayerUpdateType updateType, bool pendingConfigChange) {
@@ -92,24 +99,28 @@
 }
 
 bool LayerInfoV2::hasEnoughDataForHeuristic() const {
-    // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+    // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
     if (mFrameTimes.size() < 2) {
+        ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
         return false;
     }
 
     if (!isFrameTimeValid(mFrameTimes.front())) {
+        ALOGV("stale frames still captured");
         return false;
     }
 
-    if (mFrameTimes.size() < HISTORY_SIZE &&
-        mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+    const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+    if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+        ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+              totalDuration / 1e9f);
         return false;
     }
 
     return true;
 }
 
-std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
+std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
     nsecs_t totalPresentTimeDeltas = 0;
     nsecs_t totalQueueTimeDeltas = 0;
     bool missingPresentTime = false;
@@ -117,15 +128,20 @@
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
         // Ignore frames captured during a config change
         if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
-            continue;
+            return std::nullopt;
         }
 
         totalQueueTimeDeltas +=
                 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
         numFrames++;
 
-        if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+        if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
             missingPresentTime = true;
+            // If there are no presentation timestamps and we haven't calculated
+            // one in the past then we can't calculate the refresh rate
+            if (mLastRefreshRate.reported == 0) {
+                return std::nullopt;
+            }
             continue;
         }
 
@@ -140,58 +156,46 @@
     // when implementing render ahead for specific refresh rates. When hwui no longer provides
     // presentation timestamps we look at the queue time to see if the current refresh rate still
     // matches the content.
+
     const auto averageFrameTime =
             static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
             numFrames;
-    return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
+    return static_cast<nsecs_t>(averageFrameTime);
 }
 
-bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
-    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
-        // Ignore frames captured during a config change
-        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
-            continue;
-        }
-        const auto presentTimeDeltas = [&] {
-            const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
-                                                  : (it + 1)->presetTime - it->presetTime;
-            return std::max(delta, mHighRefreshRatePeriod);
-        }();
-
-        if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
     static constexpr float MARGIN = 1.0f; // 1Hz
-
     if (!hasEnoughDataForHeuristic()) {
         ALOGV("Not enough data");
         return std::nullopt;
     }
 
-    const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
+    const auto averageFrameTime = calculateAverageFrameTime();
+    if (averageFrameTime.has_value()) {
+        const auto refreshRate = 1e9f / *averageFrameTime;
+        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+        if (refreshRateConsistent) {
+            const auto knownRefreshRate =
+                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
 
-    // If there are no presentation timestamps provided we can't calculate the refresh rate
-    if (missingPresentTime && mLastReportedRefreshRate == 0) {
-        return std::nullopt;
+            // To avoid oscillation, use the last calculated refresh rate if it is
+            // close enough
+            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+                mLastRefreshRate.reported != knownRefreshRate) {
+                mLastRefreshRate.calculated = refreshRate;
+                mLastRefreshRate.reported = knownRefreshRate;
+            }
+
+            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        } else {
+            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        }
     }
 
-    if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
-        return std::nullopt;
-    }
-
-    const auto refreshRate = 1e9f / averageFrameTime;
-    if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
-        mLastReportedRefreshRate = refreshRate;
-    }
-
-    ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
-    return mLastReportedRefreshRate;
+    return mLastRefreshRate.reported == 0 ? std::nullopt
+                                          : std::make_optional(mLastRefreshRate.reported);
 }
 
 std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
@@ -202,15 +206,24 @@
 
     if (isAnimating(now)) {
         ALOGV("%s is animating", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
         return {LayerHistory::LayerVoteType::Max, 0};
     }
 
     if (!isFrequent(now)) {
         ALOGV("%s is infrequent", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
         return {LayerHistory::LayerVoteType::Min, 0};
     }
 
-    auto refreshRate = calculateRefreshRateIfPossible();
+    // If the layer was previously tagged as animating or infrequent, we clear
+    // the history as it is likely the layer just changed its behavior
+    // and we should not look at stale data
+    if (mLastRefreshRate.animatingOrInfrequent) {
+        clearHistory(now);
+    }
+
+    auto refreshRate = calculateRefreshRateIfPossible(now);
     if (refreshRate.has_value()) {
         ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
@@ -220,4 +233,65 @@
     return {LayerHistory::LayerVoteType::Max, 0};
 }
 
+const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+    if (mTraceTags.count(type) == 0) {
+        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+        mTraceTags.emplace(type, tag);
+    }
+
+    return mTraceTags.at(type).c_str();
+}
+
+LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
+LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
+    const std::string prefix = "LFPS ";
+    const std::string suffix = "Heuristic ";
+    return {.min = prefix + mName + suffix + "min",
+            .max = prefix + mName + suffix + "max",
+            .consistent = prefix + mName + suffix + "consistent",
+            .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfoV2::RefreshRateHistory::clear() {
+    mRefreshRates.clear();
+}
+
+bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+    mRefreshRates.push_back({refreshRate, now});
+    while (mRefreshRates.size() >= HISTORY_SIZE ||
+           now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+        mRefreshRates.pop_front();
+    }
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+    }
+
+    return isConsistent();
+}
+
+bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
+    if (mRefreshRates.empty()) return true;
+
+    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+    }
+
+    return consistent;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 9e6783f..33dc66f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -56,6 +56,12 @@
     friend class LayerHistoryTestV2;
 
 public:
+    static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+        sRefreshRateConfigs = &refreshRateConfigs;
+    }
+
     LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
                 LayerHistory::LayerVoteType defaultVote);
 
@@ -86,6 +92,9 @@
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
+    // Returns a C string for tracing a vote
+    const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
     void onLayerInactive(nsecs_t now) {
         // Mark mFrameTimeValidSince to now to ignore all previous frame times.
         // We are not deleting the old frame to keep track of whether we should treat the first
@@ -93,7 +102,8 @@
         // posting infrequent updates.
         const auto timePoint = std::chrono::nanoseconds(now);
         mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
-        mLastReportedRefreshRate = 0.0f;
+        mLastRefreshRate = {};
+        mRefreshRateHistory.clear();
     }
 
     void clearHistory(nsecs_t now) {
@@ -109,12 +119,73 @@
         bool pendingConfigChange;
     };
 
+    // Holds information about the calculated and reported refresh rate
+    struct RefreshRateHeuristicData {
+        // Rate calculated on the layer
+        float calculated = 0.0f;
+        // Last reported rate for LayerInfoV2::getRefreshRate()
+        float reported = 0.0f;
+        // Whether the last reported rate for LayerInfoV2::getRefreshRate()
+        // was due to animation or infrequent updates
+        bool animatingOrInfrequent = false;
+    };
+
+    // Holds information about the layer vote
+    struct LayerVote {
+        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+        float fps = 0.0f;
+    };
+
+    // Class to store past calculated refresh rate and determine whether
+    // the refresh rate calculated is consistent with past values
+    class RefreshRateHistory {
+    public:
+        static constexpr auto HISTORY_SIZE = 90;
+        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+        RefreshRateHistory(const std::string& name) : mName(name) {}
+
+        // Clears History
+        void clear();
+
+        // Adds a new refresh rate and returns true if it is consistent
+        bool add(float refreshRate, nsecs_t now);
+
+    private:
+        friend class LayerHistoryTestV2;
+
+        // Holds the refresh rate when it was calculated
+        struct RefreshRateData {
+            float refreshRate = 0.0f;
+            nsecs_t timestamp = 0;
+
+            bool operator<(const RefreshRateData& other) const {
+                return refreshRate < other.refreshRate;
+            }
+        };
+
+        // Holds tracing strings
+        struct HeuristicTraceTagData {
+            std::string min;
+            std::string max;
+            std::string consistent;
+            std::string average;
+        };
+
+        bool isConsistent() const;
+        HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+        const std::string mName;
+        mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+        std::deque<RefreshRateData> mRefreshRates;
+        static constexpr float MARGIN_FPS = 1.0;
+    };
+
     bool isFrequent(nsecs_t now) const;
     bool isAnimating(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
-    std::optional<float> calculateRefreshRateIfPossible();
-    std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
-    bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;
+    std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+    std::optional<nsecs_t> calculateAverageFrameTime() const;
     bool isFrameTimeValid(const FrameTimeData&) const;
 
     const std::string mName;
@@ -123,23 +194,27 @@
     const nsecs_t mHighRefreshRatePeriod;
     LayerHistory::LayerVoteType mDefaultVote;
 
+    LayerVote mLayerVote;
+
     nsecs_t mLastUpdatedTime = 0;
 
     nsecs_t mLastAnimationTime = 0;
 
-    float mLastReportedRefreshRate = 0.0f;
-
-    // Holds information about the layer vote
-    struct {
-        LayerHistory::LayerVoteType type;
-        float fps;
-    } mLayerVote;
+    RefreshRateHeuristicData mLastRefreshRate;
 
     std::deque<FrameTimeData> mFrameTimes;
     std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
             std::chrono::steady_clock::now();
-    static constexpr size_t HISTORY_SIZE = 90;
-    static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+    static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+    static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+    RefreshRateHistory mRefreshRateHistory;
+
+    mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+    // Shared for all LayerInfo instances
+    static const RefreshRateConfigs* sRefreshRateConfigs;
+    static bool sTraceEnabled;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6067e69..47a4f42 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <binder/IPCThreadState.h>
 
@@ -28,6 +26,7 @@
 #include <gui/IDisplayEventConnection.h>
 
 #include "EventThread.h"
+#include "FrameTimeline.h"
 #include "MessageQueue.h"
 #include "SurfaceFlinger.h"
 
@@ -39,8 +38,9 @@
     }
 }
 
-void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) {
+void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
     if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+        mVsyncId = vsyncId;
         mExpectedVSyncTime = expectedVSyncTimestamp;
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
@@ -50,11 +50,11 @@
     switch (message.what) {
         case INVALIDATE:
             android_atomic_and(~eventMaskInvalidate, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
         case REFRESH:
             android_atomic_and(~eventMaskRefresh, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
     }
 }
@@ -67,15 +67,58 @@
     mHandler = new Handler(*this);
 }
 
+// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
+// and remove the EventThread from MessageQueue
 void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
     if (mEventTube.getFd() >= 0) {
         mLooper->removeFd(mEventTube.getFd());
     }
 
     mEvents = connection;
-    mEvents->stealReceiveChannel(&mEventTube);
-    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                   this);
+    if (mEvents) {
+        mEvents->stealReceiveChannel(&mEventTube);
+        mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+                       this);
+    }
+}
+
+void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+    ATRACE_CALL();
+    // Trace VSYNC-sf
+    mVsync.value = (mVsync.value + 1) % 2;
+
+    {
+        std::lock_guard lock(mVsync.mutex);
+        mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+        mVsync.mScheduled = false;
+    }
+    mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
+                                         {targetWakeupTime, readyTime, vsyncTime}),
+                                 vsyncTime);
+}
+
+void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+                             frametimeline::TokenManager& tokenManager,
+                             std::chrono::nanoseconds workDuration) {
+    setDuration(workDuration);
+    mVsync.tokenManager = &tokenManager;
+    mVsync.registration = std::make_unique<
+            scheduler::VSyncCallbackRegistration>(dispatch,
+                                                  std::bind(&MessageQueue::vsyncCallback, this,
+                                                            std::placeholders::_1,
+                                                            std::placeholders::_2,
+                                                            std::placeholders::_3),
+                                                  "sf");
+}
+
+void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
+    ATRACE_CALL();
+    std::lock_guard lock(mVsync.mutex);
+    mVsync.workDuration = workDuration;
+    if (mVsync.mScheduled) {
+        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+                                       mVsync.lastCallbackTime.count()});
+    }
 }
 
 void MessageQueue::waitMessage() {
@@ -105,7 +148,15 @@
 }
 
 void MessageQueue::invalidate() {
-    mEvents->requestNextVsync();
+    ATRACE_CALL();
+    if (mEvents) {
+        mEvents->requestNextVsync();
+    } else {
+        std::lock_guard lock(mVsync.mutex);
+        mVsync.mScheduled = true;
+        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+                                       mVsync.lastCallbackTime.count()});
+    }
 }
 
 void MessageQueue::refresh() {
@@ -123,7 +174,8 @@
     while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp);
+                mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
+                                             buffer[i].vsync.expectedVSyncTimestamp);
                 break;
             }
         }
@@ -133,5 +185,3 @@
 
 } // namespace android::impl
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 132b416..99ce3a6 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,6 +29,8 @@
 #include <private/gui/BitTube.h>
 
 #include "EventThread.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
 
 namespace android {
 
@@ -63,6 +65,9 @@
     virtual ~MessageQueue() = default;
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+    virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                           std::chrono::nanoseconds workDuration) = 0;
+    virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
     virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
@@ -74,18 +79,20 @@
 
 namespace impl {
 
-class MessageQueue final : public android::MessageQueue {
+class MessageQueue : public android::MessageQueue {
+protected:
     class Handler : public MessageHandler {
         enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
         MessageQueue& mQueue;
         int32_t mEventMask;
+        std::atomic<int64_t> mVsyncId;
         std::atomic<nsecs_t> mExpectedVSyncTime;
 
     public:
         explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
-        virtual void handleMessage(const Message& message);
-        void dispatchRefresh();
-        void dispatchInvalidate(nsecs_t expectedVSyncTimestamp);
+        void handleMessage(const Message& message) override;
+        virtual void dispatchRefresh();
+        virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
     };
 
     friend class Handler;
@@ -93,15 +100,34 @@
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
     sp<EventThreadConnection> mEvents;
+
+    struct Vsync {
+        frametimeline::TokenManager* tokenManager = nullptr;
+        std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
+
+        std::mutex mutex;
+        TracedOrdinal<std::chrono::nanoseconds> workDuration
+                GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
+        std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+        bool mScheduled GUARDED_BY(mutex) = false;
+        TracedOrdinal<int> value = {"VSYNC-sf", 0};
+    };
+
+    Vsync mVsync;
+
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
 
     static int cb_eventReceiver(int fd, int events, void* data);
     int eventReceiver(int fd, int events);
+    void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
 
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
+    void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                   std::chrono::nanoseconds workDuration) override;
+    void setDuration(std::chrono::nanoseconds workDuration) override;
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index a90d05e..2783800 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -20,6 +20,21 @@
 #include <sstream>
 #include <thread>
 
+namespace {
+using namespace std::chrono_literals;
+
+constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+// The syscall interface uses a pair of integers for the timestamp. The first
+// (tv_sec) is the whole count of seconds. The second (tv_nsec) is the
+// nanosecond part of the count. This function takes care of translation.
+void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) {
+    clock_gettime(CLOCK_MONOTONIC, spec);
+    spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds);
+    spec->tv_nsec += timestamp.count() % kNsToSeconds;
+}
+} // namespace
+
 namespace android {
 namespace scheduler {
 
@@ -32,81 +47,95 @@
 }
 
 void OneShotTimer::start() {
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mState = TimerState::RESET;
+    sem_init(&mSemaphore, 0, 0);
+
+    if (!mThread.joinable()) {
+        // Only create thread if it has not been created.
+        mThread = std::thread(&OneShotTimer::loop, this);
     }
-    mThread = std::thread(&OneShotTimer::loop, this);
 }
 
 void OneShotTimer::stop() {
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mState = TimerState::STOPPED;
-    }
-    mCondition.notify_all();
+    mStopTriggered = true;
+    sem_post(&mSemaphore);
+
     if (mThread.joinable()) {
         mThread.join();
+        sem_destroy(&mSemaphore);
     }
 }
 
 void OneShotTimer::loop() {
+    TimerState state = TimerState::RESET;
     while (true) {
         bool triggerReset = false;
         bool triggerTimeout = false;
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            if (mState == TimerState::STOPPED) {
-                break;
-            }
 
-            if (mState == TimerState::IDLE) {
-                mCondition.wait(mMutex);
-                continue;
-            }
-
-            if (mState == TimerState::RESET) {
-                triggerReset = true;
-            }
+        state = checkForResetAndStop(state);
+        if (state == TimerState::STOPPED) {
+            break;
         }
+
+        if (state == TimerState::IDLE) {
+            sem_wait(&mSemaphore);
+            continue;
+        }
+
+        if (state == TimerState::RESET) {
+            triggerReset = true;
+        }
+
         if (triggerReset && mResetCallback) {
             mResetCallback();
         }
 
-        { // lock the mutex again. someone might have called stop meanwhile
-            std::lock_guard<std::mutex> lock(mMutex);
-            if (mState == TimerState::STOPPED) {
-                break;
-            }
+        state = checkForResetAndStop(state);
+        if (state == TimerState::STOPPED) {
+            break;
+        }
 
-            auto triggerTime = std::chrono::steady_clock::now() + mInterval;
-            mState = TimerState::WAITING;
-            while (mState == TimerState::WAITING) {
-                constexpr auto zero = std::chrono::steady_clock::duration::zero();
-                auto waitTime = triggerTime - std::chrono::steady_clock::now();
-                if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
-                if (mState == TimerState::RESET) {
-                    triggerTime = std::chrono::steady_clock::now() + mInterval;
-                    mState = TimerState::WAITING;
-                } else if (mState == TimerState::WAITING &&
-                           (triggerTime - std::chrono::steady_clock::now()) <= zero) {
-                    triggerTimeout = true;
-                    mState = TimerState::IDLE;
-                }
+        auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+        state = TimerState::WAITING;
+        while (state == TimerState::WAITING) {
+            constexpr auto zero = std::chrono::steady_clock::duration::zero();
+            // Wait for mInterval time for semaphore signal.
+            struct timespec ts;
+            calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
+            sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+
+            state = checkForResetAndStop(state);
+            if (state == TimerState::RESET) {
+                triggerTime = std::chrono::steady_clock::now() + mInterval;
+                state = TimerState::WAITING;
+            } else if (state == TimerState::WAITING &&
+                       (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+                triggerTimeout = true;
+                state = TimerState::IDLE;
             }
         }
+
         if (triggerTimeout && mTimeoutCallback) {
             mTimeoutCallback();
         }
     }
 }
 
-void OneShotTimer::reset() {
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mState = TimerState::RESET;
+OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) {
+    // Stop takes precedence of the reset.
+    if (mStopTriggered.exchange(false)) {
+        return TimerState::STOPPED;
     }
-    mCondition.notify_all();
+    // If the state was stopped, the thread was joined, and we cannot reset
+    // the timer anymore.
+    if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) {
+        return TimerState::RESET;
+    }
+    return state;
+}
+
+void OneShotTimer::reset() {
+    mResetTriggered = true;
+    sem_post(&mSemaphore);
 }
 
 std::string OneShotTimer::dump() const {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index b005754..8bbd4f5 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <semaphore.h>
 #include <chrono>
 #include <condition_variable>
 #include <thread>
@@ -70,17 +71,15 @@
     // Function that loops until the condition for stopping is met.
     void loop();
 
+    // Checks whether mResetTriggered and mStopTriggered were set and updates
+    // mState if so.
+    TimerState checkForResetAndStop(TimerState state);
+
     // Thread waiting for timer to expire.
     std::thread mThread;
 
-    // Condition used to notify mThread.
-    std::condition_variable_any mCondition;
-
-    // Lock used for synchronizing the waiting thread with the application thread.
-    std::mutex mMutex;
-
-    // Current timer state
-    TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+    // Semaphore to keep mThread synchronized.
+    sem_t mSemaphore;
 
     // Interval after which timer expires.
     const Interval mInterval;
@@ -90,6 +89,12 @@
 
     // Callback that happens when timer expires.
     const TimeoutCallback mTimeoutCallback;
+
+    // After removing lock guarding mState, the state can be now accessed at
+    // any time. Keep a bool if the reset or stop were requested, and occasionally
+    // check in the main loop if they were.
+    std::atomic<bool> mResetTriggered = false;
+    std::atomic<bool> mStopTriggered = false;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
deleted file mode 100644
index fe2e406..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PhaseOffsets.h"
-
-#include <cutils/properties.h>
-
-#include <optional>
-
-#include "SurfaceFlingerProperties.h"
-
-namespace {
-
-std::optional<nsecs_t> getProperty(const char* name) {
-    char value[PROPERTY_VALUE_MAX];
-    property_get(name, value, "-1");
-    if (const int i = atoi(value); i != -1) return i;
-    return std::nullopt;
-}
-
-bool fpsEqualsWithMargin(float fpsA, float fpsB) {
-    static constexpr float MARGIN = 0.01f;
-    return std::abs(fpsA - fpsB) <= MARGIN;
-}
-
-std::vector<float> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<float> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate->getFps());
-    }
-
-    return refreshRates;
-}
-
-} // namespace
-
-namespace android::scheduler {
-
-PhaseConfiguration::~PhaseConfiguration() = default;
-
-namespace impl {
-
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
-                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                     sysprop::vsync_event_phase_offset_ns(1000000),
-                     sysprop::vsync_sf_event_phase_offset_ns(1000000),
-                     getProperty("debug.sf.early_phase_offset_ns"),
-                     getProperty("debug.sf.early_gl_phase_offset_ns"),
-                     getProperty("debug.sf.early_app_phase_offset_ns"),
-                     getProperty("debug.sf.early_gl_app_phase_offset_ns"),
-                     // Below defines the threshold when an offset is considered to be negative,
-                     // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
-                     // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
-                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
-                     // vsync.
-                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
-                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
-
-PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
-                           nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
-                           std::optional<nsecs_t> earlySfOffsetNs,
-                           std::optional<nsecs_t> earlyGlSfOffsetNs,
-                           std::optional<nsecs_t> earlyAppOffsetNs,
-                           std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
-      : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
-        mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
-        mEarlySfOffsetNs(earlySfOffsetNs),
-        mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
-        mEarlyAppOffsetNs(earlyAppOffsetNs),
-        mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
-        mThresholdForNextVsync(thresholdForNextVsync),
-        mOffsets(initializeOffsets(refreshRates)),
-        mRefreshRateFps(currentFps) {}
-
-void PhaseOffsets::dump(std::string& result) const {
-    const auto [early, earlyGl, late] = getCurrentOffsets();
-    using base::StringAppendF;
-    StringAppendF(&result,
-                  "           app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
-                  "     early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
-                  "  GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
-                  "next VSYNC threshold: %9" PRId64 " ns\n",
-                  late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
-                  mThresholdForNextVsync);
-}
-
-std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
-        const std::vector<float>& refreshRates) const {
-    std::unordered_map<float, Offsets> offsets;
-
-    for (const auto& refreshRate : refreshRates) {
-        offsets.emplace(refreshRate,
-                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
-    }
-    return offsets;
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
-    if (fps > 65.0f) {
-        return getHighFpsOffsets(vsyncPeriod);
-    } else {
-        return getDefaultOffsets(vsyncPeriod);
-    }
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
-    return {
-            {
-                    mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
-                            ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
-                            : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
-                    mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
-            },
-            {
-                    mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
-                            ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
-                            : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
-                    mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
-            },
-            {
-                    mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
-                            ? mSfVSyncPhaseOffsetNs
-                            : mSfVSyncPhaseOffsetNs - vsyncDuration,
-
-                    mVSyncPhaseOffsetNs,
-            },
-    };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
-    const auto highFpsLateAppOffsetNs =
-            getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
-    const auto highFpsLateSfOffsetNs =
-            getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
-
-    const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
-    const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
-    const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
-    const auto highFpsEarlyGlAppOffsetNs =
-            getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-
-    return {
-            {
-                    highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
-                            ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
-                            : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
-                                    vsyncDuration,
-
-                    highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
-            },
-            {
-                    highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
-                                    mThresholdForNextVsync
-                            ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
-                            : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
-                                    vsyncDuration,
-
-                    highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
-            },
-            {
-                    highFpsLateSfOffsetNs < mThresholdForNextVsync
-                            ? highFpsLateSfOffsetNs
-                            : highFpsLateSfOffsetNs - vsyncDuration,
-
-                    highFpsLateAppOffsetNs,
-            },
-    };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
-    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
-                                   [&fps](const std::pair<float, Offsets>& candidateFps) {
-                                       return fpsEqualsWithMargin(fps, candidateFps.first);
-                                   });
-
-    if (iter != mOffsets.end()) {
-        return iter->second;
-    }
-
-    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
-    // In this case just construct the offset.
-    ALOGW("Can't find offset for %.2f fps", fps);
-    return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
-}
-
-static void validateSysprops() {
-    const auto validatePropertyBool = [](const char* prop) {
-        LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
-    };
-
-    validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
-
-    LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
-                        "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
-                        "duration");
-
-    LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
-                        "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
-                        "duration");
-
-    const auto validateProperty = [](const char* prop) {
-        LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
-                            "%s is set to %" PRId64 " but expecting duration", prop,
-                            getProperty(prop).value_or(-1));
-    };
-
-    validateProperty("debug.sf.early_phase_offset_ns");
-    validateProperty("debug.sf.early_gl_phase_offset_ns");
-    validateProperty("debug.sf.early_app_phase_offset_ns");
-    validateProperty("debug.sf.early_gl_app_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_early_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
-    validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-}
-
-static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
-    return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
-}
-
-static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
-    return sfDuration == -1 ? 1'000'000
-                            : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
-}
-
-PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
-    return Offsets{
-            {
-                    mSfEarlyDuration < vsyncDuration
-                            ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
-                            : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
-
-                    appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
-            },
-            {
-                    mSfEarlyGlDuration < vsyncDuration
-                            ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
-                            : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
-
-                    appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
-            },
-            {
-                    mSfDuration < vsyncDuration
-                            ? sfDurationToOffset(mSfDuration, vsyncDuration)
-                            : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
-
-                    appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
-            },
-    };
-}
-
-std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
-        const std::vector<float>& refreshRates) const {
-    std::unordered_map<float, Offsets> offsets;
-
-    for (const auto fps : refreshRates) {
-        offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
-    }
-    return offsets;
-}
-
-PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
-                       refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                       getProperty("debug.sf.late.sf.duration").value_or(-1),
-                       getProperty("debug.sf.late.app.duration").value_or(-1),
-                       getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
-                       getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
-                       getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
-                       getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
-    validateSysprops();
-}
-
-PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
-                               nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
-                               nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
-                               nsecs_t appEarlyGlDuration)
-      : mSfDuration(sfDuration),
-        mAppDuration(appDuration),
-        mSfEarlyDuration(sfEarlyDuration),
-        mAppEarlyDuration(appEarlyDuration),
-        mSfEarlyGlDuration(sfEarlyGlDuration),
-        mAppEarlyGlDuration(appEarlyGlDuration),
-        mOffsets(initializeOffsets(refreshRates)),
-        mRefreshRateFps(currentFps) {}
-
-PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
-    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
-        return fpsEqualsWithMargin(fps, candidateFps.first);
-    });
-
-    if (iter != mOffsets.end()) {
-        return iter->second;
-    }
-
-    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
-    // In this case just construct the offset.
-    ALOGW("Can't find offset for %.2f fps", fps);
-    return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
-}
-
-void PhaseDurations::dump(std::string& result) const {
-    const auto [early, earlyGl, late] = getCurrentOffsets();
-    using base::StringAppendF;
-    StringAppendF(&result,
-                  "           app phase:    %9" PRId64 " ns\t         SF phase:    %9" PRId64
-                  " ns\n"
-                  "           app duration: %9" PRId64 " ns\t         SF duration: %9" PRId64
-                  " ns\n"
-                  "     early app phase:    %9" PRId64 " ns\t   early SF phase:    %9" PRId64
-                  " ns\n"
-                  "     early app duration: %9" PRId64 " ns\t   early SF duration: %9" PRId64
-                  " ns\n"
-                  "  GL early app phase:    %9" PRId64 " ns\tGL early SF phase:    %9" PRId64
-                  " ns\n"
-                  "  GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
-                  " ns\n",
-                  late.app,
-
-                  late.sf,
-
-                  mAppDuration, mSfDuration,
-
-                  early.app, early.sf,
-
-                  mAppEarlyDuration, mSfEarlyDuration,
-
-                  earlyGl.app,
-
-                  earlyGl.sf,
-
-                  mAppEarlyGlDuration, mSfEarlyGlDuration);
-}
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
deleted file mode 100644
index 9ec6d56..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include "RefreshRateConfigs.h"
-#include "VSyncModulator.h"
-
-namespace android::scheduler {
-
-/*
- * This class encapsulates offsets for different refresh rates. Depending
- * on what refresh rate we are using, and wheter we are composing in GL,
- * different offsets will help us with latency. This class keeps track of
- * which mode the device is on, and returns approprate offsets when needed.
- */
-class PhaseConfiguration {
-public:
-    using Offsets = VSyncModulator::OffsetsConfig;
-
-    virtual ~PhaseConfiguration();
-
-    virtual Offsets getCurrentOffsets() const = 0;
-    virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
-
-    virtual void setRefreshRateFps(float fps) = 0;
-
-    virtual void dump(std::string& result) const = 0;
-};
-
-namespace impl {
-
-/*
- * This is the old implementation of phase offsets and considered as deprecated.
- * PhaseDurations is the new implementation.
- */
-class PhaseOffsets : public scheduler::PhaseConfiguration {
-public:
-    PhaseOffsets(const scheduler::RefreshRateConfigs&);
-
-    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    Offsets getOffsetsForRefreshRate(float fps) const override;
-
-    // Returns early, early GL, and late offsets for Apps and SF.
-    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
-    // This function should be called when the device is switching between different
-    // refresh rates, to properly update the offsets.
-    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
-    // Returns current offsets in human friendly format.
-    void dump(std::string& result) const override;
-
-protected:
-    // Used for unit tests
-    PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
-                 nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
-                 std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
-                 std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
-                 nsecs_t thresholdForNextVsync);
-    std::unordered_map<float, Offsets> initializeOffsets(
-            const std::vector<float>& refreshRates) const;
-    Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
-    Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
-    Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
-
-    const nsecs_t mVSyncPhaseOffsetNs;
-    const nsecs_t mSfVSyncPhaseOffsetNs;
-    const std::optional<nsecs_t> mEarlySfOffsetNs;
-    const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
-    const std::optional<nsecs_t> mEarlyAppOffsetNs;
-    const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
-    const nsecs_t mThresholdForNextVsync;
-    const std::unordered_map<float, Offsets> mOffsets;
-
-    std::atomic<float> mRefreshRateFps;
-};
-
-/*
- * Class that encapsulates the phase offsets for SurfaceFlinger and App.
- * The offsets are calculated from durations for each one of the (late, early, earlyGL)
- * offset types.
- */
-class PhaseDurations : public scheduler::PhaseConfiguration {
-public:
-    PhaseDurations(const scheduler::RefreshRateConfigs&);
-
-    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    Offsets getOffsetsForRefreshRate(float fps) const override;
-
-    // Returns early, early GL, and late offsets for Apps and SF.
-    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
-    // This function should be called when the device is switching between different
-    // refresh rates, to properly update the offsets.
-    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
-    // Returns current offsets in human friendly format.
-    void dump(std::string& result) const override;
-
-protected:
-    // Used for unit tests
-    PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
-                   nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
-                   nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
-
-private:
-    std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
-    PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
-
-    const nsecs_t mSfDuration;
-    const nsecs_t mAppDuration;
-
-    const nsecs_t mSfEarlyDuration;
-    const nsecs_t mAppEarlyDuration;
-
-    const nsecs_t mSfEarlyGlDuration;
-    const nsecs_t mAppEarlyGlDuration;
-
-    const std::unordered_map<float, Offsets> mOffsets;
-
-    std::atomic<float> mRefreshRateFps;
-};
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 3c8bd68..150f925 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -48,6 +48,13 @@
     }
 }
 
+std::string RefreshRateConfigs::Policy::toString() const {
+    return base::StringPrintf("default config ID: %d, allowGroupSwitching = %d"
+                              ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]",
+                              defaultConfig.value(), allowGroupSwitching, primaryRange.min,
+                              primaryRange.max, appRequestRange.min, appRequestRange.max);
+}
+
 const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
         const std::vector<LayerRequirement>& layers) const {
     std::lock_guard lock(mLock);
@@ -115,12 +122,24 @@
 }
 
 const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
-        const std::vector<LayerRequirement>& layers, bool touchActive, bool idle,
-        bool* touchConsidered) const {
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
     ATRACE_CALL();
     ALOGV("getRefreshRateForContent %zu layers", layers.size());
 
-    if (touchConsidered) *touchConsidered = false;
+    if (outSignalsConsidered) *outSignalsConsidered = {};
+    const auto setTouchConsidered = [&] {
+        if (outSignalsConsidered) {
+            outSignalsConsidered->touch = true;
+        }
+    };
+
+    const auto setIdleConsidered = [&] {
+        if (outSignalsConsidered) {
+            outSignalsConsidered->idle = true;
+        }
+    };
+
     std::lock_guard lock(mLock);
 
     int noVoteLayers = 0;
@@ -150,9 +169,9 @@
 
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
-    if (touchActive && !hasExplicitVoteLayers) {
+    if (globalSignals.touch && !hasExplicitVoteLayers) {
         ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
-        if (touchConsidered) *touchConsidered = true;
+        setTouchConsidered();
         return getMaxRefreshRateByPolicyLocked();
     }
 
@@ -162,8 +181,10 @@
     const Policy* policy = getCurrentPolicyLocked();
     const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
 
-    if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+    if (!globalSignals.touch && globalSignals.idle &&
+        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+        setIdleConsidered();
         return getMinRefreshRateByPolicyLocked();
     }
 
@@ -198,10 +219,9 @@
             bool inPrimaryRange =
                     scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
-                layer.vote != LayerVoteType::ExplicitDefault &&
-                layer.vote != LayerVoteType::ExplicitExactOrMultiple) {
-                // Only layers with explicit frame rate settings are allowed to score refresh rates
-                // outside the primary range.
+                !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
+                // Only focused layers with ExplicitDefault frame rate settings are allowed to score
+                // refresh rates outside the primary range.
                 continue;
             }
 
@@ -242,7 +262,7 @@
 
             if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
                 layer.vote == LayerVoteType::Heuristic) {
-                const auto layerScore = [&]() {
+                const auto layerScore = [&] {
                     // Calculate how many display vsyncs we need to present a single frame for this
                     // layer
                     const auto [displayFramesQuot, displayFramesRem] =
@@ -307,9 +327,9 @@
     // actually increase the refresh rate over the normal selection.
     const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
 
-    if (touchActive && explicitDefaultVoteLayers == 0 &&
+    if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
         bestRefreshRate->fps < touchRefreshRate.fps) {
-        if (touchConsidered) *touchConsidered = true;
+        setTouchConsidered();
         ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
         return touchRefreshRate;
     }
@@ -384,7 +404,8 @@
 
 RefreshRateConfigs::RefreshRateConfigs(
         const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
-        HwcConfigIndexType currentConfigId) {
+        HwcConfigIndexType currentConfigId)
+      : mKnownFrameRates(constructKnownFrameRates(configs)) {
     LOG_ALWAYS_FATAL_IF(configs.empty());
     LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
 
@@ -412,10 +433,12 @@
     // defaultConfig must be a valid config, and within the given refresh rate range.
     auto iter = mRefreshRates.find(policy.defaultConfig);
     if (iter == mRefreshRates.end()) {
+        ALOGE("Default config is not found.");
         return false;
     }
     const RefreshRate& refreshRate = *iter->second;
     if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+        ALOGE("Default config is not in the primary range.");
         return false;
     }
     return policy.appRequestRange.min <= policy.primaryRange.min &&
@@ -425,6 +448,7 @@
 status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
     std::lock_guard lock(mLock);
     if (!isPolicyValid(policy)) {
+        ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
     Policy previousPolicy = *getCurrentPolicyLocked();
@@ -544,4 +568,96 @@
                        &mAppRequestRefreshRates);
 }
 
+std::vector<float> RefreshRateConfigs::constructKnownFrameRates(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+    std::vector<float> knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f};
+    knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+    // Add all supported refresh rates to the set
+    for (const auto& config : configs) {
+        const auto refreshRate = 1e9f / config->getVsyncPeriod();
+        knownFrameRates.emplace_back(refreshRate);
+    }
+
+    // Sort and remove duplicates
+    const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; };
+    std::sort(knownFrameRates.begin(), knownFrameRates.end());
+    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+                                      frameRatesEqual),
+                          knownFrameRates.end());
+    return knownFrameRates;
+}
+
+float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
+    if (frameRate <= *mKnownFrameRates.begin()) {
+        return *mKnownFrameRates.begin();
+    }
+
+    if (frameRate >= *std::prev(mKnownFrameRates.end())) {
+        return *std::prev(mKnownFrameRates.end());
+    }
+
+    auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate);
+
+    const auto distance1 = std::abs(frameRate - *lowerBound);
+    const auto distance2 = std::abs(frameRate - *std::prev(lowerBound));
+    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
+    std::lock_guard lock(mLock);
+    const auto& deviceMin = getMinRefreshRate();
+    const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
+    const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+
+    // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+    // the min allowed refresh rate is higher than the device min, we do not want to enable the
+    // timer.
+    if (deviceMin < minByPolicy) {
+        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    }
+    if (minByPolicy == maxByPolicy) {
+        // Do not sent the call to toggle off kernel idle timer if the device min and policy min and
+        // max are all the same. This saves us extra unnecessary calls to sysprop.
+        if (deviceMin == minByPolicy) {
+            return RefreshRateConfigs::KernelIdleTimerAction::NoChange;
+        }
+        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    }
+    // Turn on the timer in all other cases.
+    return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+}
+
+void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
+    if (refreshRateHz > 0 && refreshRateHz < 1) {
+        return;
+    }
+
+    std::lock_guard lock(mLock);
+    if (refreshRateHz != 0) {
+        mPreferredRefreshRateForUid[uid] = refreshRateHz;
+    } else {
+        mPreferredRefreshRateForUid.erase(uid);
+    }
+}
+
+int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
+    constexpr float kThreshold = 0.1f;
+    std::lock_guard lock(mLock);
+
+    const auto iter = mPreferredRefreshRateForUid.find(uid);
+    if (iter == mPreferredRefreshRateForUid.end()) {
+        return 1;
+    }
+
+    const auto refreshRateHz = iter->second;
+    const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
+    const auto numPeriodsRounded = std::round(numPeriods);
+    if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+        return 1;
+    }
+
+    return static_cast<int>(numPeriods);
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ff1eabd..8ff92a0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -29,7 +29,6 @@
 #include "Scheduler/StrongTyping.h"
 
 namespace android::scheduler {
-class RefreshRateConfigsTest;
 
 using namespace std::chrono_literals;
 
@@ -83,11 +82,13 @@
             return configId != other.configId || hwcConfig != other.hwcConfig;
         }
 
+        bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }
+
         bool operator==(const RefreshRate& other) const { return !(*this != other); }
 
     private:
         friend RefreshRateConfigs;
-        friend RefreshRateConfigsTest;
+        friend class RefreshRateConfigsTest;
 
         // The tolerance within which we consider FPS approximately equals.
         static constexpr float FPS_EPSILON = 0.001f;
@@ -107,6 +108,10 @@
             std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
 
     struct Policy {
+    private:
+        static constexpr int kAllowGroupSwitchingDefault = false;
+
+    public:
         struct Range {
             float min = 0;
             float max = std::numeric_limits<float>::max();
@@ -121,6 +126,8 @@
         // The default config, used to ensure we only initiate display config switches within the
         // same config group as defaultConfigId's group.
         HwcConfigIndexType defaultConfig;
+        // Whether or not we switch config groups to get the best frame rate.
+        bool allowGroupSwitching = kAllowGroupSwitchingDefault;
         // The primary refresh rate range represents display manager's general guidance on the
         // display configs we'll consider when switching refresh rates. Unless we get an explicit
         // signal from an app, we should stay within this range.
@@ -132,15 +139,23 @@
         // app request range. The app request range will be greater than or equal to the primary
         // refresh rate range, never smaller.
         Range appRequestRange;
-        // Whether or not we switch config groups to get the best frame rate. Only used by tests.
-        bool allowGroupSwitching = false;
 
         Policy() = default;
+
         Policy(HwcConfigIndexType defaultConfig, const Range& range)
-              : Policy(defaultConfig, range, range) {}
+              : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
+
+        Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range)
+              : Policy(defaultConfig, allowGroupSwitching, range, range) {}
+
         Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
                const Range& appRequestRange)
+              : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
+
+        Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
+               const Range& primaryRange, const Range& appRequestRange)
               : defaultConfig(defaultConfig),
+                allowGroupSwitching(allowGroupSwitching),
                 primaryRange(primaryRange),
                 appRequestRange(appRequestRange) {}
 
@@ -151,6 +166,7 @@
         }
 
         bool operator!=(const Policy& other) const { return !(*this == other); }
+        std::string toString() const;
     };
 
     // Return code set*Policy() to indicate the current policy is unchanged.
@@ -194,15 +210,22 @@
     // Captures the layer requirements for a refresh rate. This will be used to determine the
     // display refresh rate.
     struct LayerRequirement {
-        std::string name;         // Layer's name. Used for debugging purposes.
-        LayerVoteType vote;       // Layer vote type.
-        float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
-        float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
-                      // impact this layer would have on choosing the refresh rate.
+        // Layer's name. Used for debugging purposes.
+        std::string name;
+        // Layer vote type.
+        LayerVoteType vote = LayerVoteType::NoVote;
+        // Layer's desired refresh rate, if applicable.
+        float desiredRefreshRate = 0.0f;
+        // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
+        // would have on choosing the refresh rate.
+        float weight = 0.0f;
+        // Whether layer is in focus or not based on WindowManager's state
+        bool focused = false;
 
         bool operator==(const LayerRequirement& other) const {
             return name == other.name && vote == other.vote &&
-                    desiredRefreshRate == other.desiredRefreshRate && weight == other.weight;
+                    desiredRefreshRate == other.desiredRefreshRate && weight == other.weight &&
+                    focused == other.focused;
         }
 
         bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
@@ -212,14 +235,22 @@
     const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
             EXCLUDES(mLock);
 
+    // Global state describing signals that affect refresh rate choice.
+    struct GlobalSignals {
+        // Whether the user touched the screen recently. Used to apply touch boost.
+        bool touch = false;
+        // True if the system hasn't seen any buffers posted to layers recently.
+        bool idle = false;
+    };
+
     // Returns the refresh rate that fits best to the given layers.
     //   layers - The layer requirements to consider.
-    //   touchActive - Whether the user touched the screen recently. Used to apply touch boost.
-    //   idle - True if the system hasn't seen any buffers posted to layers recently.
-    //   touchConsidered - An output param that tells the caller whether the refresh rate was chosen
-    //                     based on touch boost.
+    //   globalSignals - global state of touch and idle
+    //   outSignalsConsidered - An output param that tells the caller whether the refresh rate was
+    //                          chosen based on touch boost and/or idle timer.
     const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                          bool touchActive, bool idle, bool* touchConsidered) const
+                                          const GlobalSignals& globalSignals,
+                                          GlobalSignals* outSignalsConsidered = nullptr) const
             EXCLUDES(mLock);
 
     // Returns all the refresh rates supported by the device. This won't change at runtime.
@@ -258,11 +289,41 @@
     // Returns a string that represents the layer vote type
     static std::string layerVoteTypeString(LayerVoteType vote);
 
+    // Returns a known frame rate that is the closest to frameRate
+    float findClosestKnownFrameRate(float frameRate) const;
+
     RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
                        HwcConfigIndexType currentConfigId);
 
+    // Returns whether switching configs (refresh rate or resolution) is possible.
+    // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
+    // differ in resolution.
+    bool canSwitch() const { return mRefreshRates.size() > 1; }
+
+    // Class to enumerate options around toggling the kernel timer on and off. We have an option
+    // for no change to avoid extra calls to kernel.
+    enum class KernelIdleTimerAction {
+        NoChange, // Do not change the idle timer.
+        TurnOff,  // Turn off the idle timer.
+        TurnOn    // Turn on the idle timer.
+    };
+    // Checks whether kernel idle timer should be active depending the policy decisions around
+    // refresh rates.
+    KernelIdleTimerAction getIdleTimerAction() const;
+
+    // Stores the preferred refresh rate that an app should run at.
+    // refreshRate == 0 means no preference.
+    void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+
+    // Returns a divider for the current refresh rate
+    int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+
 private:
+    friend class RefreshRateConfigsTest;
+
     void constructAvailableRefreshRates() REQUIRES(mLock);
+    static std::vector<float> constructKnownFrameRates(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
 
     void getSortedRefreshRateList(
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
@@ -314,12 +375,18 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
+    std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
+
     // The min and max refresh rates supported by the device.
     // This will not change at runtime.
     const RefreshRate* mMinSupportedRefreshRate;
     const RefreshRate* mMaxSupportedRefreshRate;
 
     mutable std::mutex mLock;
+
+    // A sorted list of known frame rates that a Heuristic layer will choose
+    // from based on the closest value.
+    const std::vector<float> mKnownFrameRates;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e250bbf..a14019e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,17 +20,18 @@
 
 #include "Scheduler.h"
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
-#include <cutils/properties.h>
 #include <input/InputWindow.h>
 #include <system/window.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <FrameTimeline/FrameTimeline.h>
 #include <algorithm>
 #include <cinttypes>
 #include <cstdint>
@@ -39,9 +40,7 @@
 #include <numeric>
 
 #include "../Layer.h"
-#include "DispSync.h"
 #include "DispSyncSource.h"
-#include "EventControlThread.h"
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
 #include "OneShotTimer.h"
@@ -51,6 +50,7 @@
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
+#include "VsyncController.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -60,67 +60,80 @@
         }                                                            \
     } while (false)
 
+using namespace std::string_literals;
+
 namespace android {
 
-std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
-    // TODO (140302863) remove this and use the vsync_reactor system.
-    if (property_get_bool("debug.sf.vsync_reactor", true)) {
-        // TODO (144707443) tune Predictor tunables.
-        static constexpr int defaultRate = 60;
-        static constexpr auto initialPeriod =
-                std::chrono::duration<nsecs_t, std::ratio<1, defaultRate>>(1);
-        static constexpr size_t vsyncTimestampHistorySize = 20;
-        static constexpr size_t minimumSamplesForPrediction = 6;
-        static constexpr uint32_t discardOutlierPercent = 20;
-        auto tracker = std::make_unique<
-                scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
-                                                   initialPeriod)
-                                                   .count(),
-                                           vsyncTimestampHistorySize, minimumSamplesForPrediction,
-                                           discardOutlierPercent);
+namespace {
 
-        static constexpr auto vsyncMoveThreshold =
-                std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
-        static constexpr auto timerSlack =
-                std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
-        auto dispatch = std::make_unique<
-                scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
-                                                    timerSlack.count(), vsyncMoveThreshold.count());
-
-        static constexpr size_t pendingFenceLimit = 20;
-        return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
-                                                         std::move(dispatch), std::move(tracker),
-                                                         pendingFenceLimit, supportKernelTimer);
-    } else {
-        return std::make_unique<impl::DispSync>("SchedulerDispSync",
-                                                sysprop::running_without_sync_framework(true));
-    }
+std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
+    // TODO(b/144707443): Tune constants.
+    constexpr int kDefaultRate = 60;
+    constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
+    constexpr nsecs_t idealPeriod =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
+    constexpr size_t vsyncTimestampHistorySize = 20;
+    constexpr size_t minimumSamplesForPrediction = 6;
+    constexpr uint32_t discardOutlierPercent = 20;
+    return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
+                                                       minimumSamplesForPrediction,
+                                                       discardOutlierPercent);
 }
 
-Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                     const scheduler::RefreshRateConfigs& refreshRateConfig,
-                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
-                     bool useContentDetection)
-      : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
-        mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
-        mEventControlThread(new impl::EventControlThread(std::move(function))),
-        mSchedulerCallback(schedulerCallback),
-        mRefreshRateConfigs(refreshRateConfig),
-        mUseContentDetection(useContentDetection),
-        mUseContentDetectionV2(useContentDetectionV2) {
-    using namespace sysprop;
+std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
+    // TODO(b/144707443): Tune constants.
+    constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
+    constexpr std::chrono::nanoseconds timerSlack = 500us;
+    return std::make_unique<
+            scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
+                                                timerSlack.count(), vsyncMoveThreshold.count());
+}
 
-    if (mUseContentDetectionV2) {
-        mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
-    } else {
-        mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
+    if (!useContentDetection) return "off";
+    return useContentDetectionV2 ? "V2" : "V1";
+}
+
+} // namespace
+
+class PredictedVsyncTracer {
+public:
+    PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
+          : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
+                          "PredictedVsyncTracer") {
+        scheduleRegistration();
     }
 
-    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
+private:
+    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+    scheduler::VSyncCallbackRegistration mRegistration;
+
+    void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
+
+    void callback() {
+        mParity = !mParity;
+        scheduleRegistration();
+    }
+};
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+      : Scheduler(configs, callback,
+                  {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
+                   .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false),
+                   .useContentDetectionV2 =
+                           base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true)}) {}
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+                     Options options)
+      : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
+                  createLayerHistory(configs, options.useContentDetectionV2), options) {
+    using namespace sysprop;
+
+    const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
 
     if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
-        const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
-                                                  : &Scheduler::idleTimerCallback;
+        const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
+                                                          : &Scheduler::idleTimerCallback;
         mIdleTimer.emplace(
                 std::chrono::milliseconds(millis),
                 [this, callback] { std::invoke(callback, this, TimerState::Reset); },
@@ -146,18 +159,20 @@
     }
 }
 
-Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
-                     std::unique_ptr<EventControlThread> eventControlThread,
-                     const scheduler::RefreshRateConfigs& configs,
-                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
-                     bool useContentDetection)
-      : mSupportKernelTimer(false),
-        mPrimaryDispSync(std::move(primaryDispSync)),
-        mEventControlThread(std::move(eventControlThread)),
+Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs,
+                     ISchedulerCallback& schedulerCallback,
+                     std::unique_ptr<LayerHistory> layerHistory, Options options)
+      : mOptions(options),
+        mVsyncSchedule(std::move(schedule)),
+        mLayerHistory(std::move(layerHistory)),
         mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(configs),
-        mUseContentDetection(useContentDetection),
-        mUseContentDetectionV2(useContentDetectionV2) {}
+        mPredictedVsyncTracer(
+                base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
+                        ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
+                        : nullptr) {
+    mSchedulerCallback.setVsyncEnabled(false);
+}
 
 Scheduler::~Scheduler() {
     // Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -166,22 +181,57 @@
     mIdleTimer.reset();
 }
 
-DispSync& Scheduler::getPrimaryDispSync() {
-    return *mPrimaryDispSync;
+Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
+    auto clock = std::make_unique<scheduler::SystemClock>();
+    auto tracker = createVSyncTracker();
+    auto dispatch = createVSyncDispatch(*tracker);
+
+    // TODO(b/144707443): Tune constants.
+    constexpr size_t pendingFenceLimit = 20;
+    auto controller =
+            std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
+                                                      supportKernelTimer);
+    return {std::move(controller), std::move(tracker), std::move(dispatch)};
 }
 
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
-                                                                  nsecs_t phaseOffsetNs) {
-    return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
-                                            true /* traceVsync */, name);
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
+        const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+    if (!configs.canSwitch()) return nullptr;
+
+    if (useContentDetectionV2) {
+        return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+    }
+
+    return std::make_unique<scheduler::impl::LayerHistory>();
+}
+
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
+        const char* name, std::chrono::nanoseconds workDuration,
+        std::chrono::nanoseconds readyDuration, bool traceVsync) {
+    return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+                                                       readyDuration, traceVsync, name);
+}
+
+bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+    const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+    if (divider <= 1) {
+        return true;
+    }
+
+    return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
 }
 
 Scheduler::ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs,
+        const char* connectionName, frametimeline::TokenManager* tokenManager,
+        std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
-    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
-                                                           std::move(interceptCallback));
+    auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+    auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+        return !isVsyncValid(expectedVsyncTimestamp, uid);
+    };
+    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
+                                                           std::move(interceptCallback),
+                                                           std::move(throttleVsync));
     return createConnection(std::move(eventThread));
 }
 
@@ -192,6 +242,7 @@
     auto connection =
             createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
 
+    std::lock_guard<std::mutex> lock(mConnectionsLock);
     mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
     return handle;
 }
@@ -203,55 +254,128 @@
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
         ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+    std::lock_guard<std::mutex> lock(mConnectionsLock);
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
     return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
 }
 
 sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+    std::lock_guard<std::mutex> lock(mConnectionsLock);
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
     return mConnections[handle].connection;
 }
 
 void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
                                   bool connected) {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->onHotplugReceived(displayId, connected);
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+
+    thread->onHotplugReceived(displayId, connected);
 }
 
 void Scheduler::onScreenAcquired(ConnectionHandle handle) {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->onScreenAcquired();
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+    thread->onScreenAcquired();
 }
 
 void Scheduler::onScreenReleased(ConnectionHandle handle) {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->onScreenReleased();
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+    thread->onScreenReleased();
 }
 
-void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
+void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                              HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    // Cache the last reported config for primary display.
+    mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
+}
+
+void Scheduler::dispatchCachedReportedConfig() {
+    // Check optional fields first.
+    if (!mFeatures.configId.has_value()) {
+        ALOGW("No config ID found, not dispatching cached config.");
+        return;
+    }
+    if (!mFeatures.cachedConfigChangedParams.has_value()) {
+        ALOGW("No config changed params found, not dispatching cached config.");
+        return;
+    }
+
+    const auto configId = *mFeatures.configId;
+    const auto vsyncPeriod =
+            mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+
+    // If there is no change from cached config, there is no need to dispatch an event
+    if (configId == mFeatures.cachedConfigChangedParams->configId &&
+        vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+        return;
+    }
+
+    mFeatures.cachedConfigChangedParams->configId = configId;
+    mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
+    onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
+                                     mFeatures.cachedConfigChangedParams->displayId,
+                                     mFeatures.cachedConfigChangedParams->configId,
+                                     mFeatures.cachedConfigChangedParams->vsyncPeriod);
+}
+
+void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
+                                                 PhysicalDisplayId displayId,
+                                                 HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+    thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
 
 size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+    std::lock_guard<std::mutex> lock(mConnectionsLock);
     RETURN_IF_INVALID_HANDLE(handle, 0);
     return mConnections[handle].thread->getEventThreadConnectionCount();
 }
 
 void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections.at(handle).thread->dump(result);
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections.at(handle).thread.get();
+    }
+    thread->dump(result);
 }
 
-void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
-    RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->setPhaseOffset(phaseOffset);
+void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+                            std::chrono::nanoseconds readyDuration) {
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+    thread->setDuration(workDuration, readyDuration);
 }
 
-void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
-    stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime());
-    stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) {
+    stats->vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
+    stats->vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
 }
 
 Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -267,7 +391,9 @@
 
         auto eventThread =
                 std::make_unique<impl::EventThread>(std::move(vsyncSource),
-                                                    impl::EventThread::InterceptVSyncsCallback());
+                                                    /*tokenManager=*/nullptr,
+                                                    impl::EventThread::InterceptVSyncsCallback(),
+                                                    impl::EventThread::ThrottleVsyncCallback());
 
         mInjectorConnectionHandle = createConnection(std::move(eventThread));
     }
@@ -276,20 +402,20 @@
     return mInjectorConnectionHandle;
 }
 
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) {
+bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
     if (!mInjectVSyncs || !mVSyncInjector) {
         return false;
     }
 
-    mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime);
+    mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
     return true;
 }
 
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
-        mPrimaryDispSync->beginResync();
-        mEventControlThread->setVsyncEnabled(true);
+        mVsyncSchedule.tracker->resetModel();
+        mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
 }
@@ -297,8 +423,7 @@
 void Scheduler::disableHardwareVsync(bool makeUnavailable) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (mPrimaryHWVsyncEnabled) {
-        mEventControlThread->setVsyncEnabled(false);
-        mPrimaryDispSync->endResync();
+        mSchedulerCallback.setVsyncEnabled(false);
         mPrimaryHWVsyncEnabled = false;
     }
     if (makeUnavailable) {
@@ -338,11 +463,11 @@
 
 void Scheduler::setVsyncPeriod(nsecs_t period) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    mPrimaryDispSync->setPeriod(period);
+    mVsyncSchedule.controller->startPeriodTransition(period);
 
     if (!mPrimaryHWVsyncEnabled) {
-        mPrimaryDispSync->beginResync();
-        mEventControlThread->setVsyncEnabled(true);
+        mVsyncSchedule.tracker->resetModel();
+        mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
 }
@@ -354,8 +479,8 @@
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync =
-                    mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
+            needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+                                                                          periodFlushed);
         }
     }
 
@@ -367,7 +492,7 @@
 }
 
 void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
-    if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+    if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
         enableHardwareVsync();
     } else {
         disableHardwareVsync(false);
@@ -375,11 +500,7 @@
 }
 
 void Scheduler::setIgnorePresentFences(bool ignore) {
-    mPrimaryDispSync->setIgnorePresentFences(ignore);
-}
-
-nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
-    return mPrimaryDispSync->expectedPresentTime(now);
+    mVsyncSchedule.controller->setIgnorePresentFences(ignore);
 }
 
 void Scheduler::registerLayer(Layer* layer) {
@@ -388,25 +509,25 @@
     const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
     const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
 
-    if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+    if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
         mLayerHistory->registerLayer(layer, minFps, maxFps,
                                      scheduler::LayerHistory::LayerVoteType::NoVote);
-    } else if (!mUseContentDetection) {
+    } else if (!mOptions.useContentDetection) {
         // If the content detection feature is off, all layers are registered at Max. We still keep
         // the layer history, since we use it for other features (like Frame Rate API), so layers
         // still need to be registered.
         mLayerHistory->registerLayer(layer, minFps, maxFps,
                                      scheduler::LayerHistory::LayerVoteType::Max);
-    } else if (!mUseContentDetectionV2) {
+    } else if (!mOptions.useContentDetectionV2) {
         // In V1 of content detection, all layers are registered as Heuristic (unless it's
         // wallpaper).
         const auto highFps =
-                layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER ? minFps : maxFps;
+                layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps;
 
         mLayerHistory->registerLayer(layer, minFps, highFps,
                                      scheduler::LayerHistory::LayerVoteType::Heuristic);
     } else {
-        if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+        if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
             // Running Wallpaper at Min is considered as part of content detection.
             mLayerHistory->registerLayer(layer, minFps, maxFps,
                                          scheduler::LayerHistory::LayerVoteType::Min);
@@ -446,13 +567,21 @@
         mFeatures.contentDetectionV1 =
                 !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
 
-        newConfigId = calculateRefreshRateConfigIndexType();
+        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
         if (mFeatures.configId == newConfigId) {
+            // We don't need to change the config, but we might need to send an event
+            // about a config change, since it was suppressed due to a previous idleConsidered
+            if (!consideredSignals.idle) {
+                dispatchCachedReportedConfig();
+            }
             return;
         }
         mFeatures.configId = newConfigId;
         auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-        mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+        mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                             consideredSignals.idle ? ConfigEvent::None
+                                                                    : ConfigEvent::Changed);
     }
 }
 
@@ -463,19 +592,10 @@
 }
 
 void Scheduler::notifyTouchEvent() {
-    if (!mTouchTimer) return;
-
-    // Touch event will boost the refresh rate to performance.
-    // Clear Layer History to get fresh FPS detection.
-    // NOTE: Instead of checking all the layers, we should be checking the layer
-    // that is currently on top. b/142507166 will give us this capability.
-    std::lock_guard<std::mutex> lock(mFeatureStateLock);
-    if (mLayerHistory) {
-        // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
-
+    if (mTouchTimer) {
         mTouchTimer->reset();
 
-        if (mSupportKernelTimer && mIdleTimer) {
+        if (mOptions.supportKernelTimer && mIdleTimer) {
             mIdleTimer->reset();
         }
     }
@@ -514,7 +634,7 @@
                refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
-        // need to update the DispSync model anyway.
+        // need to update the VsyncController model anyway.
         disableHardwareVsync(false /* makeUnavailable */);
     }
 
@@ -522,64 +642,82 @@
 }
 
 void Scheduler::idleTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+    handleTimerStateChanged(&mFeatures.idleTimer, state);
     ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
 void Scheduler::touchTimerCallback(TimerState state) {
     const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
-    if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) {
-        mLayerHistory->clear();
+    // Touch event will boost the refresh rate to performance.
+    // Clear layer history to get fresh FPS detection.
+    // NOTE: Instead of checking all the layers, we should be checking the layer
+    // that is currently on top. b/142507166 will give us this capability.
+    if (handleTimerStateChanged(&mFeatures.touch, touch)) {
+        if (mLayerHistory) {
+            mLayerHistory->clear();
+        }
     }
     ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
 void Scheduler::displayPowerTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
-                            true /* eventOnContentDetection */);
+    handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
     ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
 void Scheduler::dump(std::string& result) const {
     using base::StringAppendF;
-    const char* const states[] = {"off", "on"};
 
-    StringAppendF(&result, "+  Idle timer: %s\n",
-                  mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
     StringAppendF(&result, "+  Touch timer: %s\n",
-                  mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
-    StringAppendF(&result, "+  Use content detection: %s\n\n",
-                  sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
+                  mTouchTimer ? mTouchTimer->dump().c_str() : "off");
+    StringAppendF(&result, "+  Content detection: %s %s\n\n",
+                  toContentDetectionString(mOptions.useContentDetection,
+                                           mOptions.useContentDetectionV2),
+                  mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+}
+
+void Scheduler::dumpVsync(std::string& s) const {
+    using base::StringAppendF;
+
+    StringAppendF(&s, "VSyncReactor:\n");
+    mVsyncSchedule.controller->dump(s);
+    StringAppendF(&s, "VSyncDispatch:\n");
+    mVsyncSchedule.dispatch->dump(s);
 }
 
 template <class T>
-bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
-    ConfigEvent event = ConfigEvent::None;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     HwcConfigIndexType newConfigId;
-    bool touchConsidered = false;
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (*currentState == newState) {
-            return touchConsidered;
+            return false;
         }
         *currentState = newState;
-        newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered);
+        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
         if (mFeatures.configId == newConfigId) {
-            return touchConsidered;
+            // We don't need to change the config, but we might need to send an event
+            // about a config change, since it was suppressed due to a previous idleConsidered
+            if (!consideredSignals.idle) {
+                dispatchCachedReportedConfig();
+            }
+            return consideredSignals.touch;
         }
         mFeatures.configId = newConfigId;
-        if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
-            event = ConfigEvent::Changed;
-        }
     }
     const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
-    return touchConsidered;
+    mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                         consideredSignals.idle ? ConfigEvent::None
+                                                                : ConfigEvent::Changed);
+    return consideredSignals.touch;
 }
 
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+        scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
     ATRACE_CALL();
-    if (touchConsidered) *touchConsidered = false;
+    if (consideredSignals) *consideredSignals = {};
 
     // If Display Power is not in normal operation we want to be in performance mode. When coming
     // back to normal mode, a grace period is given with DisplayPowerTimer.
@@ -592,7 +730,7 @@
     const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
     const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
 
-    if (!mUseContentDetectionV2) {
+    if (!mOptions.useContentDetectionV2) {
         // As long as touch is active we want to be in performance mode.
         if (touchActive) {
             return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
@@ -600,6 +738,7 @@
 
         // If timer has expired as it means there is no new content on the screen.
         if (idle) {
+            if (consideredSignals) consideredSignals->idle = true;
             return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
         }
 
@@ -615,7 +754,8 @@
     }
 
     return mRefreshRateConfigs
-            .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered)
+            .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+                                consideredSignals)
             .getConfigId();
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ebee9e3..4c86d26 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -29,7 +29,6 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop
 
-#include "EventControlThread.h"
 #include "EventThread.h"
 #include "LayerHistory.h"
 #include "OneShotTimer.h"
@@ -41,18 +40,29 @@
 using namespace std::chrono_literals;
 using scheduler::LayerHistory;
 
-class DispSync;
 class FenceTime;
 class InjectVSyncSource;
-struct DisplayStateInfo;
+class PredictedVsyncTracer;
 
-class ISchedulerCallback {
-public:
-    virtual ~ISchedulerCallback() = default;
+namespace scheduler {
+class VsyncController;
+class VSyncDispatch;
+class VSyncTracker;
+} // namespace scheduler
+
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
+struct ISchedulerCallback {
+    virtual void setVsyncEnabled(bool) = 0;
     virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
                                    scheduler::RefreshRateConfigEvent) = 0;
     virtual void repaintEverythingForHWC() = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
+
+protected:
+    ~ISchedulerCallback() = default;
 };
 
 class Scheduler {
@@ -60,19 +70,13 @@
     using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
     using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    // Indicates whether to start the transaction early, or at vsync time.
-    enum class TransactionStart { EARLY, NORMAL };
-
-    Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
-              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
-              bool useContentDetectionV2, bool useContentDetection);
-
-    virtual ~Scheduler();
-
-    DispSync& getPrimaryDispSync();
+    Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
+    ~Scheduler();
 
     using ConnectionHandle = scheduler::ConnectionHandle;
-    ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+    ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
+                                      std::chrono::nanoseconds workDuration,
+                                      std::chrono::nanoseconds readyDuration,
                                       impl::EventThread::InterceptVSyncsCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -81,23 +85,24 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
-                         nsecs_t vsyncPeriod);
-
+    void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+                                       HwcConfigIndexType configId, nsecs_t vsyncPeriod)
+            EXCLUDES(mFeatureStateLock);
+    void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+                                          HwcConfigIndexType configId, nsecs_t vsyncPeriod);
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
-    // Modifies phase offset in the event thread.
-    void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
+    // Modifies work duration in the event thread.
+    void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
+                     std::chrono::nanoseconds readyDuration);
 
-    void getDisplayStatInfo(DisplayStatInfo* stats);
+    void getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now);
 
     // Returns injector handle if injection has toggled, or an invalid handle otherwise.
     ConnectionHandle enableVSyncInjection(bool enable);
-
     // Returns false if injection is disabled.
-    bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime);
-
+    bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
 
@@ -109,13 +114,12 @@
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void resync();
 
-    // Passes a vsync sample to DispSync. periodFlushed will be true if
-    // DispSync detected that the vsync period changed, and false otherwise.
+    // Passes a vsync sample to VsyncController. periodFlushed will be true if
+    // VsyncController detected that the vsync period changed, and false otherwise.
     void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                          bool* periodFlushed);
     void addPresentFence(const std::shared_ptr<FenceTime>&);
     void setIgnorePresentFences(bool ignore);
-    nsecs_t getDispSyncExpectedPresentTime(nsecs_t now);
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
@@ -133,8 +137,15 @@
 
     void setDisplayPowerState(bool normal);
 
+    scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+
+    // Returns true if a given vsync timestamp is considered valid vsync
+    // for a given uid
+    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
+    void dumpVsync(std::string&) const;
 
     // Get the appropriate refresh for current conditions.
     std::optional<HwcConfigIndexType> getPreferredConfigId();
@@ -150,6 +161,11 @@
 
     size_t getEventThreadConnectionCount(ConnectionHandle handle);
 
+    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name,
+                                                           std::chrono::nanoseconds workDuration,
+                                                           std::chrono::nanoseconds readyDuration,
+                                                           bool traceVsync = true);
+
 private:
     friend class TestableScheduler;
 
@@ -159,12 +175,31 @@
     enum class TimerState { Reset, Expired };
     enum class TouchState { Inactive, Active };
 
-    // Used by tests to inject mocks.
-    Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
-              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
-              bool useContentDetectionV2, bool useContentDetection);
+    struct Options {
+        // Whether to use idle timer callbacks that support the kernel timer.
+        bool supportKernelTimer;
+        // Whether to use content detection at all.
+        bool useContentDetection;
+        // Whether to use improved content detection.
+        bool useContentDetectionV2;
+    };
 
-    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
+    struct VsyncSchedule {
+        std::unique_ptr<scheduler::VsyncController> controller;
+        std::unique_ptr<scheduler::VSyncTracker> tracker;
+        std::unique_ptr<scheduler::VSyncDispatch> dispatch;
+    };
+
+    // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
+    Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options);
+
+    // Used by tests to inject mocks.
+    Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
+              std::unique_ptr<LayerHistory>, Options);
+
+    static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
+    static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
+                                                            bool useContentDetectionV2);
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -179,16 +214,19 @@
 
     // handles various timer features to change the refresh rate.
     template <class T>
-    bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
+    bool handleTimerStateChanged(T* currentState, T newState);
 
     void setVsyncPeriod(nsecs_t period);
 
     // This function checks whether individual features that are affecting the refresh rate
     // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
     // for the suggested refresh rate.
-    HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr)
+    HwcConfigIndexType calculateRefreshRateConfigIndexType(
+            scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
             REQUIRES(mFeatureStateLock);
 
+    void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
@@ -196,7 +234,8 @@
     };
 
     ConnectionHandle::Id mNextConnectionHandleId = 0;
-    std::unordered_map<ConnectionHandle, Connection> mConnections;
+    mutable std::mutex mConnectionsLock;
+    std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
 
     bool mInjectVSyncs = false;
     InjectVSyncSource* mVSyncInjector = nullptr;
@@ -208,14 +247,11 @@
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
-    // Whether to use idle timer callbacks that support the kernel timer.
-    const bool mSupportKernelTimer;
-
-    std::unique_ptr<DispSync> mPrimaryDispSync;
-    std::unique_ptr<EventControlThread> mEventControlThread;
+    const Options mOptions;
+    VsyncSchedule mVsyncSchedule;
 
     // Used to choose refresh rate if content detection is enabled.
-    std::unique_ptr<LayerHistory> mLayerHistory;
+    const std::unique_ptr<LayerHistory> mLayerHistory;
 
     // Timer that records time between requests for next vsync.
     std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -240,6 +276,16 @@
         LayerHistory::Summary contentRequirements;
 
         bool isDisplayPowerStateNormal = true;
+
+        // Used to cache the last parameters of onPrimaryDisplayConfigChanged
+        struct ConfigChangedParams {
+            ConnectionHandle handle;
+            PhysicalDisplayId displayId;
+            HwcConfigIndexType configId;
+            nsecs_t vsyncPeriod;
+        };
+
+        std::optional<ConfigChangedParams> cachedConfigChangedParams;
     } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
@@ -249,10 +295,7 @@
             GUARDED_BY(mVsyncTimelineLock);
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
-    // This variable indicates whether to use the content detection feature at all.
-    const bool mUseContentDetection;
-    // This variable indicates whether to use V2 version of the content detection.
-    const bool mUseContentDetectionV2;
+    const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index da2195c..40dd841 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -43,10 +43,10 @@
     virtual ~TimeKeeper();
 
     /*
-     * Arms callback to fired in time nanoseconds.
+     * 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 alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+    virtual void alarmAt(std::function<void()> const& callback, nsecs_t time) = 0;
 
     /*
      * Cancels an existing pending callback
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 7c5058e..c9c2d84 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -88,8 +88,8 @@
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+void Timer::alarmAt(std::function<void()> const& cb, 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();
@@ -99,17 +99,17 @@
     struct itimerspec old_timer;
     struct itimerspec new_timer {
         .it_interval = {.tv_sec = 0, .tv_nsec = 0},
-        .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
-                     .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+        .it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
+                     .tv_nsec = static_cast<long>(time % ns_per_s)},
     };
 
-    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+    if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
         ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
     }
 }
 
 void Timer::alarmCancel() {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
     struct itimerspec old_timer;
     struct itimerspec new_timer {
@@ -192,7 +192,7 @@
                 setDebugState(DebugState::Running);
                 std::function<void()> cb;
                 {
-                    std::lock_guard<decltype(mMutex)> lk(mMutex);
+                    std::lock_guard lock(mMutex);
                     cb = mCallback;
                 }
                 if (cb) {
@@ -211,7 +211,7 @@
 }
 
 void Timer::setDebugState(DebugState state) {
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     mDebugState = state;
 }
 
@@ -233,7 +233,7 @@
 }
 
 void Timer::dump(std::string& result) const {
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
 }
 
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index a8e2d5a..69ce079 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -30,9 +30,9 @@
     ~Timer();
     nsecs_t now() const final;
 
-    // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+    // 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 alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+    void alarmAt(std::function<void()> const& cb, nsecs_t time) final;
     void alarmCancel() final;
     void dump(std::string& result) const final;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 2a2d7c5..9d71103 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -40,11 +40,13 @@
 
     /*
      * A callback that can be registered to be awoken at a given time relative to a vsync event.
-     * \param [in] vsyncTime The timestamp of the vsync the callback is for.
-     * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
-     *
+     * \param [in] vsyncTime:        The timestamp of the vsync the callback is for.
+     * \param [in] targetWakeupTime: The timestamp of intended wakeup time of the cb.
+     * \param [in] readyTime:        The timestamp of intended time where client needs to finish
+     *                               its work by.
      */
-    using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+    using Callback =
+            std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime)>;
 
     /*
      * Registers a callback that will be called at designated points on the vsync timeline.
@@ -71,33 +73,61 @@
     virtual void unregisterCallback(CallbackToken token) = 0;
 
     /*
+     * Timing information about a scheduled callback
+     *
+     * @workDuration:  The time needed for the client to perform its work
+     * @readyDuration: The time needed for the client to be ready before a vsync event.
+     *                 For external (non-SF) clients, not only do we need to account for their
+     *                 workDuration, but we also need to account for the time SF will take to
+     *                 process their buffer/transaction. In this case, readyDuration will be set
+     *                 to the SF duration in order to provide enough end-to-end time, and to be
+     *                 able to provide the ready-by time (deadline) on the callback.
+     *                 For internal clients, we don't need to add additional padding, so
+     *                 readyDuration will typically be 0.
+     * @earliestVsync: The targeted display time. This will be snapped to the closest
+     *                 predicted vsync time after earliestVsync.
+     *
+     * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+     * event.
+     */
+    struct ScheduleTiming {
+        nsecs_t workDuration = 0;
+        nsecs_t readyDuration = 0;
+        nsecs_t earliestVsync = 0;
+
+        bool operator==(const ScheduleTiming& other) const {
+            return workDuration == other.workDuration && readyDuration == other.readyDuration &&
+                    earliestVsync == other.earliestVsync;
+        }
+
+        bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
+    };
+
+    /*
      * Schedules the registered callback to be dispatched.
      *
-     * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+     * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+     * event.
      *
      * The caller designates the earliest vsync event that should be targeted by the earliestVsync
      * parameter.
-     * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
-     * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+     * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
+     * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
      *
-     * If (workDuration - earliestVsync) is in the past, or if a callback has already been
-     * dispatched for the predictedVsync, an error will be returned.
+     * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+     * already been dispatched for the predictedVsync, an error will be returned.
      *
      * It is valid to reschedule a callback to a different time.
      *
      * \param [in] token           The callback to schedule.
-     * \param [in] workDuration    The time before the actual vsync time to invoke the callback
-     *                             associated with token.
-     * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
-     *                             predicted vsync time after earliestVsync.
+     * \param [in] scheduleTiming  The timing information for this schedule call
      * \return                     A ScheduleResult::Scheduled if callback was scheduled.
      *                             A ScheduleResult::CannotSchedule
-     *                             if (workDuration - earliestVsync) is in the past, or
-     *                             if a callback was dispatched for the predictedVsync already.
-     *                             A ScheduleResult::Error if there was another error.
+     *                             if (workDuration + readyDuration - earliestVsync) is in the past,
+     * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
+     * there was another error.
      */
-    virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
-                                    nsecs_t earliestVsync) = 0;
+    virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
 
     /* Cancels a scheduled callback, if possible.
      *
@@ -129,7 +159,7 @@
     ~VSyncCallbackRegistration();
 
     // See documentation for VSyncDispatch::schedule.
-    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+    ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
 
     // See documentation for VSyncDispatch::cancel.
     CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index cd15617..ca6ea27 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -35,8 +35,6 @@
                                                            nsecs_t minVsyncDistance)
       : mName(name),
         mCallback(cb),
-        mWorkDuration(0),
-        mEarliestVsync(0),
         mMinVsyncDistance(minVsyncDistance) {}
 
 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
@@ -54,6 +52,13 @@
     return {mArmedInfo->mActualWakeupTime};
 }
 
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualReadyTime};
+}
+
 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
     if (!mArmedInfo) {
         return {};
@@ -61,10 +66,10 @@
     return {mArmedInfo->mActualVsyncTime};
 }
 
-ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
                                                       VSyncTracker& tracker, nsecs_t now) {
-    auto nextVsyncTime =
-            tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+    auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
 
     bool const wouldSkipAVsyncTarget =
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
@@ -80,20 +85,39 @@
                 tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
     }
 
-    auto const nextWakeupTime = nextVsyncTime - workDuration;
-    mWorkDuration = workDuration;
-    mEarliestVsync = earliestVsync;
-    mArmedInfo = {nextWakeupTime, nextVsyncTime};
+    auto const nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
+    auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
+    mScheduleTiming = timing;
+    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
     return ScheduleResult::Scheduled;
 }
 
+void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
+    mWorkloadUpdateInfo = timing;
+}
+
+bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
+    return mWorkloadUpdateInfo.has_value();
+}
+
 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
-    if (!mArmedInfo) {
+    if (!mArmedInfo && !mWorkloadUpdateInfo) {
         return;
     }
-    auto const nextVsyncTime =
-            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
-    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+
+    if (mWorkloadUpdateInfo) {
+        mScheduleTiming = *mWorkloadUpdateInfo;
+        mWorkloadUpdateInfo.reset();
+    }
+
+    const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
+    const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+
+    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
+    const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
+    const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
+
+    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
 }
 
 void VSyncDispatchTimerQueueEntry::disarm() {
@@ -106,13 +130,14 @@
     return *mLastDispatchTime;
 }
 
-void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
+                                            nsecs_t deadlineTimestamp) {
     {
         std::lock_guard<std::mutex> lk(mRunningMutex);
         mRunning = true;
     }
 
-    mCallback(vsyncTimestamp, wakeupTimestamp);
+    mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
 
     std::lock_guard<std::mutex> lk(mRunningMutex);
     mRunning = false;
@@ -128,15 +153,20 @@
     std::lock_guard<std::mutex> lk(mRunningMutex);
     std::string armedInfo;
     if (mArmedInfo) {
-        StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+        StringAppendF(&armedInfo,
+                      "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
                       (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+                      (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
                       (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
     }
 
     StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
                   mRunning ? "(in callback function)" : "", armedInfo.c_str());
-    StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
-                  mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+    StringAppendF(&result,
+                  "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+                  "to now\n",
+                  mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
+                  (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
 
     if (mLastDispatchTime) {
         StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -155,7 +185,7 @@
         mMinVsyncDistance(minVsyncDistance) {}
 
 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     cancelTimer();
 }
 
@@ -164,10 +194,10 @@
     mTimeKeeper->alarmCancel();
 }
 
-void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
     mIntendedWakeupTime = targetTime;
-    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
-                         targetTime - now);
+    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+                         mIntendedWakeupTime);
     mLastTimerSchedule = mTimeKeeper->now();
 }
 
@@ -192,7 +222,7 @@
     std::optional<std::string_view> nextWakeupName;
     for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
         auto& callback = it->second;
-        if (!callback->wakeupTime()) {
+        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
             continue;
         }
 
@@ -223,11 +253,13 @@
         std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
         nsecs_t vsyncTimestamp;
         nsecs_t wakeupTimestamp;
+        nsecs_t deadlineTimestamp;
     };
     std::vector<Invocation> invocations;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
-        mLastTimerCallback = mTimeKeeper->now();
+        std::lock_guard lock(mMutex);
+        auto const now = mTimeKeeper->now();
+        mLastTimerCallback = now;
         for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
             auto& callback = it->second;
             auto const wakeupTime = callback->wakeupTime();
@@ -235,10 +267,13 @@
                 continue;
             }
 
-            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+            auto const readyTime = callback->readyTime();
+
+            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
+            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                 callback->executing();
-                invocations.emplace_back(
-                        Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
+                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
+                                                    *wakeupTime, *readyTime});
             }
         }
 
@@ -247,13 +282,14 @@
     }
 
     for (auto const& invocation : invocations) {
-        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
+        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
+                                      invocation.deadlineTimestamp);
     }
 }
 
 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
         Callback const& callbackFn, std::string callbackName) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     return CallbackToken{
             mCallbacks
                     .emplace(++mCallbackToken,
@@ -266,7 +302,7 @@
 void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
     std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         auto it = mCallbacks.find(token);
         if (it != mCallbacks.end()) {
             entry = it->second;
@@ -279,11 +315,11 @@
     }
 }
 
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
-                                                 nsecs_t earliestVsync) {
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
+                                                 ScheduleTiming scheduleTiming) {
     auto result = ScheduleResult::Error;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
 
         auto it = mCallbacks.find(token);
         if (it == mCallbacks.end()) {
@@ -291,7 +327,16 @@
         }
         auto& callback = it->second;
         auto const now = mTimeKeeper->now();
-        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+
+        /* If the timer thread will run soon, we'll apply this work update via the callback
+         * timer recalculation to avoid cancelling a callback that is about to fire. */
+        auto const rearmImminent = now > mIntendedWakeupTime;
+        if (CC_UNLIKELY(rearmImminent)) {
+            callback->addPendingWorkloadUpdate(scheduleTiming);
+            return ScheduleResult::Scheduled;
+        }
+
+        result = callback->schedule(scheduleTiming, mTracker, now);
         if (result == ScheduleResult::CannotSchedule) {
             return result;
         }
@@ -305,7 +350,7 @@
 }
 
 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
     auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
@@ -313,17 +358,21 @@
     }
     auto& callback = it->second;
 
-    if (callback->wakeupTime()) {
+    auto const wakeupTime = callback->wakeupTime();
+    if (wakeupTime) {
         callback->disarm();
-        mIntendedWakeupTime = kInvalidTime;
-        rearmTimer(mTimeKeeper->now());
+
+        if (*wakeupTime == mIntendedWakeupTime) {
+            mIntendedWakeupTime = kInvalidTime;
+            rearmTimer(mTimeKeeper->now());
+        }
         return CancelResult::Cancelled;
     }
     return CancelResult::TooLate;
 }
 
 void VSyncDispatchTimerQueue::dump(std::string& result) const {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "\tTimer:\n");
     mTimeKeeper->dump(result);
     StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
@@ -365,11 +414,11 @@
     if (mValidToken) mDispatch.get().unregisterCallback(mToken);
 }
 
-ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mValidToken) {
         return ScheduleResult::Error;
     }
-    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+    return mDispatch.get().schedule(mToken, scheduleTiming);
 }
 
 CancelResult VSyncCallbackRegistration::cancel() {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 26a8ec0..26237b6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -47,7 +47,7 @@
     std::optional<nsecs_t> lastExecutedVsyncTarget() const;
 
     // This moves the state from disarmed->armed and will calculate the wakeupTime.
-    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+    ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker,
                             nsecs_t now);
     // This will update armed entries with the latest vsync information. Entry remains armed.
     void update(VSyncTracker& tracker, nsecs_t now);
@@ -56,6 +56,8 @@
     // It will not update the wakeupTime.
     std::optional<nsecs_t> wakeupTime() const;
 
+    std::optional<nsecs_t> readyTime() const;
+
     std::optional<nsecs_t> targetVsync() const;
 
     // This moves state from armed->disarmed.
@@ -64,10 +66,17 @@
     // This moves the state from armed->running.
     // Store the timestamp that this was intended for as the last called timestamp.
     nsecs_t executing();
+
+    // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
+    // call to update()
+    void addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming);
+
+    // Checks if there is a pending update to the workload, returning true if so.
+    bool hasPendingWorkloadUpdate() const;
     // End: functions that are not threadsafe.
 
     // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
-    void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
+    void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp, nsecs_t deadlineTimestamp);
     // Block calling thread while the callback is executing.
     void ensureNotRunning();
 
@@ -77,17 +86,19 @@
     std::string const mName;
     VSyncDispatch::Callback const mCallback;
 
-    nsecs_t mWorkDuration;
-    nsecs_t mEarliestVsync;
+    VSyncDispatch::ScheduleTiming mScheduleTiming;
     nsecs_t const mMinVsyncDistance;
 
     struct ArmingInfo {
         nsecs_t mActualWakeupTime;
         nsecs_t mActualVsyncTime;
+        nsecs_t mActualReadyTime;
     };
     std::optional<ArmingInfo> mArmedInfo;
     std::optional<nsecs_t> mLastDispatchTime;
 
+    std::optional<VSyncDispatch::ScheduleTiming> mWorkloadUpdateInfo;
+
     mutable std::mutex mRunningMutex;
     std::condition_variable mCv;
     bool mRunning GUARDED_BY(mRunningMutex) = false;
@@ -112,7 +123,7 @@
 
     CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
     void unregisterCallback(CallbackToken token) final;
-    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+    ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final;
     CancelResult cancel(CallbackToken token) final;
     void dump(std::string& result) const final;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
deleted file mode 100644
index 40a992c..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "VSyncModulator.h"
-
-#include <cutils/properties.h>
-#include <utils/Trace.h>
-
-#include <cinttypes>
-#include <mutex>
-
-namespace android::scheduler {
-
-VSyncModulator::VSyncModulator(Scheduler& scheduler,
-                               Scheduler::ConnectionHandle appConnectionHandle,
-                               Scheduler::ConnectionHandle sfConnectionHandle,
-                               const OffsetsConfig& config)
-      : mScheduler(scheduler),
-        mAppConnectionHandle(appConnectionHandle),
-        mSfConnectionHandle(sfConnectionHandle),
-        mOffsetsConfig(config) {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.vsync_trace_detailed_info", value, "0");
-    mTraceDetailedInfo = atoi(value);
-}
-
-void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mOffsetsConfig = config;
-    updateOffsetsLocked();
-}
-
-void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
-    if (transactionStart == Scheduler::TransactionStart::EARLY) {
-        mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
-    }
-
-    // An early transaction stays an early transaction.
-    if (transactionStart == mTransactionStart ||
-        mTransactionStart == Scheduler::TransactionStart::EARLY) {
-        return;
-    }
-    mTransactionStart = transactionStart;
-    updateOffsets();
-}
-
-void VSyncModulator::onTransactionHandled() {
-    if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
-    mTransactionStart = Scheduler::TransactionStart::NORMAL;
-    updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeInitiated() {
-    if (mRefreshRateChangePending) {
-        return;
-    }
-    mRefreshRateChangePending = true;
-    updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeCompleted() {
-    if (!mRefreshRateChangePending) {
-        return;
-    }
-    mRefreshRateChangePending = false;
-    updateOffsets();
-}
-
-void VSyncModulator::onRefreshed(bool usedRenderEngine) {
-    bool updateOffsetsNeeded = false;
-    if (mRemainingEarlyFrameCount > 0) {
-        mRemainingEarlyFrameCount--;
-        updateOffsetsNeeded = true;
-    }
-    if (usedRenderEngine) {
-        mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
-        updateOffsetsNeeded = true;
-    } else if (mRemainingRenderEngineUsageCount > 0) {
-        mRemainingRenderEngineUsageCount--;
-        updateOffsetsNeeded = true;
-    }
-    if (updateOffsetsNeeded) {
-        updateOffsets();
-    }
-}
-
-VSyncModulator::Offsets VSyncModulator::getOffsets() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mOffsets;
-}
-
-const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
-    // Early offsets are used if we're in the middle of a refresh rate
-    // change, or if we recently begin a transaction.
-    if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
-        mRefreshRateChangePending) {
-        return mOffsetsConfig.early;
-    } else if (mRemainingRenderEngineUsageCount > 0) {
-        return mOffsetsConfig.earlyGl;
-    } else {
-        return mOffsetsConfig.late;
-    }
-}
-
-void VSyncModulator::updateOffsets() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    updateOffsetsLocked();
-}
-
-void VSyncModulator::updateOffsetsLocked() {
-    const Offsets& offsets = getNextOffsets();
-
-    mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
-    mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
-
-    mOffsets = offsets;
-
-    if (!mTraceDetailedInfo) {
-        return;
-    }
-
-    const bool isEarly = &offsets == &mOffsetsConfig.early;
-    const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
-    const bool isLate = &offsets == &mOffsetsConfig.late;
-
-    ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
-    ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
-    ATRACE_INT("Vsync-LateOffsetsOn", isLate);
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
deleted file mode 100644
index 704a5d5..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <mutex>
-
-#include "Scheduler.h"
-
-namespace android::scheduler {
-
-/*
- * Modulates the vsync-offsets depending on current SurfaceFlinger state.
- */
-class VSyncModulator {
-private:
-    // Number of frames we'll keep the early phase offsets once they are activated for a
-    // transaction. This acts as a low-pass filter in case the client isn't quick enough in
-    // sending new transactions.
-    static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
-
-    // Number of frames we'll keep the early gl phase offsets once they are activated.
-    // This acts as a low-pass filter to avoid scenarios where we rapidly
-    // switch in and out of gl composition.
-    static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
-
-public:
-    // Wrapper for a collection of surfaceflinger/app offsets for a particular
-    // configuration.
-    struct Offsets {
-        nsecs_t sf;
-        nsecs_t app;
-
-        bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
-
-        bool operator!=(const Offsets& other) const { return !(*this == other); }
-    };
-
-    struct OffsetsConfig {
-        Offsets early;   // For transactions with the eEarlyWakeup flag.
-        Offsets earlyGl; // As above but while compositing with GL.
-        Offsets late;    // Default.
-
-        bool operator==(const OffsetsConfig& other) const {
-            return early == other.early && earlyGl == other.earlyGl && late == other.late;
-        }
-
-        bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
-    };
-
-    VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
-                   ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
-
-    void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
-
-    // Signals that a transaction has started, and changes offsets accordingly.
-    void setTransactionStart(Scheduler::TransactionStart transactionStart);
-
-    // Signals that a transaction has been completed, so that we can finish
-    // special handling for a transaction.
-    void onTransactionHandled();
-
-    // Called when we send a refresh rate change to hardware composer, so that
-    // we can move into early offsets.
-    void onRefreshRateChangeInitiated();
-
-    // Called when we detect from vsync signals that the refresh rate changed.
-    // This way we can move out of early offsets if no longer necessary.
-    void onRefreshRateChangeCompleted();
-
-    // Called when the display is presenting a new frame. usedRenderEngine
-    // should be set to true if RenderEngine was involved with composing the new
-    // frame.
-    void onRefreshed(bool usedRenderEngine);
-
-    // Returns the offsets that we are currently using
-    Offsets getOffsets() const EXCLUDES(mMutex);
-
-private:
-    // Returns the next offsets that we should be using
-    const Offsets& getNextOffsets() const REQUIRES(mMutex);
-    // Updates offsets and persists them into the scheduler framework.
-    void updateOffsets() EXCLUDES(mMutex);
-    void updateOffsetsLocked() REQUIRES(mMutex);
-
-    Scheduler& mScheduler;
-    const ConnectionHandle mAppConnectionHandle;
-    const ConnectionHandle mSfConnectionHandle;
-
-    mutable std::mutex mMutex;
-    OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
-
-    Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
-
-    std::atomic<Scheduler::TransactionStart> mTransactionStart =
-            Scheduler::TransactionStart::NORMAL;
-    std::atomic<bool> mRefreshRateChangePending = false;
-    std::atomic<int> mRemainingEarlyFrameCount = 0;
-    std::atomic<int> mRemainingRenderEngineUsageCount = 0;
-
-    bool mTraceDetailedInfo = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index a3cb772..75d1e6f 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 #include "VSyncPredictor.h"
@@ -31,6 +27,9 @@
 #include <chrono>
 #include <sstream>
 
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
+
 namespace android::scheduler {
 using base::StringAppendF;
 
@@ -54,7 +53,7 @@
     }
 }
 
-inline size_t VSyncPredictor::next(int i) const {
+inline size_t VSyncPredictor::next(size_t i) const {
     return (i + 1) % mTimestamps.size();
 }
 
@@ -69,15 +68,22 @@
 }
 
 nsecs_t VSyncPredictor::currentPeriod() const {
-    std::lock_guard<std::mutex> lk(mMutex);
-    return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+    std::lock_guard lock(mMutex);
+    return mRateMap.find(mIdealPeriod)->second.slope;
 }
 
 bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
     if (!validate(timestamp)) {
-        ALOGV("timestamp was too far off the last known timestamp");
+        // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
+        // don't insert this ts into mTimestamps ringbuffer.
+        if (!mTimestamps.empty()) {
+            mKnownTimestamp =
+                    std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
+        } else {
+            mKnownTimestamp = timestamp;
+        }
         return false;
     }
 
@@ -115,7 +121,7 @@
     // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
     auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
     auto it = mRateMap.find(mIdealPeriod);
-    auto const currentPeriod = std::get<0>(it->second);
+    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
@@ -131,14 +137,14 @@
 
     auto meanTS = scheduler::calculate_mean(vsyncTS);
     auto meanOrdinal = scheduler::calculate_mean(ordinals);
-    for (auto i = 0; i < vsyncTS.size(); i++) {
+    for (size_t i = 0; i < vsyncTS.size(); i++) {
         vsyncTS[i] -= meanTS;
         ordinals[i] -= meanOrdinal;
     }
 
     auto top = 0ll;
     auto bottom = 0ll;
-    for (auto i = 0; i < vsyncTS.size(); i++) {
+    for (size_t i = 0; i < vsyncTS.size(); i++) {
         top += vsyncTS[i] * ordinals[i];
         bottom += ordinals[i] * ordinals[i];
     }
@@ -169,10 +175,8 @@
     return true;
 }
 
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
-    std::lock_guard<std::mutex> lk(mMutex);
-
-    auto const [slope, intercept] = getVSyncPredictionModel(lk);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+    auto const [slope, intercept] = getVSyncPredictionModelLocked();
 
     if (mTimestamps.empty()) {
         traceInt64If("VSP-mode", 1);
@@ -207,20 +211,78 @@
     return prediction;
 }
 
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
-    std::lock_guard<std::mutex> lk(mMutex);
-    return VSyncPredictor::getVSyncPredictionModel(lk);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+    std::lock_guard lock(mMutex);
+    return nextAnticipatedVSyncTimeFromLocked(timePoint);
 }
 
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
-        std::lock_guard<std::mutex> const&) const {
+/*
+ * Returns whether a given vsync timestamp is in phase with a vsync divider.
+ * For example, if the vsync timestamps are (0,16,32,48):
+ * isVSyncInPhase(0, 2) = true
+ * isVSyncInPhase(16, 2) = false
+ * isVSyncInPhase(32, 2) = true
+ */
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+    struct VsyncError {
+        nsecs_t vsyncTimestamp;
+        float error;
+
+        bool operator<(const VsyncError& other) const { return error < other.error; }
+    };
+
+    std::lock_guard lock(mMutex);
+    if (divider <= 1) {
+        return true;
+    }
+
+    const nsecs_t period = mRateMap[mIdealPeriod].slope;
+    const nsecs_t justBeforeTimePoint = timePoint - period / 2;
+    const nsecs_t dividedPeriod = mIdealPeriod / divider;
+
+    // If this is the first time we have asked about this divider with the
+    // current vsync period, it is considered in phase and we store the closest
+    // vsync timestamp
+    const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
+    if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+        const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
+        mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+        return true;
+    }
+
+    // Find the next N vsync timestamp where N is the divider.
+    // One of these vsyncs will be in phase. We return the one which is
+    // the most aligned with the last known in phase vsync
+    std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+    const nsecs_t knownVsync = knownTimestampIter->second;
+    nsecs_t point = justBeforeTimePoint;
+    for (size_t i = 0; i < divider; i++) {
+        const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
+        const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+        const auto error = std::abs(std::round(numPeriods) - numPeriods);
+        vsyncs[i] = {vsync, error};
+        point = vsync + 1;
+    }
+
+    const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
+    mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+    return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
+    std::lock_guard lock(mMutex);
+    const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+    return {model.slope, model.intercept};
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
     return mRateMap.find(mIdealPeriod)->second;
 }
 
 void VSyncPredictor::setPeriod(nsecs_t period) {
     ATRACE_CALL();
 
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     static constexpr size_t kSizeLimit = 30;
     if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
         mRateMap.erase(mRateMap.begin());
@@ -236,48 +298,40 @@
 
 void VSyncPredictor::clearTimestamps() {
     if (!mTimestamps.empty()) {
-        mKnownTimestamp = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+        auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+        if (mKnownTimestamp) {
+            mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+        } else {
+            mKnownTimestamp = maxRb;
+        }
+
         mTimestamps.clear();
         mLastTimestampIndex = 0;
     }
 }
 
-bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
-    using namespace std::literals::chrono_literals;
-    std::lock_guard<std::mutex> lk(mMutex);
-    bool needsMoreSamples = true;
-    if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
-        nsecs_t constexpr aLongTime =
-                std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
-        if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
-            auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
-            needsMoreSamples = !((lastTimestamp + aLongTime) > now);
-        }
-    }
-
-    ATRACE_INT("VSP-moreSamples", needsMoreSamples);
-    return needsMoreSamples;
+bool VSyncPredictor::needsMoreSamples() const {
+    std::lock_guard lock(mMutex);
+    return mTimestamps.size() < kMinimumSamplesForPrediction;
 }
 
 void VSyncPredictor::resetModel() {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
     clearTimestamps();
 }
 
 void VSyncPredictor::dump(std::string& result) const {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
     StringAppendF(&result, "\tRefresh Rate Map:\n");
     for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
         StringAppendF(&result,
                       "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
-                      idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
-                      std::get<1>(periodInterceptTuple));
+                      idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+                      periodInterceptTuple.intercept);
     }
 }
 
 } // namespace android::scheduler
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3ca878d..381cf81 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@
                    uint32_t outlierTolerancePercent);
     ~VSyncPredictor();
 
-    bool addVsyncTimestamp(nsecs_t timestamp) final;
-    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
-    nsecs_t currentPeriod() const final;
-    void resetModel() final;
+    bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+    nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+    void resetModel() final EXCLUDES(mMutex);
 
     /*
      * Inform the model that the period is anticipated to change to a new value.
@@ -50,17 +50,23 @@
      *
      * \param [in] period   The new period that should be used.
      */
-    void setPeriod(nsecs_t period) final;
+    void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
 
-    /* Query if the model is in need of more samples to make a prediction at timePoint.
-     * \param [in] timePoint    The timePoint to inquire of.
+    /* Query if the model is in need of more samples to make a prediction.
      * \return  True, if model would benefit from more samples, False if not.
      */
-    bool needsMoreSamples(nsecs_t timePoint) const;
+    bool needsMoreSamples() const final EXCLUDES(mMutex);
 
-    std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+    struct Model {
+        nsecs_t slope;
+        nsecs_t intercept;
+    };
 
-    void dump(std::string& result) const final;
+    VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
+
+    bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+
+    void dump(std::string& result) const final EXCLUDES(mMutex);
 
 private:
     VSyncPredictor(VSyncPredictor const&) = delete;
@@ -75,17 +81,23 @@
     size_t const kOutlierTolerancePercent;
 
     std::mutex mutable mMutex;
-    size_t next(int i) const REQUIRES(mMutex);
+    size_t next(size_t i) const REQUIRES(mMutex);
     bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
-    std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
-            REQUIRES(mMutex);
+
+    Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+
+    nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
 
     nsecs_t mIdealPeriod GUARDED_BY(mMutex);
     std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
 
-    std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+    // Map between ideal vsync period and the calculated model
+    std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
 
-    int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+    // Map between the divided vsync period and the last known vsync timestamp
+    std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
+
+    size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 16d102c..7b5d462 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -30,136 +30,23 @@
 namespace android::scheduler {
 using base::StringAppendF;
 
+VsyncController::~VsyncController() = default;
+
 Clock::~Clock() = default;
 nsecs_t SystemClock::now() const {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-class PredictedVsyncTracer {
-public:
-    PredictedVsyncTracer(VSyncDispatch& dispatch)
-          : mRegistration(dispatch,
-                          std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
-                                    std::placeholders::_2),
-                          "PredictedVsyncTracer") {
-        mRegistration.schedule(0, 0);
-    }
-
-private:
-    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
-    VSyncCallbackRegistration mRegistration;
-
-    void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
-        mParity = !mParity;
-        mRegistration.schedule(0, 0);
-    }
-};
-
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
-                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
-                           bool supportKernelIdleTimer)
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker,
+                           size_t pendingFenceLimit, bool supportKernelIdleTimer)
       : mClock(std::move(clock)),
-        mTracker(std::move(tracker)),
-        mDispatch(std::move(dispatch)),
+        mTracker(tracker),
         mPendingLimit(pendingFenceLimit),
-        mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
-                                      ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
-                                      : nullptr),
         mSupportKernelIdleTimer(supportKernelIdleTimer) {}
 
 VSyncReactor::~VSyncReactor() = default;
 
-// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
-// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
-// for now.
-class CallbackRepeater {
-public:
-    CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
-                     nsecs_t period, nsecs_t offset, nsecs_t notBefore)
-          : mName(name),
-            mCallback(cb),
-            mRegistration(dispatch,
-                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
-                                    std::placeholders::_2),
-                          mName),
-            mPeriod(period),
-            mOffset(offset),
-            mLastCallTime(notBefore) {}
-
-    ~CallbackRepeater() {
-        std::lock_guard<std::mutex> lk(mMutex);
-        mRegistration.cancel();
-    }
-
-    void start(nsecs_t offset) {
-        std::lock_guard<std::mutex> lk(mMutex);
-        mStopped = false;
-        mOffset = offset;
-
-        auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
-        LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
-                            "Error scheduling callback: rc %X", schedule_result);
-    }
-
-    void setPeriod(nsecs_t period) {
-        std::lock_guard<std::mutex> lk(mMutex);
-        if (period == mPeriod) {
-            return;
-        }
-        mPeriod = period;
-    }
-
-    void stop() {
-        std::lock_guard<std::mutex> lk(mMutex);
-        LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
-        mStopped = true;
-        mRegistration.cancel();
-    }
-
-    void dump(std::string& result) const {
-        std::lock_guard<std::mutex> lk(mMutex);
-        StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
-                      mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
-                      mStopped ? "stopped" : "running");
-    }
-
-private:
-    void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
-        {
-            std::lock_guard<std::mutex> lk(mMutex);
-            mLastCallTime = vsynctime;
-        }
-
-        mCallback->onDispSyncEvent(wakeupTime, vsynctime);
-
-        {
-            std::lock_guard<std::mutex> lk(mMutex);
-            if (mStopped) {
-                return;
-            }
-            auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
-            LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
-                                "Error rescheduling callback: rc %X", schedule_result);
-        }
-    }
-
-    // DispSync offsets are defined as time after the vsync before presentation.
-    // VSyncReactor workloads are defined as time before the intended presentation vsync.
-    // Note change in sign between the two defnitions.
-    nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
-
-    const std::string mName;
-    DispSync::Callback* const mCallback;
-
-    std::mutex mutable mMutex;
-    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
-    bool mStopped GUARDED_BY(mMutex) = false;
-    nsecs_t mPeriod GUARDED_BY(mMutex);
-    nsecs_t mOffset GUARDED_BY(mMutex);
-    nsecs_t mLastCallTime GUARDED_BY(mMutex);
-};
-
-bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
     if (!fence) {
         return false;
     }
@@ -169,7 +56,7 @@
         return true;
     }
 
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     if (mExternalIgnoreFences || mInternalIgnoreFences) {
         return true;
     }
@@ -182,7 +69,7 @@
         } else if (time == Fence::SIGNAL_TIME_INVALID) {
             it = mUnfiredFences.erase(it);
         } else {
-            timestampAccepted &= mTracker->addVsyncTimestamp(time);
+            timestampAccepted &= mTracker.addVsyncTimestamp(time);
 
             it = mUnfiredFences.erase(it);
         }
@@ -194,7 +81,7 @@
         }
         mUnfiredFences.push_back(fence);
     } else {
-        timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+        timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
     }
 
     if (!timestampAccepted) {
@@ -206,14 +93,14 @@
     return mMoreSamplesNeeded;
 }
 
-void VSyncReactor::setIgnorePresentFences(bool ignoration) {
-    std::lock_guard<std::mutex> lk(mMutex);
-    mExternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFences(bool ignore) {
+    std::lock_guard lock(mMutex);
+    mExternalIgnoreFences = ignore;
     updateIgnorePresentFencesInternal();
 }
 
-void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
-    mInternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) {
+    mInternalIgnoreFences = ignore;
     updateIgnorePresentFencesInternal();
 }
 
@@ -223,16 +110,8 @@
     }
 }
 
-nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
-    auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
-    return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
-}
-
-nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
-    return mTracker->nextAnticipatedVSyncTimeFrom(now);
-}
-
-void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
+    ATRACE_CALL();
     mPeriodConfirmationInProgress = true;
     mPeriodTransitioningTo = newPeriod;
     mMoreSamplesNeeded = true;
@@ -240,35 +119,26 @@
 }
 
 void VSyncReactor::endPeriodTransition() {
-    setIgnorePresentFencesInternal(false);
-    mMoreSamplesNeeded = false;
+    ATRACE_CALL();
     mPeriodTransitioningTo.reset();
     mPeriodConfirmationInProgress = false;
     mLastHwVsync.reset();
 }
 
-void VSyncReactor::setPeriod(nsecs_t period) {
+void VSyncReactor::startPeriodTransition(nsecs_t period) {
     ATRACE_INT64("VSR-setPeriod", period);
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     mLastHwVsync.reset();
 
-    if (!mSupportKernelIdleTimer && period == getPeriod()) {
+    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
         endPeriodTransition();
+        setIgnorePresentFencesInternal(false);
+        mMoreSamplesNeeded = false;
     } else {
-        startPeriodTransition(period);
+        startPeriodTransitionInternal(period);
     }
 }
 
-nsecs_t VSyncReactor::getPeriod() {
-    return mTracker->currentPeriod();
-}
-
-void VSyncReactor::beginResync() {
-    mTracker->resetModel();
-}
-
-void VSyncReactor::endResync() {}
-
 bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
     if (!mPeriodConfirmationInProgress) {
         return false;
@@ -278,12 +148,14 @@
         return false;
     }
 
-    if (mSupportKernelIdleTimer) {
+    const bool periodIsChanging =
+            mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
+    if (mSupportKernelIdleTimer && !periodIsChanging) {
         // Clear out the Composer-provided period and use the allowance logic below
         HwcVsyncPeriod = {};
     }
 
-    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
     auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -295,78 +167,45 @@
     return std::abs(distance - period) < allowance;
 }
 
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                                   bool* periodFlushed) {
+bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                       bool* periodFlushed) {
     assert(periodFlushed);
 
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+        ATRACE_NAME("VSR: period confirmed");
         if (mPeriodTransitioningTo) {
-            mTracker->setPeriod(*mPeriodTransitioningTo);
-            for (auto& entry : mCallbacks) {
-                entry.second->setPeriod(*mPeriodTransitioningTo);
-            }
+            mTracker.setPeriod(*mPeriodTransitioningTo);
             *periodFlushed = true;
         }
+
+        if (mLastHwVsync) {
+            mTracker.addVsyncTimestamp(*mLastHwVsync);
+        }
+        mTracker.addVsyncTimestamp(timestamp);
+
         endPeriodTransition();
+        mMoreSamplesNeeded = mTracker.needsMoreSamples();
     } else if (mPeriodConfirmationInProgress) {
+        ATRACE_NAME("VSR: still confirming period");
         mLastHwVsync = timestamp;
         mMoreSamplesNeeded = true;
         *periodFlushed = false;
     } else {
-        mMoreSamplesNeeded = false;
+        ATRACE_NAME("VSR: adding sample");
         *periodFlushed = false;
+        mTracker.addVsyncTimestamp(timestamp);
+        mMoreSamplesNeeded = mTracker.needsMoreSamples();
     }
 
-    mTracker->addVsyncTimestamp(timestamp);
+    if (!mMoreSamplesNeeded) {
+        setIgnorePresentFencesInternal(false);
+    }
     return mMoreSamplesNeeded;
 }
 
-status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
-                                        DispSync::Callback* callback,
-                                        nsecs_t /* lastCallbackTime */) {
-    std::lock_guard<std::mutex> lk(mMutex);
-    auto it = mCallbacks.find(callback);
-    if (it == mCallbacks.end()) {
-        // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
-        static auto constexpr maxListeners = 4;
-        if (mCallbacks.size() >= maxListeners) {
-            ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
-                  maxListeners, mCallbacks.size());
-            return NO_MEMORY;
-        }
-
-        auto const period = mTracker->currentPeriod();
-        auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
-                                                           phase, mClock->now());
-        it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
-    }
-
-    it->second->start(phase);
-    return NO_ERROR;
-}
-
-status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
-                                           nsecs_t* /* outLastCallback */) {
-    std::lock_guard<std::mutex> lk(mMutex);
-    auto const it = mCallbacks.find(callback);
-    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
-
-    it->second->stop();
-    return NO_ERROR;
-}
-
-status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
-    std::lock_guard<std::mutex> lk(mMutex);
-    auto const it = mCallbacks.find(callback);
-    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
-
-    it->second->start(phase);
-    return NO_ERROR;
-}
-
 void VSyncReactor::dump(std::string& result) const {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "VsyncReactor in use\n");
     StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
     StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
@@ -386,17 +225,8 @@
         StringAppendF(&result, "No Last HW vsync\n");
     }
 
-    StringAppendF(&result, "CallbackRepeaters:\n");
-    for (const auto& [callback, repeater] : mCallbacks) {
-        repeater->dump(result);
-    }
-
     StringAppendF(&result, "VSyncTracker:\n");
-    mTracker->dump(result);
-    StringAppendF(&result, "VSyncDispatch:\n");
-    mDispatch->dump(result);
+    mTracker.dump(result);
 }
 
-void VSyncReactor::reset() {}
-
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 265d89c..449d4c3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,74 +22,53 @@
 #include <mutex>
 #include <unordered_map>
 #include <vector>
-#include "DispSync.h"
 #include "TimeKeeper.h"
+#include "VsyncController.h"
 namespace android::scheduler {
 
 class Clock;
 class VSyncDispatch;
 class VSyncTracker;
-class CallbackRepeater;
-class PredictedVsyncTracer;
 
 // TODO (b/145217110): consider renaming.
-class VSyncReactor : public android::DispSync {
+class VSyncReactor : public VsyncController {
 public:
-    VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
-                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+    VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
                  bool supportKernelIdleTimer);
     ~VSyncReactor();
 
-    bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
-    void setIgnorePresentFences(bool ignoration) final;
+    bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+    void setIgnorePresentFences(bool ignore) final;
 
-    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final;
-    nsecs_t expectedPresentTime(nsecs_t now) final;
+    void startPeriodTransition(nsecs_t period) final;
 
-    void setPeriod(nsecs_t period) final;
-    nsecs_t getPeriod() final;
-
-    // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
-    void beginResync() final;
-    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                         bool* periodFlushed) final;
-    void endResync() final;
-
-    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
-                              nsecs_t lastCallbackTime) final;
-    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
-    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+    bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                             bool* periodFlushed) final;
 
     void dump(std::string& result) const final;
-    void reset() final;
 
 private:
-    void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+    void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
     void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
-    void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+    void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
     void endPeriodTransition() REQUIRES(mMutex);
     bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
             REQUIRES(mMutex);
 
     std::unique_ptr<Clock> const mClock;
-    std::unique_ptr<VSyncTracker> const mTracker;
-    std::unique_ptr<VSyncDispatch> const mDispatch;
+    VSyncTracker& mTracker;
     size_t const mPendingLimit;
 
     mutable std::mutex mMutex;
     bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
     bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
-    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+    std::vector<std::shared_ptr<android::FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
 
     bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
     bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
     std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
     std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
 
-    std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
-            GUARDED_BY(mMutex);
-
-    const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
     const bool mSupportKernelIdleTimer = false;
 };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 05a6fc3..2cd9b3d 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,16 @@
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
+    virtual bool needsMoreSamples() const = 0;
+
+    /*
+     * Checks if a vsync timestamp is in phase for a given divider.
+     *
+     * \param [in] timePoint  A vsync timestamp
+     * \param [in] divider  The divider to check for
+     */
+    virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+
     virtual void dump(std::string& result) const = 0;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..aac2569
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VsyncConfiguration.h"
+
+#include <cutils/properties.h>
+
+#include <optional>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+std::optional<nsecs_t> getProperty(const char* name) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name, value, "-1");
+    if (const int i = atoi(value); i != -1) return i;
+    return std::nullopt;
+}
+
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+    static constexpr float MARGIN = 0.01f;
+    return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate->getFps());
+    }
+
+    return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler::impl {
+
+VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+                                   [&fps](const std::pair<float, VsyncConfigSet>& candidateFps) {
+                                       return fpsEqualsWithMargin(fps, candidateFps.first);
+                                   });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) {
+    for (const auto fps : refreshRates) {
+        mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+    }
+}
+
+void VsyncConfiguration::dump(std::string& result) const {
+    const auto [early, earlyGpu, late] = getCurrentConfigs();
+    using base::StringAppendF;
+    StringAppendF(&result,
+                  "           app phase:    %9" PRId64 " ns\t         SF phase:    %9" PRId64
+                  " ns\n"
+                  "           app duration: %9lld ns\t         SF duration: %9lld ns\n"
+                  "     early app phase:    %9" PRId64 " ns\t   early SF phase:    %9" PRId64
+                  " ns\n"
+                  "     early app duration: %9lld ns\t   early SF duration: %9lld ns\n"
+                  "  GL early app phase:    %9" PRId64 " ns\tGL early SF phase:    %9" PRId64
+                  " ns\n"
+                  "  GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
+                  late.appOffset, late.sfOffset,
+
+                  late.appWorkDuration.count(), late.sfWorkDuration.count(),
+
+                  early.appOffset, early.sfOffset,
+
+                  early.appWorkDuration.count(), early.sfWorkDuration.count(),
+
+                  earlyGpu.appOffset, earlyGpu.sfOffset,
+
+                  earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
+}
+
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                     sysprop::vsync_event_phase_offset_ns(1000000),
+                     sysprop::vsync_sf_event_phase_offset_ns(1000000),
+                     getProperty("debug.sf.early_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_phase_offset_ns"),
+                     getProperty("debug.sf.early_app_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+                     getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
+                     getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
+                     getProperty("debug.sf.high_fps_early_phase_offset_ns"),
+                     getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
+                     getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
+                     getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"),
+                     // Below defines the threshold when an offset is considered to be negative,
+                     // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+                     // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+                     // vsync.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(
+        const std::vector<float>& refreshRates, float currentFps, nsecs_t vsyncPhaseOffsetNs,
+        nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
+        std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+        std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+        nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+        std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+        std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+        std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+      : VsyncConfiguration(currentFps),
+        mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+        mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+        mEarlySfOffsetNs(earlySfOffsetNs),
+        mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
+        mEarlyAppOffsetNs(earlyAppOffsetNs),
+        mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
+        mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
+        mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
+        mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
+        mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
+        mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
+        mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
+        mThresholdForNextVsync(thresholdForNextVsync) {
+    initializeOffsets(refreshRates);
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+    if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
+        return getHighFpsOffsets(vsyncDuration);
+    } else {
+        return getDefaultOffsets(vsyncDuration);
+    }
+}
+
+namespace {
+std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
+    return std::chrono::nanoseconds(vsyncDuration - sfOffset);
+}
+
+std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
+                                             nsecs_t vsyncDuration) {
+    auto duration = vsyncDuration + (sfOffset - appOffset);
+    if (duration < vsyncDuration) {
+        duration += vsyncDuration;
+    }
+
+    return std::chrono::nanoseconds(duration);
+}
+} // namespace
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+    const auto earlySfOffset =
+            mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+            ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+            : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+    const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+    const auto earlyGpuSfOffset =
+            mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+            ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+            : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+    const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+    const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+            ? mSfVSyncPhaseOffsetNs
+            : mSfVSyncPhaseOffsetNs - vsyncDuration;
+    const auto lateAppOffset = mVSyncPhaseOffsetNs;
+
+    return {
+            .early = {.sfOffset = earlySfOffset,
+                      .appOffset = earlyAppOffset,
+                      .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+                      .appWorkDuration =
+                              appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
+            .earlyGpu = {.sfOffset = earlyGpuSfOffset,
+                         .appOffset = earlyGpuAppOffset,
+                         .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+                         .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
+                                                                vsyncDuration)},
+            .late = {.sfOffset = lateSfOffset,
+                     .appOffset = lateAppOffset,
+                     .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+                     .appWorkDuration =
+                             appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
+    };
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+    const auto earlySfOffset =
+            mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+            ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+            : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+    const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+    const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
+                                          mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+            ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+            : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+    const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+    const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+            ? mHighFpsSfVSyncPhaseOffsetNs
+            : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
+    const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
+
+    return {
+            .early =
+                    {
+                            .sfOffset = earlySfOffset,
+                            .appOffset = earlyAppOffset,
+                            .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+                            .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
+                                                                   vsyncDuration),
+                    },
+            .earlyGpu =
+                    {
+                            .sfOffset = earlyGpuSfOffset,
+                            .appOffset = earlyGpuAppOffset,
+                            .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+                            .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
+                                                                   earlyGpuSfOffset, vsyncDuration),
+                    },
+            .late =
+                    {
+                            .sfOffset = lateSfOffset,
+                            .appOffset = lateAppOffset,
+                            .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+                            .appWorkDuration =
+                                    appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
+                    },
+    };
+}
+
+static void validateSysprops() {
+    const auto validatePropertyBool = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+    };
+
+    validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    const auto validateProperty = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+                            "%s is set to %" PRId64 " but expecting duration", prop,
+                            getProperty(prop).value_or(-1));
+    };
+
+    validateProperty("debug.sf.early_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_phase_offset_ns");
+    validateProperty("debug.sf.early_app_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+namespace {
+nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+    return vsyncDuration - sfDuration.count() % vsyncDuration;
+}
+
+nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
+                            std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+    return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
+}
+} // namespace
+
+WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+    const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
+        return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
+                              : std::chrono::nanoseconds(duration);
+    };
+
+    const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
+        return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
+                              : std::chrono::nanoseconds(duration);
+    };
+
+    const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
+    const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
+    const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
+    const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
+    const auto sfDuration = sfDurationFixup(mSfDuration);
+    const auto appDuration = appDurationFixup(mAppDuration);
+
+    return {
+            .early =
+                    {
+
+                            .sfOffset = sfEarlyDuration.count() < vsyncDuration
+                                    ? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
+                                    : sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
+                                            vsyncDuration,
+
+                            .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
+                                                             vsyncDuration),
+
+                            .sfWorkDuration = sfEarlyDuration,
+                            .appWorkDuration = appEarlyDuration,
+                    },
+            .earlyGpu =
+                    {
+
+                            .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
+
+                                    ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
+                                    : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
+                                            vsyncDuration,
+
+                            .appOffset = appDurationToOffset(appEarlyGpuDuration,
+                                                             sfEarlyGpuDuration, vsyncDuration),
+                            .sfWorkDuration = sfEarlyGpuDuration,
+                            .appWorkDuration = appEarlyGpuDuration,
+                    },
+            .late =
+                    {
+
+                            .sfOffset = sfDuration.count() < vsyncDuration
+
+                                    ? sfDurationToOffset(sfDuration, vsyncDuration)
+                                    : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
+
+                            .appOffset =
+                                    appDurationToOffset(appDuration, sfDuration, vsyncDuration),
+
+                            .sfWorkDuration = sfDuration,
+                            .appWorkDuration = appDuration,
+                    },
+    };
+}
+
+WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                     getProperty("debug.sf.late.sf.duration").value_or(-1),
+                     getProperty("debug.sf.late.app.duration").value_or(-1),
+                     getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+                     getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+                     getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+                     getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+    validateSysprops();
+}
+
+WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps,
+                           nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+                           nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration,
+                           nsecs_t appEarlyGpuDuration)
+      : VsyncConfiguration(currentFps),
+        mSfDuration(sfDuration),
+        mAppDuration(appDuration),
+        mSfEarlyDuration(sfEarlyDuration),
+        mAppEarlyDuration(appEarlyDuration),
+        mSfEarlyGpuDuration(sfEarlyGpuDuration),
+        mAppEarlyGpuDuration(appEarlyGpuDuration) {
+    initializeOffsets(refreshRates);
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
new file mode 100644
index 0000000..c27a25d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "RefreshRateConfigs.h"
+#include "VsyncModulator.h"
+
+namespace android::scheduler {
+
+/*
+ * This class encapsulates vsync configurations for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class VsyncConfiguration {
+public:
+    using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
+
+    virtual ~VsyncConfiguration() = default;
+    virtual VsyncConfigSet getCurrentConfigs() const = 0;
+    virtual VsyncConfigSet getConfigsForRefreshRate(float fps) const = 0;
+
+    virtual void setRefreshRateFps(float fps) = 0;
+
+    virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
+/*
+ * This is a common implementation for both phase offsets and durations.
+ * PhaseOffsets and WorkDuration derive from this class and implement the
+ * constructOffsets method
+ */
+class VsyncConfiguration : public scheduler::VsyncConfiguration {
+public:
+    explicit VsyncConfiguration(float currentFps);
+
+    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+    VsyncConfigSet getConfigsForRefreshRate(float fps) const override;
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    VsyncConfigSet getCurrentConfigs() const override {
+        return getConfigsForRefreshRate(mRefreshRateFps);
+    }
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& result) const override;
+
+protected:
+    void initializeOffsets(const std::vector<float>& refreshRates);
+    virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+
+    std::unordered_map<float, VsyncConfigSet> mOffsets;
+    std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * WorkDuration is the new implementation.
+ */
+class PhaseOffsets : public VsyncConfiguration {
+public:
+    explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+protected:
+    // Used for unit tests
+    PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                 nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                 std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                 std::optional<nsecs_t> earlyAppOffsetNs,
+                 std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+                 nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+                 std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+                 std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+                 std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync);
+
+private:
+    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+    VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
+    VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+
+    const nsecs_t mVSyncPhaseOffsetNs;
+    const nsecs_t mSfVSyncPhaseOffsetNs;
+    const std::optional<nsecs_t> mEarlySfOffsetNs;
+    const std::optional<nsecs_t> mEarlyGpuSfOffsetNs;
+    const std::optional<nsecs_t> mEarlyAppOffsetNs;
+    const std::optional<nsecs_t> mEarlyGpuAppOffsetNs;
+
+    const nsecs_t mHighFpsVSyncPhaseOffsetNs;
+    const nsecs_t mHighFpsSfVSyncPhaseOffsetNs;
+    const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs;
+    const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs;
+    const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs;
+    const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs;
+
+    const nsecs_t mThresholdForNextVsync;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGpu)
+ * offset types.
+ */
+class WorkDuration : public VsyncConfiguration {
+public:
+    explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+
+protected:
+    // Used for unit tests
+    WorkDuration(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+                 nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                 nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+
+private:
+    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+    const nsecs_t mSfDuration;
+    const nsecs_t mAppDuration;
+
+    const nsecs_t mSfEarlyDuration;
+    const nsecs_t mAppEarlyDuration;
+
+    const nsecs_t mSfEarlyGpuDuration;
+    const nsecs_t mAppEarlyGpuDuration;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
new file mode 100644
index 0000000..0f0df22
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <ui/FenceTime.h>
+
+#include <memory>
+
+namespace android::scheduler {
+
+class FenceTime;
+
+class VsyncController {
+public:
+    virtual ~VsyncController();
+
+    /*
+     * Adds a present fence to the model. The controller will use the fence time as
+     * a vsync signal.
+     *
+     * \param [in] fence    The present fence given from the display
+     * \return              True if the model needs more vsync signals to make
+     *                      an accurate prediction,
+     *                      False otherwise
+     */
+    virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+
+    /*
+     * Adds a hw sync timestamp to the model. The controller will use the timestamp
+     * time as a vsync signal.
+     *
+     * \param [in] timestamp       The HW Vsync timestamp
+     * \param [in] hwcVsyncPeriod  The Vsync period reported by composer, if available
+     * \param [out] periodFlushed  True if the vsync period changed is completed
+     * \return                     True if the model needs more vsync signals to make
+     *                             an accurate prediction,
+     *                             False otherwise
+     */
+    virtual bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                     bool* periodFlushed) = 0;
+
+    /*
+     * Inform the controller that the period is changing and the controller needs to recalibrate
+     * itself. The controller will end the period transition internally.
+     *
+     * \param [in] period   The period that the system is changing into.
+     */
+    virtual void startPeriodTransition(nsecs_t period) = 0;
+
+    /*
+     * Tells the tracker to stop using present fences to get a vsync signal.
+     *
+     * \param [in] ignore  Whether to ignore the present fences or not
+     */
+    virtual void setIgnorePresentFences(bool ignore) = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+protected:
+    VsyncController() = default;
+    VsyncController(VsyncController const&) = delete;
+    VsyncController& operator=(VsyncController const&) = delete;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
new file mode 100644
index 0000000..1f821be
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "VsyncModulator"
+
+#include "VsyncModulator.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
+
+VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
+      : mVsyncConfigSet(config),
+        mNow(now),
+        mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+
+VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mVsyncConfigSet = config;
+    return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
+        TransactionSchedule schedule) {
+    switch (schedule) {
+        case Schedule::EarlyStart:
+            ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+            mExplicitEarlyWakeup = true;
+            break;
+        case Schedule::EarlyEnd:
+            ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+            mExplicitEarlyWakeup = false;
+            break;
+        case Schedule::Early:
+        case Schedule::Late:
+            // No change to mExplicitEarlyWakeup for non-explicit states.
+            break;
+    }
+
+    if (mTraceDetailedInfo) {
+        ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+    }
+
+    if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+        mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
+        mEarlyTransactionStartTime = mNow();
+    }
+
+    // An early transaction stays an early transaction.
+    if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
+        return std::nullopt;
+    }
+    mTransactionSchedule = schedule;
+    return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
+    mLastTransactionCommitTime = mNow();
+    if (mTransactionSchedule == Schedule::Late) return std::nullopt;
+    mTransactionSchedule = Schedule::Late;
+    return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
+    if (mRefreshRateChangePending) return std::nullopt;
+    mRefreshRateChangePending = true;
+    return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
+    if (!mRefreshRateChangePending) return std::nullopt;
+    mRefreshRateChangePending = false;
+    return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+    bool updateOffsetsNeeded = false;
+
+    if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
+        mLastTransactionCommitTime.load()) {
+        if (mEarlyTransactionFrames > 0) {
+            mEarlyTransactionFrames--;
+            updateOffsetsNeeded = true;
+        }
+    }
+    if (usedGpuComposition) {
+        mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
+        updateOffsetsNeeded = true;
+    } else if (mEarlyGpuFrames > 0) {
+        mEarlyGpuFrames--;
+        updateOffsetsNeeded = true;
+    }
+
+    if (!updateOffsetsNeeded) return std::nullopt;
+    return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mVsyncConfig;
+}
+
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+    // Early offsets are used if we're in the middle of a refresh rate
+    // change, or if we recently begin a transaction.
+    if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
+        mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+        return mVsyncConfigSet.early;
+    } else if (mEarlyGpuFrames > 0) {
+        return mVsyncConfigSet.earlyGpu;
+    } else {
+        return mVsyncConfigSet.late;
+    }
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+    const VsyncConfig& offsets = getNextVsyncConfig();
+    mVsyncConfig = offsets;
+
+    if (mTraceDetailedInfo) {
+        const bool isEarly = &offsets == &mVsyncConfigSet.early;
+        const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
+        const bool isLate = &offsets == &mVsyncConfigSet.late;
+
+        ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+        ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
+        ATRACE_INT("Vsync-LateOffsetsOn", isLate);
+    }
+
+    return offsets;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
new file mode 100644
index 0000000..355a14a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <chrono>
+#include <mutex>
+#include <optional>
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+    Late,  // Default.
+    Early, // Deprecated.
+    EarlyStart,
+    EarlyEnd
+};
+
+// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
+class VsyncModulator {
+public:
+    // Number of frames to keep early offsets after an early transaction or GPU composition.
+    // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
+    // composition strategy alternates on subsequent frames.
+    static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
+    static constexpr int MIN_EARLY_GPU_FRAMES = 2;
+
+    // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
+    // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
+    static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
+
+    // Phase offsets and work durations for SF and app deadlines from VSYNC.
+    struct VsyncConfig {
+        nsecs_t sfOffset;
+        nsecs_t appOffset;
+        std::chrono::nanoseconds sfWorkDuration;
+        std::chrono::nanoseconds appWorkDuration;
+
+        bool operator==(const VsyncConfig& other) const {
+            return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+                    sfWorkDuration == other.sfWorkDuration &&
+                    appWorkDuration == other.appWorkDuration;
+        }
+
+        bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+    };
+
+    using VsyncConfigOpt = std::optional<VsyncConfig>;
+
+    struct VsyncConfigSet {
+        VsyncConfig early;    // Used for early transactions, and during refresh rate change.
+        VsyncConfig earlyGpu; // Used during GPU composition.
+        VsyncConfig late;     // Default.
+
+        bool operator==(const VsyncConfigSet& other) const {
+            return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
+        }
+
+        bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+    };
+
+    using Clock = std::chrono::steady_clock;
+    using TimePoint = Clock::time_point;
+    using Now = TimePoint (*)();
+
+    explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
+
+    VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
+
+    [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
+
+    // Changes offsets in response to transaction flags or commit.
+    [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+    [[nodiscard]] VsyncConfigOpt onTransactionCommit();
+
+    // Called when we send a refresh rate change to hardware composer, so that
+    // we can move into early offsets.
+    [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
+
+    // Called when we detect from VSYNC signals that the refresh rate changed.
+    // This way we can move out of early offsets if no longer necessary.
+    [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
+
+    [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
+
+private:
+    const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
+    [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
+    [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
+
+    mutable std::mutex mMutex;
+    VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
+
+    VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
+
+    using Schedule = TransactionSchedule;
+    std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
+    std::atomic<bool> mExplicitEarlyWakeup = false;
+
+    std::atomic<bool> mRefreshRateChangePending = false;
+
+    std::atomic<int> mEarlyTransactionFrames = 0;
+    std::atomic<int> mEarlyGpuFrames = 0;
+    std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
+    std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
+
+    const Now mNow;
+    const bool mTraceDetailedInfo;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c58e3a4..b8b2fbc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,12 +23,15 @@
 
 #include "SurfaceFlinger.h"
 
+#include <android-base/properties.h>
 #include <android/configuration.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <android/hardware/power/Boost.h>
 #include <android/native_window.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
@@ -45,18 +48,16 @@
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <dlfcn.h>
-#include <dvr/vr_flinger.h>
 #include <errno.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
-#include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerMetadata.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
-#include <input/IInputFlinger.h>
+#include <hidl/ServiceManagement.h>
 #include <layerproto/LayerProtoParser.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -72,7 +73,6 @@
 #include <ui/DisplayState.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
-#include <ui/UiConfig.h>
 #include <utils/StopWatch.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -87,6 +87,7 @@
 #include <functional>
 #include <mutex>
 #include <optional>
+#include <type_traits>
 #include <unordered_map>
 
 #include "BufferLayer.h"
@@ -101,24 +102,26 @@
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
+#include "DisplayRenderArea.h"
 #include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
+#include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
+#include "LayerRenderArea.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "NativeWindowSurface.h"
 #include "Promise.h"
 #include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
-#include "Scheduler/DispSync.h"
 #include "Scheduler/DispSyncSource.h"
-#include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
 #include "SurfaceInterceptor.h"
@@ -252,20 +255,34 @@
 
 }  // namespace anonymous
 
+struct SetInputWindowsListener : os::BnSetInputWindowsListener {
+    explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
+
+    binder::Status onSetInputWindowsFinished() override;
+
+    std::function<void()> mListenerCb;
+};
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+    if (mListenerCb != nullptr) {
+        mListenerCb();
+    }
+    return binder::Status::ok();
+}
+
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
-const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
+const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
 uint64_t SurfaceFlinger::maxVirtualDisplaySize;
 bool SurfaceFlinger::hasSyncFramework;
-bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
@@ -311,13 +328,16 @@
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
-        mInterceptor(mFactory.createSurfaceInterceptor(this)),
+        mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(std::make_unique<FrameTracer>()),
+        mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
-        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
+        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+    mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
+}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
@@ -330,9 +350,6 @@
 
     maxVirtualDisplaySize = max_virtual_display_dimension(0);
 
-    // Vr flinger is only enabled on Daydream ready devices.
-    useVrFlinger = use_vr_flinger(false);
-
     maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
 
     maxGraphicsWidth = std::max(max_graphics_width(0), 0);
@@ -340,7 +357,9 @@
 
     hasWideColorDisplay = has_wide_color_display(false);
 
-    useColorManagement = use_color_management(false);
+    // Android 12 and beyond, color management in display pipeline is turned on
+    // by default.
+    useColorManagement = use_color_management(true);
 
     mDefaultCompositionDataspace =
             static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
@@ -395,10 +414,6 @@
     int debugDdms = atoi(value);
     ALOGI_IF(debugDdms, "DDMS debugging not supported");
 
-    property_get("debug.sf.disable_backpressure", value, "0");
-    mPropagateBackpressure = !atoi(value);
-    ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
-
     property_get("debug.sf.enable_gl_backpressure", value, "0");
     mPropagateBackpressureClientComposition = atoi(value);
     ALOGI_IF(mPropagateBackpressureClientComposition,
@@ -444,10 +459,15 @@
         // deriving the setting from the set service name, but it
         // would be brittle if the name that's not 'default' is used
         // for production purposes later on.
-        setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+        android::hardware::details::setTrebleTestingOverride(true);
     }
 
     useFrameRateApi = use_frame_rate_api(true);
+
+    mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
+    base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
+
+    mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -486,6 +506,15 @@
 }
 
 sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
+    // onTransact already checks for some permissions, but adding an additional check here.
+    // This is to ensure that only system and graphics can request to create a secure
+    // display. Secure displays can show secure content so we add an additional restriction on it.
+    const int uid = IPCThreadState::self()->getCallingUid();
+    if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
+        ALOGE("Only privileged processes can create a secure display");
+        return nullptr;
+    }
+
     class DisplayToken : public BBinder {
         sp<SurfaceFlinger> flinger;
         virtual ~DisplayToken() {
@@ -541,11 +570,11 @@
 
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(internalDisplayId->value);
+    displayIds.push_back(*internalDisplayId);
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
         if (id != *internalDisplayId) {
-            displayIds.push_back(id.value);
+            displayIds.push_back(id);
         }
     }
 
@@ -554,7 +583,7 @@
 
 sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
     Mutex::Autolock lock(mStateLock);
-    return getPhysicalDisplayTokenLocked(DisplayId{displayId});
+    return getPhysicalDisplayTokenLocked(displayId);
 }
 
 status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
@@ -600,17 +629,6 @@
     if (mWindowManager != 0) {
         mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
     }
-    sp<IBinder> input(defaultServiceManager()->getService(
-            String16("inputflinger")));
-    if (input == nullptr) {
-        ALOGE("Failed to link to input service");
-    } else {
-        mInputFlinger = interface_cast<IInputFlinger>(input);
-    }
-
-    if (mVrFlinger) {
-      mVrFlinger->OnBootFinished();
-    }
 
     // stop boot animation
     // formerly we would just kill the process, but we now ask it to exit so it
@@ -621,7 +639,15 @@
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
-    static_cast<void>(schedule([this] {
+    sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+
+    static_cast<void>(schedule([=] {
+        if (input == nullptr) {
+            ALOGE("Failed to link to input service");
+        } else {
+            mInputFlinger = interface_cast<os::IInputFlinger>(input);
+        }
+
         readPersistentProperties();
         mPowerAdvisor.onBootFinished();
         mBootStage = BootStage::FINISHED;
@@ -687,42 +713,16 @@
                         : renderengine::RenderEngine::ContextPriority::MEDIUM)
                 .build()));
     mCompositionEngine->setTimeStats(mTimeStats);
-
-    LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
-            "Starting with vr flinger active is not currently supported.");
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
     mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
     LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+    const auto displayId = display->getPhysicalId();
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
                         "Internal display is disconnected.");
 
-    if (useVrFlinger) {
-        auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
-            // This callback is called from the vr flinger dispatch thread. We
-            // need to call signalTransaction(), which requires holding
-            // mStateLock when we're not on the main thread. Acquiring
-            // mStateLock from the vr flinger dispatch thread might trigger a
-            // deadlock in surface flinger (see b/66916578), so post a message
-            // to be handled on the main thread instead.
-            static_cast<void>(schedule([=] {
-                ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
-                mVrFlingerRequestsDisplay = requestDisplay;
-                signalTransaction();
-            }));
-        };
-        mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
-                                            getHwComposer()
-                                                    .fromPhysicalDisplayId(*display->getId())
-                                                    .value_or(0),
-                                            vrFlingerRequestDisplayCallback);
-        if (!mVrFlinger) {
-            ALOGE("Failed to start vrflinger");
-        }
-    }
-
     // initialize our drawing state
     mDrawingState = mCurrentState;
 
@@ -837,8 +837,9 @@
     state->layerStack = display->getLayerStack();
     state->orientation = display->getOrientation();
 
-    const Rect viewport = display->getViewport();
-    state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+    const Rect layerStackRect = display->getLayerStackSpaceRect();
+    state->layerStackSpaceRect =
+            layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize();
 
     return NO_ERROR;
 }
@@ -922,9 +923,10 @@
         const nsecs_t period = hwConfig->getVsyncPeriod();
         config.refreshRate = 1e9f / period;
 
-        const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
-        config.appVsyncOffset = offsets.late.app;
-        config.sfVsyncOffset = offsets.late.sf;
+        const auto vsyncConfigSet =
+                mVsyncConfiguration->getConfigsForRefreshRate(config.refreshRate);
+        config.appVsyncOffset = vsyncConfigSet.late.appOffset;
+        config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
         config.configGroup = hwConfig->getConfigGroup();
 
         // This is how far in advance a buffer must be queued for
@@ -934,7 +936,7 @@
         //
         // Normally it's one full refresh period (to give SF a chance to
         // latch the buffer), but this can be reduced by configuring a
-        // DispSync offset.  Any additional delays introduced by the hardware
+        // VsyncController offset.  Any additional delays introduced by the hardware
         // composer or panel must be accounted for here.
         //
         // We add an additional 1ms to allow for processing time and
@@ -952,7 +954,7 @@
         return BAD_VALUE;
     }
 
-    mScheduler->getDisplayStatInfo(stats);
+    mScheduler->getDisplayStatInfo(stats, systemTime());
     return NO_ERROR;
 }
 
@@ -1010,11 +1012,10 @@
         // switch.
         mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
-        // DispSync model is locked.
-        mVSyncModulator->onRefreshRateChangeInitiated();
+        // VsyncController model is locked.
+        modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
-        mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
-        mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+        updatePhaseConfiguration(refreshRate);
         mScheduler->setConfigChangePending(true);
     }
 
@@ -1042,7 +1043,12 @@
         } else {
             const HwcConfigIndexType config(mode);
             const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
-            const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}};
+            // Keep the old switching type.
+            const auto allowGroupSwitching =
+                    mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching;
+            const scheduler::RefreshRateConfigs::Policy policy{config,
+                                                               allowGroupSwitching,
+                                                               {fps, fps}};
             constexpr bool kOverridePolicy = false;
 
             return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
@@ -1073,16 +1079,16 @@
     if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
         mTimeStats->incrementRefreshRateSwitches();
     }
-    mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
-    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+    updatePhaseConfiguration(refreshRate);
     ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
 
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
         const nsecs_t vsyncPeriod =
                 mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
                         .getVsyncPeriod();
-        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    mUpcomingActiveConfig.configId, vsyncPeriod);
+        const auto physicalId = display->getPhysicalId();
+        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
+                                                  mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
 
@@ -1093,9 +1099,9 @@
 
     const auto& refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+
     mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
-    mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
-    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+    updatePhaseConfiguration(refreshRate);
     mScheduler->setConfigChangePending(false);
 }
 
@@ -1130,8 +1136,7 @@
     }
 
     mUpcomingActiveConfig = *desiredActiveConfig;
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
+    const auto displayId = display->getPhysicalId();
 
     ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps());
 
@@ -1142,7 +1147,7 @@
 
     hal::VsyncPeriodChangeTimeline outTimeline;
     auto status =
-            getHwComposer().setActiveConfigWithConstraints(*displayId,
+            getHwComposer().setActiveConfigWithConstraints(displayId,
                                                            mUpcomingActiveConfig.configId.value(),
                                                            constraints, &outTimeline);
     if (status != NO_ERROR) {
@@ -1416,8 +1421,8 @@
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mEventQueue->setEventConnection(
-                    mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
+            mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
+                                                   : nullptr);
         }
     }).wait();
 
@@ -1426,20 +1431,21 @@
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
     Mutex::Autolock lock(mStateLock);
-    return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE;
+    const auto expectedPresent = calculateExpectedPresentTime(when);
+    return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
+                                   /*deadlineTimestamp=*/expectedPresent)
+            ? NO_ERROR
+            : BAD_VALUE;
 }
 
-status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
-    TimedLock lock(mStateLock, s2ns(1), __FUNCTION__);
-    if (!lock.locked()) {
-        return TIMED_OUT;
-    }
-
-    const auto display = getDefaultDisplayDeviceLocked();
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
     outLayers->clear();
-    mCurrentState.traverseInZOrder(
-            [&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); });
-
+    schedule([=] {
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            outLayers->push_back(layer->getLayerDebugInfo(display.get()));
+        });
+    }).wait();
     return NO_ERROR;
 }
 
@@ -1545,7 +1551,7 @@
     mEventQueue->refresh();
 }
 
-nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
     const auto displayId = getInternalDisplayIdLocked();
     if (!displayId || !getHwComposer().isConnected(*displayId)) {
         return 0;
@@ -1577,7 +1583,7 @@
     bool periodFlushed = false;
     mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
-        mVSyncModulator->onRefreshRateChangeCompleted();
+        modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
     }
 }
 
@@ -1657,125 +1663,26 @@
     repaintEverythingForHWC();
 }
 
-void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
+void SurfaceFlinger::setVsyncEnabled(bool enabled) {
     ATRACE_CALL();
 
-    // Enable / Disable HWVsync from the main thread to avoid race conditions with
-    // display power state.
-    static_cast<void>(schedule([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); }));
-}
+    // On main thread to avoid race conditions with display power state.
+    static_cast<void>(schedule([=]() MAIN_THREAD {
+        mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
 
-void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
-    ATRACE_CALL();
-
-    mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-
-    if (const auto displayId = getInternalDisplayIdLocked()) {
-        sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
-        if (display && display->isPoweredOn()) {
-            setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+        if (const auto display = getDefaultDisplayDeviceLocked();
+            display && display->isPoweredOn()) {
+            getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
         }
-    }
-}
-
-void SurfaceFlinger::resetDisplayState() {
-    mScheduler->disableHardwareVsync(true);
-    // Clear the drawing state so that the logic inside of
-    // handleTransactionLocked will fire. It will determine the delta between
-    // mCurrentState and mDrawingState and re-apply all changes when we make the
-    // transition.
-    mDrawingState.displays.clear();
-    mDisplays.clear();
-}
-
-void SurfaceFlinger::updateVrFlinger() {
-    ATRACE_CALL();
-    if (!mVrFlinger)
-        return;
-    bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
-    if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) {
-        return;
-    }
-
-    if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) {
-        ALOGE("Vr flinger is only supported for remote hardware composer"
-              " service connections. Ignoring request to transition to vr"
-              " flinger.");
-        mVrFlingerRequestsDisplay = false;
-        return;
-    }
-
-    Mutex::Autolock _l(mStateLock);
-
-    sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display);
-
-    const hal::PowerMode currentDisplayPowerMode = display->getPowerMode();
-
-    // Clear out all the output layers from the composition engine for all
-    // displays before destroying the hardware composer interface. This ensures
-    // any HWC layers are destroyed through that interface before it becomes
-    // invalid.
-    for (const auto& [token, displayDevice] : mDisplays) {
-        displayDevice->getCompositionDisplay()->clearOutputLayers();
-    }
-
-    // This DisplayDevice will no longer be relevant once resetDisplayState() is
-    // called below. Clear the reference now so we don't accidentally use it
-    // later.
-    display.clear();
-
-    if (!vrFlingerRequestsDisplay) {
-        mVrFlinger->SeizeDisplayOwnership();
-    }
-
-    resetDisplayState();
-    // Delete the current instance before creating the new one
-    mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
-    mCompositionEngine->setHwComposer(getFactory().createHWComposer(
-            vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
-    mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
-
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
-                        "Switched to non-remote hardware composer");
-
-    if (vrFlingerRequestsDisplay) {
-        mVrFlinger->GrantDisplayOwnership();
-    }
-
-    mVisibleRegionsDirty = true;
-    invalidateHwcGeometry();
-
-    // Re-enable default display.
-    display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display);
-    setPowerModeInternal(display, currentDisplayPowerMode);
-
-    // Reset the timing values to account for the period of the swapped in HWC
-    const nsecs_t vsyncPeriod = getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
-    // The present fences returned from vr_hwc are not an accurate
-    // representation of vsync times.
-    mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework);
-
-    // Use phase of 0 since phase is not known.
-    // Use latency of 0, which will snap to the ideal latency.
-    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
-    setCompositorTimingSnapped(stats, 0);
-
-    mScheduler->resyncToHardwareVsync(false, vsyncPeriod);
-
-    mRepaintEverything = true;
-    setTransactionFlags(eDisplayTransactionNeeded);
+    }));
 }
 
 sp<Fence> SurfaceFlinger::previousFrameFence() {
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
-    return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
-                                                : mPreviousPresentFences[1];
+    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
+                                                          : mPreviousPresentFences[1];
 }
 
 bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1804,17 +1711,17 @@
 
 nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
-    const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
+    mScheduler->getDisplayStatInfo(&stats, now);
     // Inflate the expected present time if we're targetting the next vsync.
-    return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
+                                                          : stats.vsyncTime + stats.vsyncPeriod;
 }
 
-void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            onMessageInvalidate(expectedVSyncTime);
+            onMessageInvalidate(vsyncId, expectedVSyncTime);
             break;
         }
         case MessageQueue::REFRESH: {
@@ -1824,7 +1731,7 @@
     }
 }
 
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
 
     const nsecs_t frameStart = systemTime();
@@ -1838,10 +1745,7 @@
     // for the present fence to fire instead of just giving up on this frame to handle cases
     // where present fence is just about to get signaled.
     const int graceTimeForPresentFenceMs =
-            (mPropagateBackpressure &&
-             (mPropagateBackpressureClientComposition || !mHadClientComposition))
-            ? 1
-            : 0;
+            (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0;
 
     // Pending frames may trigger backpressure propagation.
     const TracedOrdinal<bool> framePending = {"PrevFramePending",
@@ -1856,7 +1760,7 @@
     // smaller than a typical frame duration, but should not be so small
     // that it reports reasonable drift as a missed frame.
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
+    mScheduler->getDisplayStatInfo(&stats, systemTime());
     const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
     const nsecs_t previousPresentTime = previousFramePresentTime();
     const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
@@ -1900,7 +1804,7 @@
         ON_MAIN_THREAD(setActiveConfigInternal());
     }
 
-    if (framePending && mPropagateBackpressure) {
+    if (framePending) {
         if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
             signalLayerUpdate();
             return;
@@ -1914,6 +1818,7 @@
     // ...but if it's larger than 1s then we missed the trace cutoff.
     static constexpr nsecs_t kMaxJankyDuration =
             std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+    nsecs_t jankDurationToUpload = -1;
     // If we're in a user build then don't push any atoms
     if (!mIsUserBuild && mMissedFrameJankCount > 0) {
         const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
@@ -1925,10 +1830,7 @@
             const nsecs_t currentTime = systemTime();
             const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
             if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
-                ATRACE_NAME("Jank detected");
-                const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
-                android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
-                                           jankyDurationMillis, mMissedFrameJankCount);
+                jankDurationToUpload = jankDuration;
             }
 
             // We either reported a jank event or we missed the trace
@@ -1940,26 +1842,31 @@
         }
     }
 
-    // Now that we're going to make it to the handleMessageTransaction()
-    // call below it's safe to call updateVrFlinger(), which will
-    // potentially trigger a display handoff.
-    updateVrFlinger();
-
     if (mTracingEnabledChanged) {
         mTracingEnabled = mTracing.isEnabled();
         mTracingEnabledChanged = false;
     }
 
+    if (mRefreshRateOverlaySpinner) {
+        if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+            mRefreshRateOverlay->onInvalidate();
+        }
+    }
+
     bool refreshNeeded;
     {
-        ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
+        mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
+                mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
+                mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
+        const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
+        ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
+
+        mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
 
         refreshNeeded = handleMessageTransaction();
         refreshNeeded |= handleMessageInvalidate();
-        if (mTracingEnabled) {
-            mAddCompositionStateToTrace =
-                    mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION);
-            if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) {
+        if (tracePreComposition) {
+            if (mVisibleRegionsDirty) {
                 mTracing.notifyLocked("visibleRegionsDirty");
             }
         }
@@ -1981,6 +1888,7 @@
 
     refreshNeeded |= mRepaintEverything;
     if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
+        mLastJankDuration = jankDurationToUpload;
         // Signal a refresh if a transaction modified the window state,
         // a new buffer was latched, or if HWC has requested a full
         // repaint
@@ -2001,8 +1909,10 @@
 
     bool flushedATransaction = flushTransactionQueues();
 
-    bool runHandleTransaction = transactionFlags &&
-            ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+    bool runHandleTransaction =
+            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+            flushedATransaction ||
+            mForceTraversal;
 
     if (runHandleTransaction) {
         handleTransaction(eTransactionMask);
@@ -2033,7 +1943,7 @@
             refreshArgs.layers.push_back(layerFE);
     });
     refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-    for (sp<Layer> layer : mLayersWithQueuedFrames) {
+    for (auto layer : mLayersWithQueuedFrames) {
         if (auto layerFE = layer->getCompositionEngineLayerFE())
             refreshArgs.layersWithQueuedFrames.push_back(layerFE);
     }
@@ -2078,7 +1988,10 @@
     postFrame();
     postComposition();
 
-    const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
+    mFrameTimeline->setSfPresent(systemTime(),
+                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+
+    const bool prevFrameHadClientComposition = mHadClientComposition;
 
     mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
         const auto& state = pair.second->getCompositionDisplay()->getState();
@@ -2093,21 +2006,25 @@
                 const auto& state = pair.second->getCompositionDisplay()->getState();
                 return state.reusedClientComposition;
             });
-
-    // Only report a strategy change if we move in and out of composition with hw overlays
-    if (prevFrameHadDeviceComposition != mHadDeviceComposition) {
+    // Only report a strategy change if we move in and out of client composition
+    if (prevFrameHadClientComposition != mHadClientComposition) {
         mTimeStats->incrementCompositionStrategyChanges();
     }
 
-    mVSyncModulator->onRefreshed(mHadClientComposition);
+    // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
+    const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
+    modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
 
     mLayersWithQueuedFrames.clear();
-    if (mVisibleRegionsDirty) {
-        mVisibleRegionsDirty = false;
-        if (mTracingEnabled && mAddCompositionStateToTrace) {
+    if (mTracingEnabled && mTracePostComposition) {
+        // This may block if SurfaceTracing is running in sync mode.
+        if (mVisibleRegionsDirty) {
             mTracing.notify("visibleRegionsDirty");
+        } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
+            mTracing.notify("bufferLatched");
         }
     }
+    mVisibleRegionsDirty = false;
 
     if (mCompositionEngine->needsAnotherUpdate()) {
         signalLayerUpdate();
@@ -2161,12 +2078,12 @@
                                                 nsecs_t compositeToPresentLatency) {
     // Integer division and modulo round toward 0 not -inf, so we need to
     // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+    nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
             ? (stats.vsyncPeriod -
-               (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
-            : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
+               (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
+            : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
 
-    // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
+    // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
     if (idealLatency <= 0) {
         idealLatency = stats.vsyncPeriod;
     }
@@ -2176,7 +2093,7 @@
     // Reducing jitter is important if an app attempts to extrapolate
     // something (such as user input) to an accurate diasplay time.
     // Snapping also allows an app to precisely calculate
-    // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
+    // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
     nsecs_t bias = stats.vsyncPeriod / 2;
     int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
     nsecs_t snappedCompositeToPresentLatency =
@@ -2194,7 +2111,7 @@
     ALOGV("postComposition");
 
     nsecs_t dequeueReadyTime = systemTime();
-    for (auto& layer : mLayersWithQueuedFrames) {
+    for (auto layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
 
@@ -2215,12 +2132,12 @@
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
     mPreviousPresentFences[0] =
-            display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
+            display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
+    mScheduler->getDisplayStatInfo(&stats, systemTime());
 
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
@@ -2249,7 +2166,8 @@
         mScheduler->addPresentFence(presentFenceTime);
     }
 
-    const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+    const bool isDisplayConnected =
+            display && getHwComposer().isConnected(display->getPhysicalId());
 
     if (!hasSyncFramework) {
         if (isDisplayConnected && display->isPoweredOn()) {
@@ -2266,7 +2184,8 @@
         } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+            const nsecs_t presentTime =
+                    getHwComposer().getRefreshTimestamp(display->getPhysicalId());
             mAnimFrameTracker.setActualPresentTime(presentTime);
         }
         mAnimFrameTracker.advanceFrame();
@@ -2287,6 +2206,14 @@
     const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
     mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
 
+    if (mLastJankDuration > 0) {
+        ATRACE_NAME("Jank detected");
+        const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
+        android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
+                                   mMissedFrameJankCount);
+        mLastJankDuration = -1;
+    }
+
     if (isDisplayConnected && !display->isPoweredOn()) {
         return;
     }
@@ -2339,7 +2266,7 @@
 }
 
 FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
-    return displayDevice.getViewport().toFloatRect();
+    return displayDevice.getLayerStackSpaceRect().toFloatRect();
 }
 
 void SurfaceFlinger::computeLayerBounds() {
@@ -2360,7 +2287,7 @@
 
 void SurfaceFlinger::postFrame() {
     const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-    if (display && getHwComposer().isConnected(*display->getId())) {
+    if (display && getHwComposer().isConnected(display->getPhysicalId())) {
         uint32_t flipCount = display->getPageFlipCount();
         if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
             logFrameStats();
@@ -2387,7 +2314,7 @@
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
 
-    mVSyncModulator->onTransactionHandled();
+    modulateVsync(&VsyncModulator::onTransactionCommit);
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
 
@@ -2398,14 +2325,14 @@
 
 void SurfaceFlinger::processDisplayHotplugEventsLocked() {
     for (const auto& event : mPendingHotplugEvents) {
-        const std::optional<DisplayIdentificationInfo> info =
+        std::optional<DisplayIdentificationInfo> info =
                 getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
 
         if (!info) {
             continue;
         }
 
-        const DisplayId displayId = info->id;
+        const auto displayId = info->id;
         const auto it = mPhysicalDisplayTokens.find(displayId);
 
         if (event.connection == hal::Connection::CONNECTED) {
@@ -2420,9 +2347,9 @@
                 state.physical = {.id = displayId,
                                   .type = getHwComposer().getDisplayConnectionType(displayId),
                                   .hwcDisplayId = event.hwcDisplayId,
-                                  .deviceProductInfo = info->deviceProductInfo};
+                                  .deviceProductInfo = std::move(info->deviceProductInfo)};
                 state.isSecure = true; // All physical displays are currently considered secure.
-                state.displayName = info->name;
+                state.displayName = std::move(info->name);
 
                 sp<IBinder> token = new BBinder();
                 mCurrentState.displays.add(token, state);
@@ -2434,7 +2361,10 @@
 
                 const auto token = it->second;
                 auto& state = mCurrentState.displays.editValueFor(token);
-                state.sequenceId = DisplayDeviceState{}.sequenceId;
+                state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId
+                if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
+                    state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
+                }
             }
         } else {
             ALOGV("Removing display %s", to_string(displayId).c_str());
@@ -2465,7 +2395,6 @@
         const DisplayDeviceState& state,
         const sp<compositionengine::DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer) {
-    auto displayId = compositionDisplay->getDisplayId();
     DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
     creationArgs.sequenceId = state.sequenceId;
     creationArgs.isSecure = state.isSecure;
@@ -2477,26 +2406,26 @@
         creationArgs.connectionType = physical->type;
     }
 
-    const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
-    creationArgs.isPrimary = isInternalDisplay;
+    if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+        creationArgs.isPrimary = id == getInternalDisplayIdLocked();
 
-    if (useColorManagement && displayId) {
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
-        for (ColorMode colorMode : modes) {
-            if (isWideColorMode(colorMode)) {
-                creationArgs.hasWideColorGamut = true;
+        if (useColorManagement) {
+            std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
+            for (ColorMode colorMode : modes) {
+                if (isWideColorMode(colorMode)) {
+                    creationArgs.hasWideColorGamut = true;
+                }
+
+                std::vector<RenderIntent> renderIntents =
+                        getHwComposer().getRenderIntents(*id, colorMode);
+                creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
             }
-
-            std::vector<RenderIntent> renderIntents =
-                    getHwComposer().getRenderIntents(*displayId, colorMode);
-            creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
         }
     }
 
-    if (displayId) {
-        getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
-        creationArgs.supportedPerFrameMetadata =
-                getHwComposer().getSupportedPerFrameMetadata(*displayId);
+    if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+        getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
+        creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
     }
 
     auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
@@ -2511,7 +2440,7 @@
     }
 
     creationArgs.physicalOrientation =
-            isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
+            creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
 
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -2533,14 +2462,15 @@
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
     if (!state.isVirtual()) {
-        LOG_ALWAYS_FATAL_IF(!displayId);
-        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+        const auto physicalId = display->getPhysicalId();
+        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(physicalId));
         display->setActiveConfig(activeConfigId);
         display->setDeviceProductInfo(state.physical->deviceProductInfo);
     }
 
     display->setLayerStack(state.layerStack);
-    display->setProjection(state.orientation, state.viewport, state.frame);
+    display->setProjection(state.orientation, state.layerStackSpaceRect,
+                           state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
 
     return display;
@@ -2582,7 +2512,8 @@
     builder.setIsSecure(state.isSecure);
     builder.setLayerStackId(state.layerStack);
     builder.setPowerAdvisor(&mPowerAdvisor);
-    builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+    builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
+    builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
     builder.setName(state.displayName);
     const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
 
@@ -2592,11 +2523,13 @@
     sp<IGraphicBufferConsumer> bqConsumer;
     getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
 
-    std::optional<DisplayId> displayId = compositionDisplay->getId();
+    DisplayId displayId = compositionDisplay->getId();
 
     if (state.isVirtual()) {
+        const auto virtualId = VirtualDisplayId::tryCast(displayId);
+        LOG_FATAL_IF(!virtualId);
         sp<VirtualDisplaySurface> vds =
-                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+                new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer,
                                           bqConsumer, state.displayName);
 
         displaySurface = vds;
@@ -2606,9 +2539,9 @@
                  "adding a supported display, but rendering "
                  "surface is provided (%p), ignoring it",
                  state.surface.get());
-
-        LOG_ALWAYS_FATAL_IF(!displayId);
-        displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+        const auto physicalId = PhysicalDisplayId::tryCast(displayId);
+        LOG_FATAL_IF(!physicalId);
+        displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer,
                                                 maxGraphicsWidth, maxGraphicsHeight);
         producer = bqProducer;
     }
@@ -2618,8 +2551,7 @@
                                                        displaySurface, producer);
     mDisplays.emplace(displayToken, display);
     if (!state.isVirtual()) {
-        LOG_FATAL_IF(!displayId);
-        dispatchDisplayHotplugEvent(displayId->value, true);
+        dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
 
     if (display->isPrimary()) {
@@ -2629,13 +2561,9 @@
 
 void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
-        // Save display ID before disconnecting.
-        const auto displayId = display->getId();
         display->disconnect();
-
         if (!display->isVirtual()) {
-            LOG_FATAL_IF(!displayId);
-            dispatchDisplayHotplugEvent(displayId->value, false);
+            dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
         }
     }
 
@@ -2649,6 +2577,7 @@
     const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
     if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
         // changing the surface is like destroying and recreating the DisplayDevice
+        getRenderEngine().cleanFramebufferCache();
         if (const auto display = getDisplayDeviceLocked(displayToken)) {
             display->disconnect();
         }
@@ -2658,7 +2587,8 @@
         }
         processDisplayAdded(displayToken, currentState);
         if (currentState.physical) {
-            initializeDisplays();
+            const auto display = getDisplayDeviceLocked(displayToken);
+            setPowerModeInternal(display, hal::PowerMode::ON);
         }
         return;
     }
@@ -2668,10 +2598,10 @@
             display->setLayerStack(currentState.layerStack);
         }
         if ((currentState.orientation != drawingState.orientation) ||
-            (currentState.viewport != drawingState.viewport) ||
-            (currentState.frame != drawingState.frame)) {
-            display->setProjection(currentState.orientation, currentState.viewport,
-                                   currentState.frame);
+            (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
+            (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
+            display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
+                                   currentState.orientedDisplaySpaceRect);
         }
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
@@ -2742,7 +2672,8 @@
      * (perform the transaction for each of them if needed)
      */
 
-    if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
+    if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
+        mForceTraversal = false;
         mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
@@ -2755,7 +2686,6 @@
                 mInputInfoChanged = true;
             }
         });
-        mTraversalNeededMainThread = false;
     }
 
     /*
@@ -2879,23 +2809,26 @@
         setInputWindowsFinished();
     }
 
+    for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+        mInputFlinger->setFocusedWindow(focusRequest);
+    }
     mInputWindowCommands.clear();
 }
 
 void SurfaceFlinger::updateInputWindowInfo() {
-    std::vector<InputWindowInfo> inputHandles;
+    std::vector<InputWindowInfo> inputInfos;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        if (layer->hasInput()) {
+        if (layer->needsInputInfo()) {
             // When calculating the screen bounds we ignore the transparent region since it may
             // result in an unwanted offset.
-            inputHandles.push_back(layer->fillInputInfo());
+            inputInfos.push_back(layer->fillInputInfo());
         }
     });
 
-    mInputFlinger->setInputWindows(inputHandles,
-                                   mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
-                                                                         : nullptr);
+    mInputFlinger->setInputWindows(inputInfos,
+                               mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
+                                                                     : nullptr);
 }
 
 void SurfaceFlinger::commitInputWindowCommands() {
@@ -2906,7 +2839,7 @@
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
-        if (display->getId()) {
+        if (HalDisplayId::tryCast(display->getId())) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
@@ -2924,7 +2857,7 @@
     changeRefreshRateLocked(refreshRate, event);
 }
 
-void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) {
     if (mScheduler) {
         // In practice it's not allowed to hotplug in/out the primary display once it's been
         // connected during startup, but some tests do it, so just warn and return.
@@ -2942,24 +2875,29 @@
                                                           currentConfig, hal::PowerMode::OFF);
     mRefreshRateStats->setConfigMode(currentConfig);
 
-    mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+    mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
 
     // start the EventThread
-    mScheduler =
-            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         *mRefreshRateConfigs, *this);
+    mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
+    const auto configs = mVsyncConfiguration->getCurrentConfigs();
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+            mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
+                                         /*workDuration=*/configs.late.appWorkDuration,
+                                         /*readyDuration=*/configs.late.sfWorkDuration,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+            mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
+                                         /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+                                         /*readyDuration=*/configs.late.sfWorkDuration,
                                          [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
-    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
-                            mPhaseConfiguration->getCurrentOffsets());
+    mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+                           configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
@@ -2971,14 +2909,33 @@
     // This is a bit hacky, but this avoids a back-pointer into the main SF
     // classes from EventThread, and there should be no run-time binder cost
     // anyway since there are no connected apps at this point.
-    const nsecs_t vsyncPeriod =
-            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
-    mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig,
-                                vsyncPeriod);
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
+                                              vsyncPeriod);
+    static auto ignorePresentFences =
+            base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
+    mScheduler->setIgnorePresentFences(
+            ignorePresentFences ||
+            getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
 }
 
-void SurfaceFlinger::commitTransaction()
-{
+void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
+    mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
+    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+                   refreshRate.getVsyncPeriod());
+}
+
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
+                                    nsecs_t vsyncPeriod) {
+    mScheduler->setDuration(mAppConnectionHandle,
+                            /*workDuration=*/config.appWorkDuration,
+                            /*readyDuration=*/config.sfWorkDuration);
+    mScheduler->setDuration(mSfConnectionHandle,
+                            /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+                            /*readyDuration=*/config.sfWorkDuration);
+    mEventQueue->setDuration(config.sfWorkDuration);
+}
+
+void SurfaceFlinger::commitTransaction() {
     commitTransactionLocked();
     mTransactionPending = false;
     mAnimTransactionPending = false;
@@ -3014,15 +2971,19 @@
     // clear the "changed" flags in current state
     mCurrentState.colorMatrixChanged = false;
 
-    mDrawingState.traverse([&](Layer* layer) {
-        layer->commitChildList();
-
-        // If the layer can be reached when traversing mDrawingState, then the layer is no
-        // longer offscreen. Remove the layer from the offscreenLayer set.
-        if (mOffscreenLayers.count(layer)) {
-            mOffscreenLayers.erase(layer);
-        }
-    });
+    for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+        rootLayer->commitChildList();
+    }
+    // TODO(b/163019109): See if this traversal is needed at all...
+    if (!mOffscreenLayers.empty()) {
+        mDrawingState.traverse([&](Layer* layer) {
+            // If the layer can be reached when traversing mDrawingState, then the layer is no
+            // longer offscreen. Remove the layer from the offscreenLayer set.
+            if (mOffscreenLayers.count(layer)) {
+                mOffscreenLayers.erase(layer);
+            }
+        });
+    }
 
     commitOffscreenLayers();
     mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
@@ -3075,7 +3036,7 @@
         if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
-                mLayersWithQueuedFrames.push_back(layer);
+                mLayersWithQueuedFrames.emplace(layer);
             } else {
                 ATRACE_NAME("!layer->shouldPresentNow()");
                 layer->useEmptyDamage();
@@ -3218,21 +3179,18 @@
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
-    return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
+    return setTransactionFlags(flags, TransactionSchedule::Late);
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
-                                             Scheduler::TransactionStart transactionStart) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
     uint32_t old = mTransactionFlags.fetch_or(flags);
-    mVSyncModulator->setTransactionStart(transactionStart);
-    if ((old & flags)==0) { // wake the server up
-        signalTransaction();
-    }
+    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+    if ((old & flags) == 0) signalTransaction();
     return old;
 }
 
-uint32_t SurfaceFlinger::setTransactionFlagsNoWake(uint32_t flags) {
-    return mTransactionFlags.fetch_or(flags);
+void SurfaceFlinger::setTraversalNeeded() {
+    mForceTraversal = true;
 }
 
 bool SurfaceFlinger::flushTransactionQueues() {
@@ -3256,11 +3214,13 @@
                     break;
                 }
                 transactions.push_back(transaction);
-                applyTransactionState(transaction.states, transaction.displays, transaction.flags,
+                applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                      transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
                                       transaction.buffer, transaction.postTime,
                                       transaction.privileged, transaction.hasListenerCallbacks,
-                                      transaction.listenerCallbacks, /*isMainThread*/ true);
+                                      transaction.listenerCallbacks, transaction.originPid,
+                                      transaction.originUid, transaction.id, /*isMainThread*/ true);
                 transactionQueue.pop();
                 flushedATransaction = true;
             }
@@ -3304,11 +3264,12 @@
     return true;
 }
 
-void SurfaceFlinger::setTransactionState(
-        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
-        const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
-        int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks) {
+status_t SurfaceFlinger::setTransactionState(
+        int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+        const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+        const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
     const int64_t postTime = systemTime();
@@ -3340,25 +3301,33 @@
         mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
     }
 
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int originPid = ipc->getCallingPid();
+    const int originUid = ipc->getCallingUid();
+
     if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
-        mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
-                                               uncacheBuffer, postTime, privileged,
-                                               hasListenerCallbacks, listenerCallbacks);
+        mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
+                                               desiredPresentTime, uncacheBuffer, postTime,
+                                               privileged, hasListenerCallbacks, listenerCallbacks,
+                                               originPid, originUid, transactionId);
         setTransactionFlags(eTransactionFlushNeeded);
-        return;
+        return NO_ERROR;
     }
 
-    applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
-                          uncacheBuffer, postTime, privileged, hasListenerCallbacks,
-                          listenerCallbacks);
+    applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
+                          desiredPresentTime, uncacheBuffer, postTime, privileged,
+                          hasListenerCallbacks, listenerCallbacks, originPid, originUid,
+                          transactionId, /*isMainThread*/ false);
+    return NO_ERROR;
 }
 
 void SurfaceFlinger::applyTransactionState(
-        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+        const Vector<DisplayState>& displays, uint32_t flags,
         const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
         const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
         bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-        bool isMainThread) {
+        int originPid, int originUid, uint64_t transactionId, bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3392,8 +3361,9 @@
     std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
-                                                 listenerCallbacksWithSurfaces);
+        clientStateFlags |=
+                setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
+                                     privileged, listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
                 mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
@@ -3412,7 +3382,11 @@
     }
     transactionFlags |= clientStateFlags;
 
-    transactionFlags |= addInputWindowCommands(inputWindowCommands);
+    if (privileged) {
+        transactionFlags |= addInputWindowCommands(inputWindowCommands);
+    } else if (!inputWindowCommands.empty()) {
+        ALOGE("Only privileged callers are allowed to send input commands.");
+    }
 
     if (uncacheBuffer.isValid()) {
         ClientCache::getInstance().erase(uncacheBuffer);
@@ -3432,18 +3406,34 @@
     // so we don't have to wake up again next frame to preform an uneeded traversal.
     if (isMainThread && (transactionFlags & eTraversalNeeded)) {
         transactionFlags = transactionFlags & (~eTraversalNeeded);
-        mTraversalNeededMainThread = true;
+        mForceTraversal = true;
     }
 
+    const auto schedule = [](uint32_t flags) {
+        if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+        if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+        if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+        return TransactionSchedule::Late;
+    }(flags);
+
     if (transactionFlags) {
         if (mInterceptor->isEnabled()) {
-            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
+            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
+                                          originPid, originUid, transactionId);
+        }
+
+        // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+        if (flags & eEarlyWakeup) {
+            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+        }
+
+        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
         }
 
         // this triggers the transaction
-        const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
-                                                  : Scheduler::TransactionStart::NORMAL;
-        setTransactionFlags(transactionFlags, start);
+        setTransactionFlags(transactionFlags, schedule);
 
         if (flags & eAnimation) {
             mAnimTransactionPending = true;
@@ -3480,6 +3470,12 @@
                 break;
             }
         }
+    } else {
+        // Update VsyncModulator state machine even if transaction is not needed.
+        if (schedule == TransactionSchedule::EarlyStart ||
+            schedule == TransactionSchedule::EarlyEnd) {
+            modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+        }
     }
 }
 
@@ -3508,12 +3504,12 @@
             state.orientation = s.orientation;
             flags |= eDisplayTransactionNeeded;
         }
-        if (state.frame != s.frame) {
-            state.frame = s.frame;
+        if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) {
+            state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect;
             flags |= eDisplayTransactionNeeded;
         }
-        if (state.viewport != s.viewport) {
-            state.viewport = s.viewport;
+        if (state.layerStackSpaceRect != s.layerStackSpaceRect) {
+            state.layerStackSpaceRect = s.layerStackSpaceRect;
             flags |= eDisplayTransactionNeeded;
         }
     }
@@ -3544,8 +3540,8 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(
-        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
-        bool privileged,
+        int64_t frameTimelineVsyncId, const ComposerState& composerState,
+        int64_t desiredPresentTime, int64_t postTime, bool privileged,
         std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
 
@@ -3614,7 +3610,8 @@
         const auto& p = layer->getParent();
         if (p == nullptr) {
             ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-            if (layer->setRelativeLayer(s.relativeLayerHandle, s.z) && idx >= 0) {
+            if (layer->setRelativeLayer(s.relativeLayerSurfaceControl->getHandle(), s.z) &&
+                idx >= 0) {
                 mCurrentState.layersSortedByZ.removeAt(idx);
                 mCurrentState.layersSortedByZ.add(layer);
                 // we need traversal (state changed)
@@ -3622,7 +3619,7 @@
                 flags |= eTransactionNeeded|eTraversalNeeded;
             }
         } else {
-            if (p->setChildRelativeLayer(layer, s.relativeLayerHandle, s.z)) {
+            if (p->setChildRelativeLayer(layer, s.relativeLayerSurfaceControl->getHandle(), s.z)) {
                 flags |= eTransactionNeeded|eTraversalNeeded;
             }
         }
@@ -3687,6 +3684,9 @@
     if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
         if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eBlurRegionsChanged) {
+        if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
@@ -3708,35 +3708,19 @@
         }
     }
     if (what & layer_state_t::eDeferTransaction_legacy) {
-        if (s.barrierHandle_legacy != nullptr) {
-            layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
-        } else if (s.barrierGbp_legacy != nullptr) {
-            const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
-            if (authenticateSurfaceTextureLocked(gbp)) {
-                const auto& otherLayer =
-                    (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
-                layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
-            } else {
-                ALOGE("Attempt to defer transaction to to an"
-                        " unrecognized GraphicBufferProducer");
-            }
-        }
+        layer->deferTransactionUntil_legacy(s.barrierSurfaceControl_legacy->getHandle(),
+                                            s.barrierFrameNumber);
         // We don't trigger a traversal here because if no other state is
         // changed, we don't want this to cause any more work
     }
     if (what & layer_state_t::eReparentChildren) {
-        if (layer->reparentChildren(s.reparentHandle)) {
+        if (layer->reparentChildren(s.reparentSurfaceControl->getHandle())) {
             flags |= eTransactionNeeded|eTraversalNeeded;
         }
     }
     if (what & layer_state_t::eDetachChildren) {
         layer->detachChildren();
     }
-    if (what & layer_state_t::eOverrideScalingModeChanged) {
-        layer->setOverrideScalingMode(s.overrideScalingMode);
-        // We don't trigger a traversal here because if no other state is
-        // changed, we don't want this to cause any more work
-    }
     if (what & layer_state_t::eTransformChanged) {
         if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
     }
@@ -3748,7 +3732,7 @@
         if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eFrameChanged) {
-        if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
+        if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eAcquireFenceChanged) {
         if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
@@ -3770,7 +3754,7 @@
     }
     if (what & layer_state_t::eInputInfoChanged) {
         if (privileged) {
-            layer->setInputInfo(s.inputInfo);
+            layer->setInputInfo(*s.inputHandle->getInfo());
             flags |= eTraversalNeeded;
         } else {
             ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
@@ -3801,6 +3785,11 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eFrameTimelineVsyncChanged) {
+        layer->setFrameTimelineVsyncForTransaction(s.frameTimelineVsyncId, postTime);
+    } else if (frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+        layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
+    }
     if (what & layer_state_t::eFixedTransformHintChanged) {
         if (layer->setFixedTransformHint(s.fixedTransformHint)) {
             flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
@@ -3812,7 +3801,10 @@
     // lose its relative z order.
     if (what & layer_state_t::eReparent) {
         bool hadParent = layer->hasParent();
-        if (layer->reparent(s.parentHandleForChild)) {
+        auto parentHandle = (s.parentSurfaceControlForChild)
+                ? s.parentSurfaceControlForChild->getHandle()
+                : nullptr;
+        if (layer->reparent(parentHandle)) {
             if (!hadParent) {
                 mCurrentState.layersSortedByZ.remove(layer);
             }
@@ -3846,11 +3838,17 @@
         buffer = s.buffer;
     }
     if (buffer) {
-        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
-                             s.cachedBuffer)) {
+        const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
+        const uint64_t frameNumber = frameNumberChanged
+                ? s.frameNumber
+                : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
+
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
+                             frameNumber)) {
             flags |= eTraversalNeeded;
         }
     }
+
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
@@ -3858,17 +3856,12 @@
 }
 
 uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
-    uint32_t flags = 0;
-    if (inputWindowCommands.syncInputWindows) {
-        flags |= eTraversalNeeded;
-    }
-
-    mPendingInputWindowCommands.merge(inputWindowCommands);
-    return flags;
+    bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+    return hasChanges ? eTraversalNeeded : 0;
 }
 
 status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
-                                     sp<IBinder>* outHandle) {
+                                     sp<IBinder>* outHandle, int32_t* outLayerId) {
     if (!mirrorFromHandle) {
         return NAME_NOT_FOUND;
     }
@@ -3893,6 +3886,7 @@
         mirrorLayer->mClonedChild = mirrorFrom->createClone();
     }
 
+    *outLayerId = mirrorLayer->sequence;
     return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
                           nullptr /* outTransformHint */);
 }
@@ -3901,8 +3895,8 @@
                                      uint32_t h, PixelFormat format, uint32_t flags,
                                      LayerMetadata metadata, sp<IBinder>* handle,
                                      sp<IGraphicBufferProducer>* gbp,
-                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
-                                     uint32_t* outTransformHint) {
+                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
+                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -3919,18 +3913,6 @@
 
     std::string uniqueName = getUniqueLayerName(name.string());
 
-    bool primaryDisplayOnly = false;
-
-    // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
-    // TODO b/64227542
-    if (metadata.has(METADATA_WINDOW_TYPE)) {
-        int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
-        if (windowType == 441731) {
-            metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
-            primaryDisplayOnly = true;
-        }
-    }
-
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
             result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
@@ -3971,10 +3953,6 @@
         return result;
     }
 
-    if (primaryDisplayOnly) {
-        layer->setPrimaryDisplayOnly();
-    }
-
     bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
     result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
                             addToCurrentState, outTransformHint);
@@ -3984,6 +3962,7 @@
     mInterceptor->saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
+    *outLayerId = layer->sequence;
     return result;
 }
 
@@ -4131,17 +4110,17 @@
     d.token = token;
     d.layerStack = 0;
     d.orientation = ui::ROTATION_0;
-    d.frame.makeInvalid();
-    d.viewport.makeInvalid();
+    d.orientedDisplaySpaceRect.makeInvalid();
+    d.layerStackSpaceRect.makeInvalid();
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
-                        {});
+    setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
+                        mPendingInputWindowCommands, -1, {}, false, {},
+                        0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
-
-    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
 
     // Use phase of 0 since phase is not known.
@@ -4155,23 +4134,14 @@
     static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
 }
 
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
-    if (mHWCVsyncState != enabled) {
-        getHwComposer().setVsyncEnabled(displayId, enabled);
-        mHWCVsyncState = enabled;
-    }
-}
-
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
     if (display->isVirtual()) {
         ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
         return;
     }
 
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-
-    ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+    const auto displayId = display->getPhysicalId();
+    ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
     const hal::PowerMode currentMode = display->getPowerMode();
     if (mode == currentMode) {
@@ -4183,16 +4153,16 @@
     if (mInterceptor->isEnabled()) {
         mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
-
+    const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     if (currentMode == hal::PowerMode::OFF) {
         if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
             ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
         }
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
-            setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+            getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+            mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
         }
 
         mVisibleRegionsDirty = true;
@@ -4209,17 +4179,17 @@
         }
 
         // Make sure HWVsync is disabled before turning off the display
-        setVsyncEnabledInHWC(*displayId, hal::Vsync::DISABLE);
+        getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE);
 
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+            mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
@@ -4227,10 +4197,10 @@
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
     } else {
         ALOGE("Attempting to set unknown power mode: %d\n", mode);
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
     }
 
     if (display->isPrimary()) {
@@ -4239,7 +4209,7 @@
         mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
     }
 
-    ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+    ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
@@ -4270,8 +4240,7 @@
     } else {
         static const std::unordered_map<std::string, Dumper> dumpers = {
                 {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
-                {"--dispsync"s,
-                 dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })},
+                {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
                 {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
                 {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
@@ -4281,6 +4250,7 @@
                 {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
                 {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
                 {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+                {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
         };
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4320,7 +4290,7 @@
 
 status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
     if (asProto && mTracing.isEnabled()) {
-        mTracing.writeToFileAsync();
+        mTracing.writeToFile();
     }
 
     return doDump(fd, DumpArgs(), asProto);
@@ -4332,7 +4302,7 @@
 }
 
 void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
-    StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
+    StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
 
     if (args.size() > 1) {
         const auto name = String8(args[1]);
@@ -4363,6 +4333,10 @@
     mTimeStats->parseArgs(asProto, args, result);
 }
 
+void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const {
+    mFrameTimeline->parseArgs(args, result);
+}
+
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
@@ -4394,31 +4368,24 @@
     mRefreshRateStats->dump(result);
     result.append("\n");
 
-    mPhaseConfiguration->dump(result);
+    mVsyncConfiguration->dump(result);
     StringAppendF(&result,
                   "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
-                  dispSyncPresentTimeOffset, getVsyncPeriod());
+                  dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
 
     scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
-    StringAppendF(&result,
-                  "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d"
-                  ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
-                  policy.defaultConfig.value(), policy.primaryRange.min, policy.primaryRange.max,
-                  policy.appRequestRange.min, policy.appRequestRange.max);
+    StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
+                  policy.toString().c_str());
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
     scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
     if (currentPolicy != policy) {
-        StringAppendF(&result,
-                      "DesiredDisplayConfigSpecs (Override): default config ID: %d"
-                      ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
-                      currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
-                      currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
-                      currentPolicy.appRequestRange.max);
+        StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
+                      currentPolicy.toString().c_str());
     }
 
     mScheduler->dump(mAppConnectionHandle, result);
-    mScheduler->getPrimaryDispSync().dump(result);
+    mScheduler->dumpVsync(result);
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4498,7 +4465,7 @@
 
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
@@ -4555,7 +4522,7 @@
     // TODO: print out if wide-color mode is active or not
 
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
@@ -4644,8 +4611,6 @@
     result.append("Build configuration:");
     colorizer.reset(result);
     appendSfConfigString(result);
-    appendUiConfigString(result);
-    appendGuiConfigString(result);
     result.append("\n");
 
     result.append("\nDisplay identification data:\n");
@@ -4741,12 +4706,22 @@
     if (const auto displayId = getInternalDisplayIdLocked();
         displayId && getHwComposer().isConnected(*displayId)) {
         const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+        std::string fps, xDpi, yDpi;
+        if (activeConfig) {
+            fps = base::StringPrintf("%.2f Hz",
+                                     1e9f / getHwComposer().getDisplayVsyncPeriod(*displayId));
+            xDpi = base::StringPrintf("%.2f", activeConfig->getDpiX());
+            yDpi = base::StringPrintf("%.2f", activeConfig->getDpiY());
+        } else {
+            fps = "unknown";
+            xDpi = "unknown";
+            yDpi = "unknown";
+        }
         StringAppendF(&result,
-                      "  refresh-rate              : %f fps\n"
-                      "  x-dpi                     : %f\n"
-                      "  y-dpi                     : %f\n",
-                      1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
-                      activeConfig->getDpiX(), activeConfig->getDpiY());
+                      "  refresh-rate              : %s\n"
+                      "  x-dpi                     : %s\n"
+                      "  y-dpi                     : %s\n",
+                      fps.c_str(), xDpi.c_str(), yDpi.c_str());
     }
 
     StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -4761,7 +4736,7 @@
      * HWC layer minidump
      */
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = HalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
@@ -4790,15 +4765,6 @@
     const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
     alloc.dump(result);
 
-    /*
-     * Dump VrFlinger state if in use.
-     */
-    if (mVrFlingerRequestsDisplay && mVrFlinger) {
-        result.append("VrFlinger state:\n");
-        result.append(mVrFlinger->Dump());
-        result.append("\n");
-    }
-
     result.append(mTimeStats->miniDump());
     result.append("\n");
 }
@@ -4905,11 +4871,14 @@
         // special permissions.
         case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
-        case SET_DISPLAY_BRIGHTNESS: {
+        // captureLayers and captureDisplay will handle the permission check in the function
+        case CAPTURE_LAYERS:
+        case CAPTURE_DISPLAY:
+        case SET_DISPLAY_BRIGHTNESS:
+        case SET_FRAME_TIMELINE_VSYNC: {
             return OK;
         }
-        case CAPTURE_LAYERS:
-        case CAPTURE_SCREEN:
+
         case ADD_REGION_SAMPLING_LISTENER:
         case REMOVE_REGION_SAMPLING_LISTENER: {
             // codes that require permission check
@@ -4923,7 +4892,8 @@
             }
             return OK;
         }
-        case CAPTURE_SCREEN_BY_ID: {
+        case ADD_TRANSACTION_TRACE_LISTENER:
+        case CAPTURE_DISPLAY_BY_ID: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int uid = ipc->getCallingUid();
             if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -4941,9 +4911,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1036 are currently used for backdoors. The code
+    // Numbers from 1000 to 1038 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 <= 1036) {
+    if (code >= 1000 && code <= 1039) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5086,14 +5056,14 @@
                 mForceFullDamage = n != 0;
                 return NO_ERROR;
             }
-            case 1018: { // Modify Choreographer's phase offset
+            case 1018: { // Modify Choreographer's duration
                 n = data.readInt32();
-                mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
+                mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
-            case 1019: { // Modify SurfaceFlinger's phase offset
+            case 1019: { // Modify SurfaceFlinger's duration
                 n = data.readInt32();
-                mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
+                mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
             case 1020: { // Layer updates interceptor
@@ -5137,19 +5107,19 @@
             }
             case 1025: { // Set layer tracing
                 n = data.readInt32();
+                bool tracingEnabledChanged;
                 if (n) {
                     ALOGD("LayerTracing enabled");
-                    mTracingEnabledChanged = mTracing.enable();
-                    reply->writeInt32(NO_ERROR);
+                    tracingEnabledChanged = mTracing.enable();
+                    if (tracingEnabledChanged) {
+                        schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+                    }
                 } else {
                     ALOGD("LayerTracing disabled");
-                    mTracingEnabledChanged = mTracing.disable();
-                    if (mTracingEnabledChanged) {
-                        reply->writeInt32(mTracing.writeToFile());
-                    } else {
-                        reply->writeInt32(NO_ERROR);
-                    }
+                    tracingEnabledChanged = mTracing.disable();
                 }
+                mTracingEnabledChanged = tracingEnabledChanged;
+                reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
             case 1026: { // Get layer tracing status
@@ -5181,11 +5151,8 @@
                 }
                 return NO_ERROR;
             }
-            // Is VrFlinger active?
-            case 1028: {
-                Mutex::Autolock _l(mStateLock);
-                reply->writeBool(getHwComposer().isUsingVrComposer());
-                return NO_ERROR;
+            case 1028: { // Unused.
+                return NAME_NOT_FOUND;
             }
             // Set buffer size for SF tracing (value in KB)
             case 1029: {
@@ -5262,7 +5229,8 @@
             case 1035: {
                 n = data.readInt32();
                 mDebugDisplayConfigSetByBackdoor = false;
-                if (n >= 0) {
+                const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
+                if (n >= 0 && n < numConfigs) {
                     const auto displayToken = getInternalDisplayToken();
                     status_t result = setActiveConfig(displayToken, n);
                     if (result != NO_ERROR) {
@@ -5284,6 +5252,39 @@
                 }
                 return NO_ERROR;
             }
+            // Inject a hotplug connected event for the primary display. This will deallocate and
+            // reallocate the display state including framebuffers.
+            case 1037: {
+                std::optional<hal::HWDisplayId> hwcId;
+                {
+                    Mutex::Autolock lock(mStateLock);
+                    hwcId = getHwComposer().getInternalHwcDisplayId();
+                }
+                onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+                return NO_ERROR;
+            }
+            // Modify the max number of display frames stored within FrameTimeline
+            case 1038: {
+                n = data.readInt32();
+                if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) {
+                    ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES);
+                    return BAD_VALUE;
+                }
+                if (n == 0) {
+                    // restore to default
+                    mFrameTimeline->reset();
+                    return NO_ERROR;
+                }
+                mFrameTimeline->setMaxDisplayFrames(n);
+                return NO_ERROR;
+            }
+            case 1039: {
+                // The first parameter is the uid
+                n = data.readInt32();
+                const float refreshRateHz = data.readFloat();
+                mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+            }
+                return NO_ERROR;
         }
     }
     return err;
@@ -5316,8 +5317,7 @@
         const auto& min = mRefreshRateConfigs->getMinRefreshRate();
 
         if (current != min) {
-            const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
-            const bool timerExpired = kernelTimerEnabled && expired;
+            const bool timerExpired = mKernelIdleTimerEnabled && expired;
 
             if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
                 mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
@@ -5327,6 +5327,35 @@
     }));
 }
 
+void SurfaceFlinger::toggleKernelIdleTimer() {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    // If the support for kernel idle timer is disabled in SF code, don't do anything.
+    if (!mSupportKernelIdleTimer) {
+        return;
+    }
+    const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
+
+    switch (action) {
+        case KernelIdleTimerAction::TurnOff:
+            if (mKernelIdleTimerEnabled) {
+                ATRACE_INT("KernelIdleTimer", 0);
+                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false");
+                mKernelIdleTimerEnabled = false;
+            }
+            break;
+        case KernelIdleTimerAction::TurnOn:
+            if (!mKernelIdleTimerEnabled) {
+                ATRACE_INT("KernelIdleTimer", 1);
+                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true");
+                mKernelIdleTimerEnabled = true;
+            }
+            break;
+        case KernelIdleTimerAction::NoChange:
+            break;
+    }
+}
+
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
@@ -5340,45 +5369,6 @@
     const int mApi;
 };
 
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
-                                       sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
-                                       Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
-                                       const Rect& sourceCrop, uint32_t reqWidth,
-                                       uint32_t reqHeight, bool useIdentityTransform,
-                                       ui::Rotation rotation, bool captureSecureLayers) {
-    ATRACE_CALL();
-
-    if (!displayToken) return BAD_VALUE;
-
-    auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
-    if (renderAreaRotation == ui::Transform::ROT_INVALID) {
-        ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
-        renderAreaRotation = ui::Transform::ROT_0;
-    }
-
-    sp<DisplayDevice> display;
-    {
-        Mutex::Autolock lock(mStateLock);
-
-        display = getDisplayDeviceLocked(displayToken);
-        if (!display) return NAME_NOT_FOUND;
-
-        // set the requested width/height to the logical display viewport size
-        // by default
-        if (reqWidth == 0 || reqHeight == 0) {
-            reqWidth = uint32_t(display->getViewport().width());
-            reqHeight = uint32_t(display->getViewport().height());
-        }
-    }
-
-    DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
-                                 renderAreaRotation, captureSecureLayers);
-    auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
-                                    std::placeholders::_1);
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
-                               useIdentityTransform, outCapturedSecureLayers);
-}
-
 static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
     switch (colorMode) {
         case ColorMode::DISPLAY_P3:
@@ -5391,6 +5381,24 @@
     }
 }
 
+static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+        return OK;
+    }
+
+    // If the caller doesn't have the correct permissions but is only attempting to screenshot
+    // itself, we allow it to continue.
+    if (captureArgs.uid == uid) {
+        return OK;
+    }
+
+    ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
+    return PERMISSION_DENIED;
+}
+
 status_t SurfaceFlinger::setSchedFifo(bool enabled) {
     static constexpr int kFifoPriority = 2;
     static constexpr int kOtherPriority = 0;
@@ -5412,8 +5420,8 @@
 }
 
 sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
-    const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
-    if (displayToken) {
+    if (const sp<IBinder> displayToken =
+                getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
         return getDisplayDeviceLocked(displayToken);
     }
     // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
@@ -5430,154 +5438,113 @@
     return nullptr;
 }
 
-status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
-                                       sp<GraphicBuffer>* outBuffer) {
-    sp<DisplayDevice> display;
-    uint32_t width;
-    uint32_t height;
-    ui::Transform::RotationFlags captureOrientation;
+status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
+                                        const sp<IScreenCaptureListener>& captureListener) {
+    ATRACE_CALL();
+
+    status_t validate = validateScreenshotPermissions(args);
+    if (validate != OK) {
+        return validate;
+    }
+
+    if (!args.displayToken) return BAD_VALUE;
+
+    wp<DisplayDevice> displayWeak;
+    ui::LayerStack layerStack;
+    ui::Size reqSize(args.width, args.height);
+    ui::Dataspace dataspace;
     {
         Mutex::Autolock lock(mStateLock);
-        display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+        sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
+        if (!display) return NAME_NOT_FOUND;
+        displayWeak = display;
+        layerStack = display->getLayerStack();
+
+        // set the requested width/height to the logical display layer stack rect size by default
+        if (args.width == 0 || args.height == 0) {
+            reqSize = display->getLayerStackSpaceRect().getSize();
+        }
+
+        // The dataspace is depended on the color mode of display, that could use non-native mode
+        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
+        // and failed if display is not in native mode. This provide a way to force using native
+        // colors when capture.
+        dataspace = args.dataspace;
+        if (dataspace == ui::Dataspace::UNKNOWN) {
+            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+            dataspace = pickDataspaceFromColorMode(colorMode);
+        }
+    }
+
+    RenderAreaFuture renderAreaFuture = promise::defer([=] {
+        return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
+                                         args.useIdentityTransform, args.captureSecureLayers);
+    });
+
+    auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+        traverseLayersInLayerStack(layerStack, args.uid, visitor);
+    };
+
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+                               args.pixelFormat, args.allowProtected, captureListener);
+}
+
+status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
+                                        const sp<IScreenCaptureListener>& captureListener) {
+    ui::LayerStack layerStack;
+    wp<DisplayDevice> displayWeak;
+    ui::Size size;
+    ui::Dataspace dataspace;
+    {
+        Mutex::Autolock lock(mStateLock);
+        sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
         if (!display) {
             return NAME_NOT_FOUND;
         }
+        layerStack = display->getLayerStack();
+        displayWeak = display;
 
-        width = uint32_t(display->getViewport().width());
-        height = uint32_t(display->getViewport().height());
+        size = display->getLayerStackSpaceRect().getSize();
 
-        const auto orientation = display->getOrientation();
-        captureOrientation = ui::Transform::toRotationFlags(orientation);
-
-        switch (captureOrientation) {
-            case ui::Transform::ROT_90:
-                captureOrientation = ui::Transform::ROT_270;
-                break;
-
-            case ui::Transform::ROT_270:
-                captureOrientation = ui::Transform::ROT_90;
-                break;
-
-            case ui::Transform::ROT_INVALID:
-                ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
-                captureOrientation = ui::Transform::ROT_0;
-                break;
-
-            default:
-                break;
-        }
-        *outDataspace =
+        dataspace =
                 pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
     }
 
-    DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
-                                 false /* captureSecureLayers */);
+    RenderAreaFuture renderAreaFuture = promise::defer([=] {
+        return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
+                                         false /* useIdentityTransform */,
+                                         false /* captureSecureLayers */);
+    });
 
-    auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
-                                    std::placeholders::_1);
-    bool ignored = false;
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
-                               false /* useIdentityTransform */,
-                               ignored /* outCapturedSecureLayers */);
-}
-
-status_t SurfaceFlinger::captureLayers(
-        const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
-        const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-        const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
-        float frameScale, bool childrenOnly) {
-    ATRACE_CALL();
-
-    class LayerRenderArea : public RenderArea {
-    public:
-        LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
-                        int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
-                        bool childrenOnly, const Rect& displayViewport)
-              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
-                mLayer(layer),
-                mCrop(crop),
-                mNeedsFiltering(false),
-                mFlinger(flinger),
-                mChildrenOnly(childrenOnly) {}
-        const ui::Transform& getTransform() const override { return mTransform; }
-        Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
-        int getHeight() const override {
-            return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
-        }
-        int getWidth() const override {
-            return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
-        }
-        bool isSecure() const override { return false; }
-        bool needsFiltering() const override { return mNeedsFiltering; }
-        sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
-        Rect getSourceCrop() const override {
-            if (mCrop.isEmpty()) {
-                return getBounds();
-            } else {
-                return mCrop;
-            }
-        }
-        class ReparentForDrawing {
-        public:
-            const sp<Layer>& oldParent;
-            const sp<Layer>& newParent;
-
-            ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
-                               const Rect& drawingBounds)
-                  : oldParent(oldParent), newParent(newParent) {
-                // Compute and cache the bounds for the new parent layer.
-                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
-                                         0.f /* shadowRadius */);
-                oldParent->setChildrenDrawingParent(newParent);
-            }
-            ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
-        };
-
-        void render(std::function<void()> drawLayers) override {
-            const Rect sourceCrop = getSourceCrop();
-            // no need to check rotation because there is none
-            mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
-                sourceCrop.height() != getReqHeight();
-
-            if (!mChildrenOnly) {
-                mTransform = mLayer->getTransform().inverse();
-                drawLayers();
-            } else {
-                uint32_t w = static_cast<uint32_t>(getWidth());
-                uint32_t h = static_cast<uint32_t>(getHeight());
-                // In the "childrenOnly" case we reparent the children to a screenshot
-                // layer which has no properties set and which does not draw.
-                sp<ContainerLayer> screenshotParentLayer =
-                        mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
-                                                                     "Screenshot Parent"s, w, h, 0,
-                                                                     LayerMetadata()});
-
-                ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
-                drawLayers();
-            }
-        }
-
-    private:
-        const sp<Layer> mLayer;
-        const Rect mCrop;
-
-        ui::Transform mTransform;
-        bool mNeedsFiltering;
-
-        SurfaceFlinger* mFlinger;
-        const bool mChildrenOnly;
+    auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+        traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
     };
 
-    int reqWidth = 0;
-    int reqHeight = 0;
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+                               ui::PixelFormat::RGBA_8888, false /* allowProtected */,
+                               captureListener);
+}
+
+status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
+                                       const sp<IScreenCaptureListener>& captureListener) {
+    ATRACE_CALL();
+
+    status_t validate = validateScreenshotPermissions(args);
+    if (validate != OK) {
+        return validate;
+    }
+
+    ui::Size reqSize;
     sp<Layer> parent;
-    Rect crop(sourceCrop);
+    Rect crop(args.sourceCrop);
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-    Rect displayViewport;
+    Rect layerStackSpaceRect;
+    ui::Dataspace dataspace;
+    bool captureSecureLayers;
     {
         Mutex::Autolock lock(mStateLock);
 
-        parent = fromHandleLocked(layerHandleBinder).promote();
+        parent = fromHandleLocked(args.layerHandle).promote();
         if (parent == nullptr || parent->isRemovedFromCurrentState()) {
             ALOGE("captureLayers called with an invalid or removed parent");
             return NAME_NOT_FOUND;
@@ -5591,25 +5558,24 @@
         }
 
         Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
-        if (sourceCrop.width() <= 0) {
+        if (args.sourceCrop.width() <= 0) {
             crop.left = 0;
             crop.right = parentSourceBounds.getWidth();
         }
 
-        if (sourceCrop.height() <= 0) {
+        if (args.sourceCrop.height() <= 0) {
             crop.top = 0;
             crop.bottom = parentSourceBounds.getHeight();
         }
 
-        if (crop.isEmpty() || frameScale <= 0.0f) {
+        if (crop.isEmpty() || args.frameScale <= 0.0f) {
             // Error out if the layer has no source bounds (i.e. they are boundless) and a source
             // crop was not specified, or an invalid frame scale was provided.
             return BAD_VALUE;
         }
-        reqWidth = crop.width() * frameScale;
-        reqHeight = crop.height() * frameScale;
+        reqSize = ui::Size(crop.width() * args.frameScale, crop.height() * args.frameScale);
 
-        for (const auto& handle : excludeHandles) {
+        for (const auto& handle : args.excludeHandles) {
             sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
             if (excludeLayer != nullptr) {
                 excludeLayers.emplace(excludeLayer);
@@ -5624,25 +5590,43 @@
             return NAME_NOT_FOUND;
         }
 
-        displayViewport = display->getViewport();
+        layerStackSpaceRect = display->getLayerStackSpaceRect();
+
+        // The dataspace is depended on the color mode of display, that could use non-native mode
+        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
+        // and failed if display is not in native mode. This provide a way to force using native
+        // colors when capture.
+        dataspace = args.dataspace;
+        if (dataspace == ui::Dataspace::UNKNOWN) {
+            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+            dataspace = pickDataspaceFromColorMode(colorMode);
+        }
+
+        captureSecureLayers = args.captureSecureLayers && display->isSecure();
     } // mStateLock
 
     // really small crop or frameScale
-    if (reqWidth <= 0) {
-        reqWidth = 1;
+    if (reqSize.width <= 0) {
+        reqSize.width = 1;
     }
-    if (reqHeight <= 0) {
-        reqHeight = 1;
+    if (reqSize.height <= 0) {
+        reqSize.height = 1;
     }
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
-                               displayViewport);
-    auto traverseLayers = [parent, childrenOnly,
-                           &excludeLayers](const LayerVector::Visitor& visitor) {
+    bool childrenOnly = args.childrenOnly;
+    RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
+        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
+                                                 childrenOnly, layerStackSpaceRect,
+                                                 captureSecureLayers);
+    });
+
+    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
             if (!layer->isVisible()) {
                 return;
-            } else if (childrenOnly && layer == parent.get()) {
+            } else if (args.childrenOnly && layer == parent.get()) {
+                return;
+            } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
                 return;
             }
 
@@ -5658,85 +5642,121 @@
         });
     };
 
-    bool outCapturedSecureLayers = false;
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
-                               outCapturedSecureLayers);
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+                               args.pixelFormat, args.allowProtected, captureListener);
 }
 
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
                                              TraverseLayersFunction traverseLayers,
-                                             sp<GraphicBuffer>* outBuffer,
-                                             const ui::PixelFormat reqPixelFormat,
-                                             bool useIdentityTransform,
-                                             bool& outCapturedSecureLayers) {
+                                             ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
+                                             const bool allowProtected,
+                                             const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
-    // TODO(b/116112787) Make buffer usage a parameter.
-    const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
-            GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-    *outBuffer =
-            getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
-                                             static_cast<android_pixel_format>(reqPixelFormat), 1,
-                                             usage, "screenshot");
+    // Loop over all visible layers to see whether there's any protected layer. A protected layer is
+    // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
+    // A protected layer has no implication on whether it's secure, which is explicitly set by
+    // application to avoid being screenshot or drawn via unsecure display.
+    const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+    bool hasProtectedLayer = false;
+    if (allowProtected && supportsProtected) {
+        traverseLayers([&](Layer* layer) {
+            hasProtectedLayer = hasProtectedLayer || (layer->isVisible() && layer->isProtected());
+        });
+    }
 
-    return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
-                               false /* regionSampling */, outCapturedSecureLayers);
+    const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+            GRALLOC_USAGE_HW_TEXTURE |
+            (hasProtectedLayer && allowProtected && supportsProtected
+                     ? GRALLOC_USAGE_PROTECTED
+                     : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+    sp<GraphicBuffer> buffer =
+            getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+                                             static_cast<android_pixel_format>(reqPixelFormat),
+                                             1 /* layerCount */, usage, "screenshot");
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+                               false /* regionSampling */, captureListener);
 }
 
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
                                              TraverseLayersFunction traverseLayers,
-                                             const sp<GraphicBuffer>& buffer,
-                                             bool useIdentityTransform, bool regionSampling,
-                                             bool& outCapturedSecureLayers) {
+                                             sp<GraphicBuffer>& buffer, const bool regionSampling,
+                                             const sp<IScreenCaptureListener>& captureListener) {
+    ATRACE_CALL();
+
+    if (captureListener == nullptr) {
+        ALOGE("capture screen must provide a capture listener callback");
+        return BAD_VALUE;
+    }
+
     const int uid = IPCThreadState::self()->getCallingUid();
     const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
 
-    status_t result;
-    int syncFd;
+    static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
+        if (mRefreshPending) {
+            ALOGW("Skipping screenshot for now");
+            captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
+                                captureListener);
+            return;
+        }
+        ScreenCaptureResults captureResults;
+        std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+        if (!renderArea) {
+            ALOGW("Skipping screen capture because of invalid render area.");
+            captureResults.result = NO_MEMORY;
+            captureListener->onScreenCaptureComplete(captureResults);
+            return;
+        }
 
-    do {
-        std::tie(result, syncFd) =
-                schedule([&] {
-                    if (mRefreshPending) {
-                        ATRACE_NAME("Skipping screenshot for now");
-                        return std::make_pair(EAGAIN, -1);
-                    }
+        status_t result = NO_ERROR;
+        int syncFd = -1;
+        renderArea->render([&] {
+            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, &syncFd,
+                                            regionSampling, captureResults);
+        });
 
-                    status_t result = NO_ERROR;
-                    int fd = -1;
+        if (result == NO_ERROR) {
+            sync_wait(syncFd, -1);
+            close(syncFd);
+        }
+        captureResults.result = result;
+        captureListener->onScreenCaptureComplete(captureResults);
+    }));
 
-                    Mutex::Autolock lock(mStateLock);
-                    renderArea.render([&] {
-                        result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
-                                                         useIdentityTransform, forSystem, &fd,
-                                                         regionSampling, outCapturedSecureLayers);
-                    });
-
-                    return std::make_pair(result, fd);
-                }).get();
-    } while (result == EAGAIN);
-
-    if (result == NO_ERROR) {
-        sync_wait(syncFd, -1);
-        close(syncFd);
-    }
-
-    return result;
+    return NO_ERROR;
 }
 
-void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
-                                            TraverseLayersFunction traverseLayers,
-                                            const sp<GraphicBuffer>& buffer,
-                                            bool useIdentityTransform, bool regionSampling,
-                                            int* outSyncFd) {
+status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
+                                                TraverseLayersFunction traverseLayers,
+                                                const sp<GraphicBuffer>& buffer, bool forSystem,
+                                                int* outSyncFd, bool regionSampling,
+                                                ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
+    traverseLayers([&](Layer* layer) {
+        captureResults.capturedSecureLayers =
+                captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
+    });
+
+    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
+    // the impetus on WindowManager to not persist them.
+    if (captureResults.capturedSecureLayers && !forSystem) {
+        ALOGW("FB is protected: PERMISSION_DENIED");
+        return PERMISSION_DENIED;
+    }
+
+    captureResults.buffer = buffer;
+    captureResults.capturedDataspace = renderArea.getReqDataSpace();
+
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
     const auto sourceCrop = renderArea.getSourceCrop();
     const auto transform = renderArea.getTransform();
     const auto rotation = renderArea.getRotationFlags();
-    const auto& displayViewport = renderArea.getDisplayViewport();
+    const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
 
     renderengine::DisplaySettings clientCompositionDisplay;
     std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
@@ -5764,17 +5784,15 @@
     std::vector<Layer*> renderedLayers;
     Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
-        const bool supportProtectedContent = false;
         Region clip(renderArea.getBounds());
         compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
                 clip,
-                useIdentityTransform,
                 layer->needsFilteringForScreenshots(display.get(), transform) ||
                         renderArea.needsFiltering(),
                 renderArea.isSecure(),
-                supportProtectedContent,
+                useProtected,
                 clearRegion,
-                displayViewport,
+                layerStackSpaceRect,
                 clientCompositionDisplay.outputDataspace,
                 true,  /* realContentIsVisible */
                 false, /* clearContent */
@@ -5810,7 +5828,7 @@
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
-    getRenderEngine().useProtectedContext(false);
+    getRenderEngine().useProtectedContext(useProtected);
     getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
                                  /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
 
@@ -5822,30 +5840,9 @@
             layer->onLayerDisplayed(releaseFence);
         }
     }
-}
+    // Always switch back to unprotected context.
+    getRenderEngine().useProtectedContext(false);
 
-status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
-                                                 TraverseLayersFunction traverseLayers,
-                                                 const sp<GraphicBuffer>& buffer,
-                                                 bool useIdentityTransform, bool forSystem,
-                                                 int* outSyncFd, bool regionSampling,
-                                                 bool& outCapturedSecureLayers) {
-    ATRACE_CALL();
-
-    traverseLayers([&](Layer* layer) {
-        outCapturedSecureLayers =
-                outCapturedSecureLayers || (layer->isVisible() && layer->isSecure());
-    });
-
-    // We allow the system server to take screenshots of secure layers for
-    // use in situations like the Screen-rotation animation and place
-    // the impetus on WindowManager to not persist them.
-    if (outCapturedSecureLayers && !forSystem) {
-        ALOGW("FB is protected: PERMISSION_DENIED");
-        return PERMISSION_DENIED;
-    }
-    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling,
-                           outSyncFd);
     return NO_ERROR;
 }
 
@@ -5871,22 +5868,25 @@
     layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
 }
 
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
-                                             const LayerVector::Visitor& visitor) {
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
+                                                const LayerVector::Visitor& visitor) {
     // We loop through the first level of layers without traversing,
     // as we need to determine which layers belong to the requested display.
     for (const auto& layer : mDrawingState.layersSortedByZ) {
-        if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+        if (!layer->belongsToDisplay(layerStack)) {
             continue;
         }
         // relative layers are traversed in Layer::traverseInZOrder
         layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-            if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+            if (layer->getPrimaryDisplayOnly()) {
                 return;
             }
             if (!layer->isVisible()) {
                 return;
             }
+            if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
+                return;
+            }
             visitor(layer);
         });
     }
@@ -5906,16 +5906,14 @@
         // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
         // it should go thru setDesiredActiveConfig, similar to primary display.
         ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
-        const auto displayId = display->getId();
-        LOG_ALWAYS_FATAL_IF(!displayId);
+        const auto displayId = display->getPhysicalId();
 
         hal::VsyncPeriodChangeConstraints constraints;
         constraints.desiredTimeNanos = systemTime();
         constraints.seamlessRequired = false;
 
         hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
-        if (getHwComposer().setActiveConfigWithConstraints(*displayId,
-                                                           policy->defaultConfig.value(),
+        if (getHwComposer().setActiveConfigWithConstraints(displayId, policy->defaultConfig.value(),
                                                            constraints, &timeline) < 0) {
             return BAD_VALUE;
         }
@@ -5925,10 +5923,10 @@
 
         display->setActiveConfig(policy->defaultConfig);
         const nsecs_t vsyncPeriod = getHwComposer()
-                                            .getConfigs(*displayId)[policy->defaultConfig.value()]
+                                            .getConfigs(displayId)[policy->defaultConfig.value()]
                                             ->getVsyncPeriod();
-        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    policy->defaultConfig, vsyncPeriod);
+        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
+                                                     policy->defaultConfig, vsyncPeriod);
         return NO_ERROR;
     }
 
@@ -5954,14 +5952,15 @@
           currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
           currentPolicy.appRequestRange.max);
 
-    // TODO(b/140204874): This hack triggers a notification that something has changed, so
-    // that listeners that care about a change in allowed configs can get the notification.
-    // Giving current ActiveConfig so that most other listeners would just drop the event
+    // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
+    // be depending in this callback.
     const nsecs_t vsyncPeriod =
             mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
                     .getVsyncPeriod();
-    mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                display->getActiveConfig(), vsyncPeriod);
+    const auto physicalId = display->getPhysicalId();
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
+                                              display->getActiveConfig(), vsyncPeriod);
+    toggleKernelIdleTimer();
 
     auto configId = mScheduler->getPreferredConfigId();
     auto& preferredRefreshRate = configId
@@ -5984,12 +5983,10 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                      int32_t defaultConfig,
-                                                      float primaryRefreshRateMin,
-                                                      float primaryRefreshRateMax,
-                                                      float appRequestRefreshRateMin,
-                                                      float appRequestRefreshRateMax) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(
+        const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
+        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+        float appRequestRefreshRateMax) {
     ATRACE_CALL();
 
     if (!displayToken) {
@@ -6008,6 +6005,7 @@
         } else {
             using Policy = scheduler::RefreshRateConfigs::Policy;
             const Policy policy{HwcConfigIndexType(defaultConfig),
+                                allowGroupSwitching,
                                 {primaryRefreshRateMin, primaryRefreshRateMax},
                                 {appRequestRefreshRateMin, appRequestRefreshRateMax}};
             constexpr bool kOverridePolicy = false;
@@ -6019,12 +6017,10 @@
     return future.get();
 }
 
-status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                      int32_t* outDefaultConfig,
-                                                      float* outPrimaryRefreshRateMin,
-                                                      float* outPrimaryRefreshRateMax,
-                                                      float* outAppRequestRefreshRateMin,
-                                                      float* outAppRequestRefreshRateMax) {
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(
+        const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+        float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
+        float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
     ATRACE_CALL();
 
     if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
@@ -6042,6 +6038,7 @@
         scheduler::RefreshRateConfigs::Policy policy =
                 mRefreshRateConfigs->getDisplayManagerPolicy();
         *outDefaultConfig = policy.defaultConfig.value();
+        *outAllowGroupSwitching = policy.allowGroupSwitching;
         *outPrimaryRefreshRateMin = policy.primaryRange.min;
         *outPrimaryRefreshRateMax = policy.primaryRange.max;
         *outAppRequestRefreshRateMin = policy.appRequestRange.min;
@@ -6050,11 +6047,10 @@
     } else if (display->isVirtual()) {
         return INVALID_OPERATION;
     } else {
-        const auto displayId = display->getId();
-        LOG_FATAL_IF(!displayId);
-
-        *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
-        auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+        const auto displayId = display->getPhysicalId();
+        *outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId);
+        *outAllowGroupSwitching = false;
+        auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod();
         *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
         *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
         *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
@@ -6063,10 +6059,6 @@
     }
 }
 
-void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
-    mFlinger->setInputWindowsFinished();
-}
-
 wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
     Mutex::Autolock _l(mStateLock);
     return fromHandleLocked(handle);
@@ -6137,9 +6129,6 @@
     // on the work to remove the table in that bug rather than adding more to
     // it.
     static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
-            // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
-            // supported, and exposed via the
-            // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
             {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
             {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
     };
@@ -6156,6 +6145,11 @@
         Mutex::Autolock lock(mStateLock);
         if (authenticateSurfaceTextureLocked(surface)) {
             sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+            if (layer == nullptr) {
+                ALOGE("Attempt to set frame rate on a layer that no longer exists");
+                return BAD_VALUE;
+            }
+
             if (layer->setFrameRate(
                         Layer::FrameRate(frameRate,
                                          Layer::FrameRate::convertCompatibility(compatibility)))) {
@@ -6186,8 +6180,8 @@
             // This is a little racy, but not in a way that hurts anything. As we grab the
             // defaultConfig from the display manager policy, we could be setting a new display
             // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
-            // matter for the override policy though, since we set allowGroupSwitching to true, so
-            // it's not a problem.
+            // matter for the override policy though, since we set allowGroupSwitching to
+            // true, so it's not a problem.
             scheduler::RefreshRateConfigs::Policy overridePolicy;
             overridePolicy.defaultConfig =
                     mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
@@ -6239,11 +6233,29 @@
     }));
 }
 
+status_t SurfaceFlinger::setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+                                               int64_t frameTimelineVsyncId) {
+    Mutex::Autolock lock(mStateLock);
+    if (!authenticateSurfaceTextureLocked(surface)) {
+        ALOGE("Attempt to set frame timeline vsync on an unrecognized IGraphicBufferProducer");
+        return BAD_VALUE;
+    }
+
+    sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+    if (layer == nullptr) {
+        ALOGE("Attempt to set frame timeline vsync on a layer that no longer exists");
+        return BAD_VALUE;
+    }
+
+    layer->setFrameTimelineVsyncForBuffer(frameTimelineVsyncId);
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
     static_cast<void>(schedule([=] {
         std::unique_ptr<RefreshRateOverlay> overlay;
         if (enable) {
-            overlay = std::make_unique<RefreshRateOverlay>(*this);
+            overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
         }
 
         {
@@ -6262,6 +6274,17 @@
     }));
 }
 
+status_t SurfaceFlinger::addTransactionTraceListener(
+        const sp<gui::ITransactionTraceListener>& listener) {
+    if (!listener) {
+        return BAD_VALUE;
+    }
+
+    mInterceptor->addTransactionTraceListener(listener);
+
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d7acce1..a821d44 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,7 +33,6 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/OccupancyTracker.h>
-#include <input/ISetInputWindowsListener.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
 #include <renderengine/LayerSettings.h>
@@ -53,13 +52,14 @@
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
 #include "LayerVector.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
+#include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceTracing.h"
 #include "TracedOrdinal.h"
@@ -89,15 +89,24 @@
 class Client;
 class EventThread;
 class HWComposer;
+struct SetInputWindowsListener;
 class IGraphicBufferProducer;
-class IInputFlinger;
 class Layer;
 class MessageBase;
 class RefreshRateOverlay;
 class RegionSamplingThread;
+class RenderArea;
 class TimeStats;
 class FrameTracer;
 
+namespace frametimeline {
+class FrameTimeline;
+}
+
+namespace os {
+    class IInputFlinger;
+}
+
 namespace compositionengine {
 class DisplaySurface;
 class OutputLayer;
@@ -109,10 +118,6 @@
 class RenderEngine;
 } // namespace renderengine
 
-namespace dvr {
-class VrFlinger;
-} // namespace dvr
-
 enum {
     eTransactionNeeded = 0x01,
     eTraversalNeeded = 0x02,
@@ -179,8 +184,15 @@
                        private HWC2::ComposerCallback,
                        private ISchedulerCallback {
 public:
-    SurfaceFlingerBE& getBE() { return mBE; }
-    const SurfaceFlingerBE& getBE() const { return mBE; }
+    struct SkipInitializationTag {};
+
+    SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
+    explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
+
+    // set main thread scheduling policy
+    static status_t setSchedFifo(bool enabled) ANDROID_API;
+
+    static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
 
     // This is the phase offset in nanoseconds of the software vsync event
     // relative to the vsync event reported by HWComposer.  The software vsync
@@ -208,7 +220,7 @@
     // If fences from sync Framework are supported.
     static bool hasSyncFramework;
 
-    // The offset in nanoseconds to use when DispSync timestamps present fence
+    // The offset in nanoseconds to use when VsyncController timestamps present fence
     // signaling time.
     static int64_t dispSyncPresentTimeOffset;
 
@@ -259,17 +271,7 @@
     // overhead that is caused by reading from sysprop.
     static bool useFrameRateApi;
 
-    // set main thread scheduling policy
-    static status_t setSchedFifo(bool enabled) ANDROID_API;
-
-    static char const* getServiceName() ANDROID_API {
-        return "SurfaceFlinger";
-    }
-
-    struct SkipInitializationTag {};
     static constexpr SkipInitializationTag SkipInitialization;
-    SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
-    explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
 
     // must be called before clients can connect
     void init() ANDROID_API;
@@ -277,6 +279,9 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
+    SurfaceFlingerBE& getBE() { return mBE; }
+    const SurfaceFlingerBE& getBE() const { return mBE; }
+
     // Schedule an asynchronous or synchronous task on the main thread.
     template <typename F, typename T = std::invoke_result_t<F>>
     [[nodiscard]] std::future<T> schedule(F&&);
@@ -296,18 +301,10 @@
     // utility function to delete a texture on the main thread
     void deleteTextureAsync(uint32_t texture);
 
-    // enable/disable h/w composer event
-    // TODO: this should be made accessible only to EventThread
-    void setPrimaryVsyncEnabled(bool enabled);
-
-    // main thread function to enable/disable h/w composer event
-    void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
-    void setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled);
-
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
-    void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime);
+    void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
 
     renderengine::RenderEngine& getRenderEngine() const;
 
@@ -336,6 +333,25 @@
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
+    void setInputWindowsFinished();
+
+protected:
+    // We're reference counted, never destroy SurfaceFlinger directly
+    virtual ~SurfaceFlinger();
+
+    virtual uint32_t setClientStateLocked(
+            int64_t frameTimelineVsyncId, const ComposerState& composerState,
+            int64_t desiredPresentTime, int64_t postTime, bool privileged,
+            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+            REQUIRES(mStateLock);
+    virtual void commitTransactionLocked();
+
+    // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
+    // root layers on a particular display in layer-coordinate space. The
+    // layers (and effectively their children) will be clipped against this
+    // rectangle. The base behavior is to clip to the visible region of the
+    // display.
+    virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
 
 private:
     friend class BufferLayer;
@@ -352,21 +368,18 @@
     friend class TestableSurfaceFlinger;
     friend class TransactionApplicationTest;
 
+    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+    using VsyncModulator = scheduler::VsyncModulator;
+    using TransactionSchedule = scheduler::TransactionSchedule;
+    using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+    using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
+    using DumpArgs = Vector<String16>;
+    using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
     // This value is specified in number of frames.  Log frame stats at most
     // every half hour.
     enum { LOG_FRAME_STATS_PERIOD =  30*60*60 };
 
-    static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
-
-protected:
-    // We're reference counted, never destroy SurfaceFlinger directly
-    virtual ~SurfaceFlinger();
-
-private:
-    /* ------------------------------------------------------------------------
-     * Internal data structures
-     */
-
     class State {
     public:
         explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
@@ -398,29 +411,119 @@
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
 
-    /* ------------------------------------------------------------------------
-     * IBinder interface
-     */
+    struct ActiveConfigInfo {
+        HwcConfigIndexType configId;
+        Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
+
+        bool operator!=(const ActiveConfigInfo& other) const {
+            return configId != other.configId || event != other.event;
+        }
+    };
+
+    enum class BootStage {
+        BOOTLOADER,
+        BOOTANIMATION,
+        FINISHED,
+    };
+
+    struct HotplugEvent {
+        hal::HWDisplayId hwcDisplayId;
+        hal::Connection connection = hal::Connection::INVALID;
+    };
+
+    struct TransactionState {
+        TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
+                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                         int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
+                         int originUid, uint64_t transactionId)
+              : frameTimelineVsyncId(frameTimelineVsyncId),
+                states(composerStates),
+                displays(displayStates),
+                flags(transactionFlags),
+                desiredPresentTime(desiredPresentTime),
+                buffer(uncacheBuffer),
+                postTime(postTime),
+                privileged(privileged),
+                hasListenerCallbacks(hasListenerCallbacks),
+                listenerCallbacks(listenerCallbacks),
+                originPid(originPid),
+                originUid(originUid),
+                id(transactionId) {}
+
+        int64_t frameTimelineVsyncId;
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags;
+        const int64_t desiredPresentTime;
+        client_cache_t buffer;
+        const int64_t postTime;
+        bool privileged;
+        bool hasListenerCallbacks;
+        std::vector<ListenerCallbacks> listenerCallbacks;
+        int originPid;
+        int originUid;
+        uint64_t id;
+    };
+
+    template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+    static Dumper dumper(F&& dump) {
+        using namespace std::placeholders;
+        return std::bind(std::forward<F>(dump), _3);
+    }
+
+    template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+    Dumper dumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _3);
+    }
+
+    template <typename F>
+    Dumper argsDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _3);
+    }
+
+    template <typename F>
+    Dumper protoDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _2, _3);
+    }
+
+    template <typename... Args,
+              typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
+    void modulateVsync(Handler handler, Args... args) {
+        if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+            const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+            setVsyncConfig(*config, vsyncPeriod);
+        }
+    }
+
+    static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+    // Maximum allowed number of display frames that can be set through backdoor
+    static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
+
+    // Implements IBinder.
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
     status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
     bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
             EXCLUDES(mStateLock);
 
-    /* ------------------------------------------------------------------------
-     * ISurfaceComposer interface
-     */
+    // Implements ISurfaceComposer
     sp<ISurfaceComposerClient> createConnection() override;
     sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
     void destroyDisplay(const sp<IBinder>& displayToken) override;
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
-    void setTransactionState(const Vector<ComposerState>& state,
-                             const Vector<DisplayState>& displays, uint32_t flags,
-                             const sp<IBinder>& applyToken,
-                             const InputWindowCommands& inputWindowCommands,
-                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                             bool hasListenerCallbacks,
-                             const std::vector<ListenerCallbacks>& listenerCallbacks) override;
+    status_t setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+                                 const Vector<DisplayState>& displays, uint32_t flags,
+                                 const sp<IBinder>& applyToken,
+                                 const InputWindowCommands& inputWindowCommands,
+                                 int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                                 bool hasListenerCallbacks,
+                                 const std::vector<ListenerCallbacks>& listenerCallbacks,
+                                 uint64_t transactionId) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -429,19 +532,12 @@
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
             ISurfaceComposer::ConfigChanged configChanged =
                     ISurfaceComposer::eConfigChangedSuppress) override;
-    status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
-                           bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
-                           ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                           uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                           ui::Rotation rotation, bool captureSecureLayers) override;
-    status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
-                           sp<GraphicBuffer>* outBuffer) override;
-    status_t captureLayers(
-            const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
-            const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
-            const Rect& sourceCrop,
-            const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& exclude,
-            float frameScale, bool childrenOnly) override;
+    status_t captureDisplay(const DisplayCaptureArgs& args,
+                            const sp<IScreenCaptureListener>& captureListener) override;
+    status_t captureDisplay(uint64_t displayOrLayerStack,
+                            const sp<IScreenCaptureListener>& captureListener) override;
+    status_t captureLayers(const LayerCaptureArgs& args,
+                           const sp<IScreenCaptureListener>& captureListener) override;
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
@@ -466,7 +562,7 @@
                                 HdrCapabilities* outCapabilities) const override;
     status_t enableVSyncInjections(bool enable) override;
     status_t injectVSync(nsecs_t when) override;
-    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const override;
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
     status_t getColorManagement(bool* outGetColorManagement) const override;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
                                       ui::Dataspace* outWideColorGamutDataspace,
@@ -487,11 +583,12 @@
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
     status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
-                                          float primaryRefreshRateMin, float primaryRefreshRateMax,
+                                          bool allowGroupSwitching, float primaryRefreshRateMin,
+                                          float primaryRefreshRateMax,
                                           float appRequestRefreshRateMin,
                                           float appRequestRefreshRateMax) override;
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                          int32_t* outDefaultConfig,
+                                          int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
                                           float* outPrimaryRefreshRateMin,
                                           float* outPrimaryRefreshRateMax,
                                           float* outAppRequestRefreshRateMin,
@@ -505,17 +602,20 @@
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility) override;
     status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
-    /* ------------------------------------------------------------------------
-     * DeathRecipient interface
-     */
+
+    status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+                                   int64_t frameTimelineVsyncId) override;
+
+    status_t addTransactionTraceListener(
+            const sp<gui::ITransactionTraceListener>& listener) override;
+
+    // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
-    /* ------------------------------------------------------------------------
-     * RefBase interface
-     */
+    // Implements RefBase.
     void onFirstRef() override;
 
-    /* ------------------------------------------------------------------------
+    /*
      * HWC2::ComposerCallback / HWComposer::EventHandler interface
      */
     void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp,
@@ -528,15 +628,29 @@
             const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
     void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
 
-    /* ------------------------------------------------------------------------
+    /*
      * ISchedulerCallback
      */
+
+    // Toggles hardware VSYNC by calling into HWC.
+    void setVsyncEnabled(bool) override;
+    // Initiates a refresh rate change to be applied on invalidate.
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
-    // force full composition on all displays without resetting the scheduler idle timer.
+    // Forces full composition on all displays without resetting the scheduler idle timer.
     void repaintEverythingForHWC() override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
-    /* ------------------------------------------------------------------------
+    // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
+    void toggleKernelIdleTimer();
+    // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
+    // make calls to sys prop each time.
+    bool mKernelIdleTimerEnabled = false;
+    // Keeps track of whether the kernel timer is supported on the SF side.
+    bool mSupportKernelIdleTimer = false;
+    // Show spinner with refresh rate overlay
+    bool mRefreshRateOverlaySpinner = false;
+
+    /*
      * Message handling
      */
     // Can only be called from the main thread or with mStateLock held
@@ -545,17 +659,6 @@
     void signalLayerUpdate();
     void signalRefresh();
 
-    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
-    struct ActiveConfigInfo {
-        HwcConfigIndexType configId;
-        Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
-
-        bool operator!=(const ActiveConfigInfo& other) const {
-            return configId != other.configId || event != other.event;
-        }
-    };
-
     // called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
@@ -581,7 +684,7 @@
 
     // Handle the INVALIDATE message queue event, latching new buffers and applying
     // incoming transactions
-    void onMessageInvalidate(nsecs_t expectedVSyncTime);
+    void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -599,9 +702,11 @@
     void updateInputFlinger();
     void updateInputWindowInfo();
     void commitInputWindowCommands() REQUIRES(mStateLock);
-    void setInputWindowsFinished();
     void updateCursorAsync();
-    void initScheduler(DisplayId primaryDisplayId);
+
+    void initScheduler(PhysicalDisplayId primaryDisplayId);
+    void updatePhaseConfiguration(const RefreshRate&);
+    void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -609,16 +714,17 @@
      */
     bool handlePageFlip();
 
-    /* ------------------------------------------------------------------------
+    /*
      * Transactions
      */
-    void applyTransactionState(const Vector<ComposerState>& state,
+    void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
+                               int originPid, int originUid, uint64_t transactionId,
                                bool isMainThread = false) REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
@@ -628,13 +734,13 @@
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
-    // Set the transaction flags, but don't trigger a wakeup! We use this cases where
-    // there are still pending transactions but we know they won't be ready until a frame
+    // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+    // where there are still pending transactions but we know they won't be ready until a frame
     // arrives from a different layer. So we need to ensure we performTransaction from invalidate
     // but there is no need to try and wake up immediately to do it. Rather we rely on
-    // onFrameAvailable to wake us up.
-    uint32_t setTransactionFlagsNoWake(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+    // onFrameAvailable or another layer update to wake us up.
+    void setTraversalNeeded();
+    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
@@ -642,30 +748,14 @@
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
-
-protected:
-    virtual uint32_t setClientStateLocked(
-            const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
-            bool privileged,
-            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
-            REQUIRES(mStateLock);
-    virtual void commitTransactionLocked();
-
-    // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
-    // root layers on a particular display in layer-coordinate space. The
-    // layers (and effectively their children) will be clipped against this
-    // rectangle. The base behavior is to clip to the visible region of the
-    // display.
-    virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
-
-private:
-    /* ------------------------------------------------------------------------
+    /*
      * Layer management
      */
     status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
                          PixelFormat format, uint32_t flags, LayerMetadata metadata,
                          sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
-                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+                         const sp<IBinder>& parentHandle, int32_t* outLayerId,
+                         const sp<Layer>& parentLayer = nullptr,
                          uint32_t* outTransformHint = nullptr);
 
     status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
@@ -686,7 +776,7 @@
                                   sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
     status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
-                         sp<IBinder>* outHandle);
+                         sp<IBinder>* outHandle, int32_t* outLayerId);
 
     std::string getUniqueLayerName(const char* name);
 
@@ -705,47 +795,31 @@
     // Traverse through all the layers and compute and cache its bounds.
     void computeLayerBounds();
 
-    /* ------------------------------------------------------------------------
-     * Boot animation, on/off animations and screen capture
-     */
-
+    // Boot animation, on/off animations and screen capture
     void startBootAnim();
 
-    using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+                                 ui::PixelFormat, const bool allowProtected,
+                                 const sp<IScreenCaptureListener>&);
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+                                 bool regionSampling, const sp<IScreenCaptureListener>&);
+    status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
+                                    const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd,
+                                    bool regionSampling, ScreenCaptureResults&);
 
-    void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
-                                bool regionSampling, int* outSyncFd);
-    status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                 sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
-                                 bool useIdentityTransform, bool& outCapturedSecureLayers);
-    status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                 const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
-                                 bool regionSampling, bool& outCapturedSecureLayers);
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
-    status_t captureScreenImplLocked(const RenderArea& renderArea,
-                                     TraverseLayersFunction traverseLayers,
-                                     const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
-                                     bool forSystem, int* outSyncFd, bool regionSampling,
-                                     bool& outCapturedSecureLayers);
-    void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
-                                 const LayerVector::Visitor& visitor);
 
-    sp<StartPropertySetThread> mStartPropertySetThread;
+    // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
+    // matching ownerUid
+    void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
 
-    /* ------------------------------------------------------------------------
-     * Properties
-     */
     void readPersistentProperties();
 
-    /* ------------------------------------------------------------------------
-     * EGL
-     */
     size_t getMaxTextureSize() const;
     size_t getMaxViewportDims() const;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Display and layer stack management
      */
     // called when starting, or restarting after system_server death
@@ -781,7 +855,7 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
-    /* ------------------------------------------------------------------------
+    /*
      * H/W composer
      */
 
@@ -790,8 +864,7 @@
     // The following thread safety rules apply when accessing mHwc, either
     // directly or via getHwComposer():
     //
-    // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
-    //    only when switching into and out of vr. Recreating mHwc must only be
+    // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be
     //    done on the main thread.
     //
     // 2. When accessing mHwc on the main thread, it's not necessary to acquire
@@ -807,7 +880,7 @@
     // acquiring mStateLock.
     HWComposer& getHwComposer() const;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Compositing
      */
     void invalidateHwcGeometry();
@@ -821,7 +894,7 @@
 
     void postFrame();
 
-    /* ------------------------------------------------------------------------
+    /*
      * Display management
      */
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
@@ -841,15 +914,14 @@
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
-    /* ------------------------------------------------------------------------
-     * VSync
+    /*
+     * VSYNC
      */
-    nsecs_t getVsyncPeriod() const REQUIRES(mStateLock);
+    nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
-    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
-            REQUIRES(mStateLock);
+    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
 
     bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
 
@@ -876,13 +948,14 @@
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) {
+    sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+            REQUIRES(mStateLock) {
         const auto it = mPhysicalDisplayTokens.find(displayId);
         return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
     }
 
-    std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
-            REQUIRES(mStateLock) {
+    std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
+            const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
         for (const auto& [id, token] : mPhysicalDisplayTokens) {
             if (token == displayToken) {
                 return id;
@@ -897,7 +970,7 @@
         return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
     }
 
-    std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+    std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
         const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
@@ -905,33 +978,6 @@
     /*
      * Debugging & dumpsys
      */
-    using DumpArgs = Vector<String16>;
-    using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
-
-    template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
-    static Dumper dumper(F&& dump) {
-        using namespace std::placeholders;
-        return std::bind(std::forward<F>(dump), _3);
-    }
-
-    template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
-    Dumper dumper(F dump) {
-        using namespace std::placeholders;
-        return std::bind(dump, this, _3);
-    }
-
-    template <typename F>
-    Dumper argsDumper(F dump) {
-        using namespace std::placeholders;
-        return std::bind(dump, this, _1, _3);
-    }
-
-    template <typename F>
-    Dumper protoDumper(F dump) {
-        using namespace std::placeholders;
-        return std::bind(dump, this, _1, _2, _3);
-    }
-
     void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
@@ -939,6 +985,7 @@
     void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
     void clearStatsLocked(const DumpArgs& args, std::string& result);
     void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
+    void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
     void logFrameStats();
 
     void dumpVSync(std::string& result) const REQUIRES(mStateLock);
@@ -975,20 +1022,28 @@
 
     void onFrameRateFlexibilityTokenReleased();
 
-    /* ------------------------------------------------------------------------
-     * VrFlinger
-     */
-    void resetDisplayState() REQUIRES(mStateLock);
-
-    // Check to see if we should handoff to vr flinger.
-    void updateVrFlinger();
-
     void updateColorMatrixLocked();
 
-    /* ------------------------------------------------------------------------
-     * Attributes
+    // Verify that transaction is being called by an approved process:
+    // either AID_GRAPHICS or AID_SYSTEM.
+    status_t CheckTransactCodeCredentials(uint32_t code);
+
+    /*
+     * Generic Layer Metadata
+     */
+    const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+    /*
+     * Misc
      */
 
+    std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
+        return std::nullopt;
+    }
+
+    sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
     // access must be protected by mStateLock
@@ -999,7 +1054,7 @@
     bool mTransactionPending = false;
     bool mAnimTransactionPending = false;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
-    bool mTraversalNeededMainThread = false;
+    bool mForceTraversal = false;
 
     // global color transform states
     Daltonizer mDaltonizer;
@@ -1035,7 +1090,11 @@
     bool mInputInfoChanged = false;
     bool mGeometryInvalid = false;
     bool mAnimCompositionPending = false;
-    std::vector<sp<Layer>> mLayersWithQueuedFrames;
+
+    // Tracks layers that have pending frames which are candidates for being
+    // latched. Because this contains a set of raw layer pointers, can only be
+    // mutated on the main thread.
+    std::unordered_set<Layer*> mLayersWithQueuedFrames;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
     std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
@@ -1050,23 +1109,17 @@
     // did not change.
     bool mReusedClientComposition = false;
 
-    enum class BootStage {
-        BOOTLOADER,
-        BOOTANIMATION,
-        FINISHED,
-    };
     BootStage mBootStage = BootStage::BOOTLOADER;
 
-    struct HotplugEvent {
-        hal::HWDisplayId hwcDisplayId;
-        hal::Connection connection = hal::Connection::INVALID;
-    };
     std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
 
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
     std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
-    std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock);
+    std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
+            GUARDED_BY(mStateLock);
+
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
 
     std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
 
@@ -1076,18 +1129,18 @@
     bool mDebugDisableTransformHint = false;
     volatile nsecs_t mDebugInTransaction = 0;
     bool mForceFullDamage = false;
-    bool mPropagateBackpressure = true;
     bool mPropagateBackpressureClientComposition = false;
-    std::unique_ptr<SurfaceInterceptor> mInterceptor;
+    sp<SurfaceInterceptor> mInterceptor;
 
     SurfaceTracing mTracing{*this};
     std::mutex mTracingLock;
     bool mTracingEnabled = false;
-    bool mAddCompositionStateToTrace = false;
+    bool mTracePostComposition = false;
     std::atomic<bool> mTracingEnabledChanged = false;
 
     const std::shared_ptr<TimeStats> mTimeStats;
     const std::unique_ptr<FrameTracer> mFrameTracer;
+    const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
     bool mUseHwcVirtualDisplays = false;
     // If blurs should be enabled on this device.
     bool mSupportsBlur = false;
@@ -1123,35 +1176,9 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    struct TransactionState {
-        TransactionState(const Vector<ComposerState>& composerStates,
-                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
-                         std::vector<ListenerCallbacks> listenerCallbacks)
-              : states(composerStates),
-                displays(displayStates),
-                flags(transactionFlags),
-                desiredPresentTime(desiredPresentTime),
-                buffer(uncacheBuffer),
-                postTime(postTime),
-                privileged(privileged),
-                hasListenerCallbacks(hasListenerCallbacks),
-                listenerCallbacks(listenerCallbacks) {}
-
-        Vector<ComposerState> states;
-        Vector<DisplayState> displays;
-        uint32_t flags;
-        const int64_t desiredPresentTime;
-        client_cache_t buffer;
-        const int64_t postTime;
-        bool privileged;
-        bool hasListenerCallbacks;
-        std::vector<ListenerCallbacks> listenerCallbacks;
-    };
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Feature prototyping
      */
 
@@ -1160,10 +1187,6 @@
 
     std::atomic<size_t> mNumLayers = 0;
 
-    // Verify that transaction is being called by an approved process:
-    // either AID_GRAPHICS or AID_SYSTEM.
-    status_t CheckTransactCodeCredentials(uint32_t code);
-
     // to linkToDeath
     sp<IBinder> mWindowManager;
     // We want to avoid multiple calls to BOOT_FINISHED as they come in on
@@ -1171,9 +1194,6 @@
     // to mWindowManager or mInputFlinger
     std::atomic<bool> mBootFinished = false;
 
-    std::unique_ptr<dvr::VrFlinger> mVrFlinger;
-    std::atomic<bool> mVrFlingerRequestsDisplay = false;
-    static bool useVrFlinger;
     std::thread::id mMainThreadId = std::this_thread::get_id();
 
     DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced;
@@ -1194,7 +1214,7 @@
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Scheduler
      */
     std::unique_ptr<Scheduler> mScheduler;
@@ -1202,30 +1222,16 @@
     scheduler::ConnectionHandle mSfConnectionHandle;
 
     // Stores phase offsets configured per refresh rate.
-    std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+    std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
 
-    // Optional to defer construction until scheduler connections are created.
-    std::optional<scheduler::VSyncModulator> mVSyncModulator;
+    // Optional to defer construction until PhaseConfiguration is created.
+    std::optional<scheduler::VsyncModulator> mVsyncModulator;
 
     std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
-
-    /* ------------------------------------------------------------------------
-     * Generic Layer Metadata
-     */
-    const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
-
-    /* ------------------------------------------------------------------------
-     * Misc
-     */
-
-    std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
-        return std::nullopt;
-    }
+    hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
 
     std::mutex mActiveConfigLock;
     // This bit is set once we start setting the config. We read from this bit during the
@@ -1247,21 +1253,12 @@
     const float mInternalDisplayDensity;
     const float mEmulatedDisplayDensity;
 
-    sp<IInputFlinger> mInputFlinger;
+    sp<os::IInputFlinger> mInputFlinger;
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
 
-    struct SetInputWindowsListener : BnSetInputWindowsListener {
-        explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
-              : mFlinger(std::move(flinger)) {}
-
-        void onSetInputWindowsFinished() override;
-
-        const sp<SurfaceFlinger> mFlinger;
-    };
-
-    const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
+    sp<SetInputWindowsListener> mSetInputWindowsListener;
 
     bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
@@ -1281,14 +1278,12 @@
     // be any issues with a raw pointer referencing an invalid object.
     std::unordered_set<Layer*> mOffscreenLayers;
 
-    // Flags to capture the state of Vsync in HWC
-    hal::Vsync mHWCVsyncState = hal::Vsync::DISABLE;
-    hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-
     // Fields tracking the current jank event: when it started and how many
     // janky frames there are.
     nsecs_t mMissedFrameJankStart = 0;
     int32_t mMissedFrameJankCount = 0;
+    // Positive if jank should be uploaded in postComposition
+    nsecs_t mLastJankDuration = -1;
 
     int mFrameRateFlexibilityTokenCount = 0;
 
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index ddd20a5..9a8deae 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -37,25 +37,15 @@
 #include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/DispSync.h"
-#include "Scheduler/EventControlThread.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
 
 namespace android::surfaceflinger {
 
 DefaultFactory::~DefaultFactory() = default;
 
-std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) {
-    return std::make_unique<android::impl::DispSync>(name, hasSyncFramework);
-}
-
-std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread(
-        SetVSyncEnabled setVSyncEnabled) {
-    return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled));
-}
-
 std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
     return std::make_unique<android::impl::HWComposer>(serviceName);
 }
@@ -64,26 +54,22 @@
     return std::make_unique<android::impl::MessageQueue>();
 }
 
-std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
         const scheduler::RefreshRateConfigs& refreshRateConfigs) {
     if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
-        return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+        return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
     } else {
         return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
     }
 }
 
 std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
-        SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
-        ISchedulerCallback& schedulerCallback) {
-    return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
-                                       property_get_bool("debug.sf.use_content_detection_v2", true),
-                                       sysprop::use_content_detection_for_refresh_rate(false));
+        const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) {
+    return std::make_unique<Scheduler>(configs, callback);
 }
 
-std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
-        SurfaceFlinger* flinger) {
-    return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
+    return new android::impl::SurfaceInterceptor();
 }
 
 sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index bd40cfb..40774ef 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -26,16 +26,13 @@
 public:
     virtual ~DefaultFactory();
 
-    std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
-    std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<MessageQueue> createMessageQueue() override;
-    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             const scheduler::RefreshRateConfigs&) override;
-    std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
-                                               const scheduler::RefreshRateConfigs&,
+    std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                ISchedulerCallback&) override;
-    std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
+    sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6f4fcc6..2dd563b 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -34,13 +34,10 @@
 class EffectLayer;
 class ContainerLayer;
 class DisplayDevice;
-class DispSync;
-class EventControlThread;
 class GraphicBuffer;
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
-class ISchedulerCallback;
 class Layer;
 class MessageQueue;
 class Scheduler;
@@ -49,6 +46,7 @@
 class SurfaceInterceptor;
 
 struct DisplayDeviceCreationArgs;
+struct ISchedulerCallback;
 struct LayerCreationArgs;
 
 namespace compositionengine {
@@ -56,7 +54,8 @@
 } // namespace compositionengine
 
 namespace scheduler {
-class PhaseConfiguration;
+class VsyncConfiguration;
+class VsyncController;
 class RefreshRateConfigs;
 } // namespace scheduler
 
@@ -68,18 +67,13 @@
 // of each interface.
 class Factory {
 public:
-    using SetVSyncEnabled = std::function<void(bool)>;
-
-    virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
-    virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
-    virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+    virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             const scheduler::RefreshRateConfigs&) = 0;
-    virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
-                                                       const scheduler::RefreshRateConfigs&,
+    virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                        ISchedulerCallback&) = 0;
-    virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
+    virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 9d78702..97725ec 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,10 @@
     return primaries;
 }
 
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue) {
+    return SurfaceFlingerProperties::update_device_product_info_on_hotplug_reconnect().value_or(
+            defaultValue);
+}
+
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index c63adfe..37a6b40 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -95,6 +95,9 @@
 int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
 
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
+
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue);
+
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 80102bd..3548923 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -40,9 +40,22 @@
 
 namespace impl {
 
-SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
-    :   mFlinger(flinger)
-{
+void SurfaceInterceptor::addTransactionTraceListener(
+        const sp<gui::ITransactionTraceListener>& listener) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+    std::scoped_lock lock(mListenersMutex);
+
+    asBinder->linkToDeath(this);
+
+    listener->onToggled(mEnabled); // notifies of current state
+
+    mTraceToggledListeners.emplace(asBinder, listener);
+}
+
+void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mListenersMutex);
+    mTraceToggledListeners.erase(who);
 }
 
 void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
@@ -52,8 +65,14 @@
         return;
     }
     ATRACE_CALL();
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (const auto& [_, listener] : mTraceToggledListeners) {
+            listener->onToggled(true);
+        }
+    }
     mEnabled = true;
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
     saveExistingDisplaysLocked(displays);
     saveExistingSurfacesLocked(layers);
 }
@@ -63,8 +82,14 @@
         return;
     }
     ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (const auto& [_, listener] : mTraceToggledListeners) {
+            listener->onToggled(false);
+        }
+    }
     mEnabled = false;
+    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
     status_t err(writeProtoFileLocked());
     ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
     ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
@@ -115,12 +140,12 @@
     addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
+    addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
     if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
         addDeferTransactionLocked(transaction, layerId,
                                   layer->mCurrentState.barrierLayer_legacy.promote(),
-                                  layer->mCurrentState.frameNumber_legacy);
+                                  layer->mCurrentState.barrierFrameNumber);
     }
-    addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
     addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
                    layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
                            layer_state_t::eLayerSecure);
@@ -143,7 +168,7 @@
     addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
     addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
     addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
-                               display.viewport, display.frame);
+                               display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
 }
 
 status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -221,6 +246,13 @@
     protoRect->set_bottom(rect.bottom);
 }
 
+void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
+                                                    int32_t uid) {
+    Origin* origin(transaction->mutable_origin());
+    origin->set_pid(pid);
+    origin->set_uid(uid);
+}
+
 void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
         float x, float y)
 {
@@ -330,6 +362,25 @@
     blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
 }
 
+void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+                                              const std::vector<BlurRegion>& blurRegions) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
+    for (const auto blurRegion : blurRegions) {
+        const auto blurRegionChange = blurRegionsChange->add_blur_regions();
+        blurRegionChange->set_blur_radius(blurRegion.blurRadius);
+        blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
+        blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
+        blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
+        blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
+        blurRegionChange->set_alpha(blurRegion.alpha);
+        blurRegionChange->set_left(blurRegion.left);
+        blurRegionChange->set_top(blurRegion.top);
+        blurRegionChange->set_right(blurRegion.right);
+        blurRegionChange->set_bottom(blurRegion.bottom);
+    }
+}
+
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
         const sp<const Layer>& layer, uint64_t frameNumber)
 {
@@ -344,14 +395,6 @@
     deferTransaction->set_frame_number(frameNumber);
 }
 
-void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
-        int32_t layerId, int32_t overrideScalingMode)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
-    overrideChange->set_override_scaling_mode(overrideScalingMode);
-}
-
 void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
                                            int32_t parentId) {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -433,36 +476,36 @@
     if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
         addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
     }
+    if (state.what & layer_state_t::eBlurRegionsChanged) {
+        addBlurRegionsLocked(transaction, layerId, state.blurRegions);
+    }
     if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
-        if (state.barrierHandle_legacy != nullptr) {
-            otherLayer =
-                    static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote();
-        } else if (state.barrierGbp_legacy != nullptr) {
-            auto const& gbp = state.barrierGbp_legacy;
-            if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
-                otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
-            } else {
-                ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
-            }
+        if (state.barrierSurfaceControl_legacy != nullptr) {
+            otherLayer = static_cast<Layer::Handle*>(
+                                 state.barrierSurfaceControl_legacy->getHandle().get())
+                                 ->owner.promote();
         }
-        addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
-    }
-    if (state.what & layer_state_t::eOverrideScalingModeChanged) {
-        addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+        addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber);
     }
     if (state.what & layer_state_t::eReparent) {
-        addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild));
+        auto parentHandle = (state.parentSurfaceControlForChild)
+                ? state.parentSurfaceControlForChild->getHandle()
+                : nullptr;
+        addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
     }
     if (state.what & layer_state_t::eReparentChildren) {
-        addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle));
+        addReparentChildrenLocked(transaction, layerId,
+                                  getLayerIdFromHandle(state.reparentSurfaceControl->getHandle()));
     }
     if (state.what & layer_state_t::eDetachChildren) {
         addDetachChildrenLocked(transaction, layerId, true);
     }
     if (state.what & layer_state_t::eRelativeLayerChanged) {
         addRelativeParentLocked(transaction, layerId,
-                                getLayerIdFromHandle(state.relativeLayerHandle), state.z);
+                                getLayerIdFromHandle(
+                                        state.relativeLayerSurfaceControl->getHandle()),
+                                state.z);
     }
     if (state.what & layer_state_t::eShadowRadiusChanged) {
         addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
@@ -483,18 +526,20 @@
     }
     if (state.what & DisplayState::eDisplayProjectionChanged) {
         addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
-                                   state.viewport, state.frame);
+                                   state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
     }
 }
 
-void SurfaceInterceptor::addTransactionLocked(Increment* increment,
-        const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
-{
+void SurfaceInterceptor::addTransactionLocked(
+        Increment* increment, const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
+        int originUid, uint64_t transactionId) {
     Transaction* transaction(increment->mutable_transaction());
     transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
     transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+    setTransactionOriginLocked(transaction, originPid, originUid);
+    transaction->set_id(transactionId);
     for (const auto& compState: stateUpdates) {
         addSurfaceChangesLocked(transaction, compState.state);
     }
@@ -613,17 +658,18 @@
     powerModeUpdate->set_mode(mode);
 }
 
-void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t flags)
-{
+void SurfaceInterceptor::saveTransaction(
+        const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
+        uint64_t transactionId) {
     if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
         return;
     }
     ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
     addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
-            flags);
+                         flags, originPid, originUid, transactionId);
 }
 
 void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 896bdcc..3df79c6 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -21,6 +21,8 @@
 
 #include <mutex>
 
+#include <binder/IBinder.h>
+
 #include <gui/LayerState.h>
 
 #include <utils/KeyedVector.h>
@@ -48,7 +50,7 @@
 
 constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
 
-class SurfaceInterceptor {
+class SurfaceInterceptor : public IBinder::DeathRecipient {
 public:
     virtual ~SurfaceInterceptor();
 
@@ -58,11 +60,16 @@
     virtual void disable() = 0;
     virtual bool isEnabled() = 0;
 
+    virtual void addTransactionTraceListener(
+            const sp<gui::ITransactionTraceListener>& listener) = 0;
+    virtual void binderDied(const wp<IBinder>& who) = 0;
+
     // Intercept display and surface transactions
     virtual void saveTransaction(
             const Vector<ComposerState>& stateUpdates,
             const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0;
+            const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
+            int originUid, uint64_t transactionId) = 0;
 
     // Intercept surface data
     virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
@@ -85,7 +92,7 @@
  */
 class SurfaceInterceptor final : public android::SurfaceInterceptor {
 public:
-    explicit SurfaceInterceptor(SurfaceFlinger* const flinger);
+    SurfaceInterceptor() = default;
     ~SurfaceInterceptor() override = default;
 
     // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
@@ -94,10 +101,14 @@
     void disable() override;
     bool isEnabled() override;
 
+    void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
+    void binderDied(const wp<IBinder>& who) override;
+
     // Intercept display and surface transactions
     void saveTransaction(const Vector<ComposerState>& stateUpdates,
                          const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                         const Vector<DisplayState>& changedDisplays, uint32_t flags) override;
+                         const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
+                         int originUid, uint64_t transactionId) override;
 
     // Intercept surface data
     void saveSurfaceCreation(const sp<const Layer>& layer) override;
@@ -154,14 +165,16 @@
     void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
                                        int32_t backgroundBlurRadius);
+    void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+                              const std::vector<BlurRegion>& effectRegions);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
-    void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
-            int32_t overrideScalingMode);
     void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
     void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+                              const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+                              const Vector<DisplayState>& changedDisplays,
+                              uint32_t transactionFlags, int originPid, int originUid,
+                              uint64_t transactionId);
     void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
     void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
     void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
@@ -182,12 +195,16 @@
     void addDisplayChangesLocked(Transaction* transaction,
             const DisplayState& state, int32_t sequenceId);
 
+    // Add transaction origin to trace
+    void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
 
     bool mEnabled {false};
     std::string mOutputFileName {DEFAULT_FILENAME};
     std::mutex mTraceMutex {};
     Trace mTrace {};
-    SurfaceFlinger* const mFlinger;
+    std::mutex mListenersMutex;
+    std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
+            GUARDED_BY(mListenersMutex);
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index d84ce69..1d1f0c5 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "SurfaceTracing"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -32,65 +29,65 @@
 
 namespace android {
 
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger)
-      : mFlinger(flinger), mSfLock(flinger.mTracingLock) {}
+SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
 
-void SurfaceTracing::mainLoop() {
-    bool enabled = addFirstEntry();
-    while (enabled) {
-        LayersTraceProto entry = traceWhenNotified();
-        enabled = addTraceToBuffer(entry);
-    }
-}
-
-bool SurfaceTracing::addFirstEntry() {
-    LayersTraceProto entry;
-    {
-        std::scoped_lock lock(mSfLock);
-        entry = traceLayersLocked("tracing.enable");
-    }
-    return addTraceToBuffer(entry);
-}
-
-LayersTraceProto SurfaceTracing::traceWhenNotified() {
-    std::unique_lock<std::mutex> lock(mSfLock);
-    mCanStartTrace.wait(lock);
-    android::base::ScopedLockAssertion assumeLock(mSfLock);
-    LayersTraceProto entry = traceLayersLocked(mWhere);
-    mTracingInProgress = false;
-    mMissedTraceEntries = 0;
-    lock.unlock();
-    return entry;
-}
-
-bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) {
+bool SurfaceTracing::enable() {
     std::scoped_lock lock(mTraceLock);
-    mBuffer.emplace(std::move(entry));
-    if (mWriteToFile) {
-        writeProtoFileLocked();
-        mWriteToFile = false;
+    if (mEnabled) {
+        return false;
     }
+
+    if (flagIsSet(TRACE_SYNC)) {
+        runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
+    } else {
+        runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
+                                                               mFlinger.mTracingLock);
+    }
+    mEnabled = true;
+    return true;
+}
+
+bool SurfaceTracing::disable() {
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return false;
+    }
+    mEnabled = false;
+    runner->stop();
+    return true;
+}
+
+bool SurfaceTracing::isEnabled() const {
+    std::scoped_lock lock(mTraceLock);
     return mEnabled;
 }
 
+status_t SurfaceTracing::writeToFile() {
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return STATUS_OK;
+    }
+    return runner->writeToFile();
+}
+
 void SurfaceTracing::notify(const char* where) {
-    std::scoped_lock lock(mSfLock);
-    notifyLocked(where);
+    if (mEnabled) {
+        runner->notify(where);
+    }
 }
 
 void SurfaceTracing::notifyLocked(const char* where) {
-    mWhere = where;
-    if (mTracingInProgress) {
-        mMissedTraceEntries++;
+    if (mEnabled) {
+        runner->notifyLocked(where);
     }
-    mTracingInProgress = true;
-    mCanStartTrace.notify_one();
 }
 
-void SurfaceTracing::writeToFileAsync() {
+void SurfaceTracing::dump(std::string& result) const {
     std::scoped_lock lock(mTraceLock);
-    mWriteToFile = true;
-    mCanStartTrace.notify_one();
+    base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+    if (mEnabled) {
+        runner->dump(result);
+    }
 }
 
 void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
@@ -101,12 +98,12 @@
 }
 
 void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
-    auto protoSize = proto.ByteSize();
+    size_t protoSize = static_cast<size_t>(proto.ByteSize());
     while (mUsedInBytes + protoSize > mSizeInBytes) {
         if (mStorage.empty()) {
             return;
         }
-        mUsedInBytes -= mStorage.front().ByteSize();
+        mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
         mStorage.pop();
     }
     mUsedInBytes += protoSize;
@@ -115,7 +112,7 @@
 }
 
 void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
-    fileProto->mutable_entry()->Reserve(mStorage.size());
+    fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
 
     while (!mStorage.empty()) {
         auto entry = fileProto->add_entry();
@@ -124,85 +121,21 @@
     }
 }
 
-bool SurfaceTracing::enable() {
-    std::scoped_lock lock(mTraceLock);
-
-    if (mEnabled) {
-        return false;
-    }
-
-    mBuffer.reset(mBufferSize);
-    mEnabled = true;
-    mThread = std::thread(&SurfaceTracing::mainLoop, this);
-    return true;
+SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
+      : mFlinger(flinger), mConfig(config) {
+    mBuffer.setSize(mConfig.bufferSize);
 }
 
-status_t SurfaceTracing::writeToFile() {
-    std::thread thread;
-    {
-        std::scoped_lock lock(mTraceLock);
-        thread = std::move(mThread);
-    }
-    thread.join();
-    return mLastErr;
+void SurfaceTracing::Runner::notify(const char* where) {
+    LayersTraceProto entry = traceLayers(where);
+    mBuffer.emplace(std::move(entry));
 }
 
-bool SurfaceTracing::disable() {
-    std::scoped_lock lock(mTraceLock);
-
-    if (!mEnabled) {
-        return false;
-    }
-
-    mEnabled = false;
-    mWriteToFile = true;
-    mCanStartTrace.notify_all();
-    return true;
+status_t SurfaceTracing::Runner::stop() {
+    return writeToFile();
 }
 
-bool SurfaceTracing::isEnabled() const {
-    std::scoped_lock lock(mTraceLock);
-    return mEnabled;
-}
-
-void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) {
-    std::scoped_lock lock(mTraceLock);
-    mBufferSize = bufferSizeInByte;
-    mBuffer.setSize(bufferSizeInByte);
-}
-
-void SurfaceTracing::setTraceFlags(uint32_t flags) {
-    std::scoped_lock lock(mSfLock);
-    mTraceFlags = flags;
-}
-
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
-    ATRACE_CALL();
-
-    LayersTraceProto entry;
-    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
-    entry.set_where(where);
-    LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
-
-    if (flagIsSetLocked(SurfaceTracing::TRACE_EXTRA)) {
-        mFlinger.dumpOffscreenLayersProto(layers);
-    }
-    entry.mutable_layers()->Swap(&layers);
-
-    if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
-        std::string hwcDump;
-        mFlinger.dumpHwc(hwcDump);
-        entry.set_hwc_blob(hwcDump);
-    }
-    if (!flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION)) {
-        entry.set_excludes_composition_state(true);
-    }
-    entry.set_missed_entries(mMissedTraceEntries);
-
-    return entry;
-}
-
-void SurfaceTracing::writeProtoFileLocked() {
+status_t SurfaceTracing::Runner::writeToFile() {
     ATRACE_CALL();
 
     LayersTraceFileProto fileProto;
@@ -211,33 +144,114 @@
     fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
                                LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
     mBuffer.flush(&fileProto);
-    mBuffer.reset(mBufferSize);
+    mBuffer.reset(mConfig.bufferSize);
 
     if (!fileProto.SerializeToString(&output)) {
         ALOGE("Could not save the proto file! Permission denied");
-        mLastErr = PERMISSION_DENIED;
+        return PERMISSION_DENIED;
     }
 
     // -rw-r--r--
     const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-    if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+    if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
                                           true)) {
         ALOGE("Could not save the proto file! There are missing fields");
-        mLastErr = PERMISSION_DENIED;
+        return PERMISSION_DENIED;
     }
 
-    mLastErr = NO_ERROR;
+    return NO_ERROR;
 }
 
-void SurfaceTracing::dump(std::string& result) const {
-    std::scoped_lock lock(mTraceLock);
-    base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
+    ATRACE_CALL();
+
+    LayersTraceProto entry;
+    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+    entry.set_where(where);
+    LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
+
+    if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
+        mFlinger.dumpOffscreenLayersProto(layers);
+    }
+    entry.mutable_layers()->Swap(&layers);
+
+    if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
+        std::string hwcDump;
+        mFlinger.dumpHwc(hwcDump);
+        entry.set_hwc_blob(hwcDump);
+    }
+    if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
+        entry.set_excludes_composition_state(true);
+    }
+    entry.set_missed_entries(mMissedTraceEntries);
+
+    return entry;
+}
+
+void SurfaceTracing::Runner::dump(std::string& result) const {
     base::StringAppendF(&result, "  number of entries: %zu (%.2fMB / %.2fMB)\n",
                         mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
                         float(mBuffer.size()) / float(1_MB));
 }
 
-} // namespace android
+SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
+                                         std::mutex& sfLock)
+      : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
+    mEnabled = true;
+    mThread = std::thread(&AsyncRunner::loop, this);
+}
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+void SurfaceTracing::AsyncRunner::loop() {
+    while (mEnabled) {
+        LayersTraceProto entry;
+        bool entryAdded = traceWhenNotified(&entry);
+        if (entryAdded) {
+            mBuffer.emplace(std::move(entry));
+        }
+        if (mWriteToFile) {
+            Runner::writeToFile();
+            mWriteToFile = false;
+        }
+    }
+}
+
+bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
+    std::unique_lock<std::mutex> lock(mSfLock);
+    mCanStartTrace.wait(lock);
+    if (!mAddEntry) {
+        return false;
+    }
+    *outProto = traceLayers(mWhere);
+    mAddEntry = false;
+    mMissedTraceEntries = 0;
+    return true;
+}
+
+void SurfaceTracing::AsyncRunner::notify(const char* where) {
+    std::scoped_lock lock(mSfLock);
+    notifyLocked(where);
+}
+
+void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
+    mWhere = where;
+    if (mAddEntry) {
+        mMissedTraceEntries++;
+    }
+    mAddEntry = true;
+    mCanStartTrace.notify_one();
+}
+
+status_t SurfaceTracing::AsyncRunner::writeToFile() {
+    mWriteToFile = true;
+    mCanStartTrace.notify_one();
+    return STATUS_OK;
+}
+
+status_t SurfaceTracing::AsyncRunner::stop() {
+    mEnabled = false;
+    mCanStartTrace.notify_one();
+    mThread.join();
+    return Runner::writeToFile();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index f208eb8..576bba7 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -32,25 +32,31 @@
 namespace android {
 
 class SurfaceFlinger;
-
 constexpr auto operator""_MB(unsigned long long const num) {
     return num * 1024 * 1024;
 }
 /*
- * SurfaceTracing records layer states during surface flinging.
+ * SurfaceTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
  */
 class SurfaceTracing {
 public:
-    explicit SurfaceTracing(SurfaceFlinger& flinger);
+    SurfaceTracing(SurfaceFlinger& flinger);
     bool enable();
     bool disable();
     status_t writeToFile();
     bool isEnabled() const;
+    /*
+     * Adds a trace entry, must be called from the drawing thread or while holding the
+     * SurfaceFlinger tracing lock.
+     */
     void notify(const char* where);
-    void notifyLocked(const char* where) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */;
+    /*
+     * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
+     */
+    void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
 
-    void setBufferSize(size_t bufferSizeInByte);
-    void writeToFileAsync();
+    void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
     void dump(std::string& result) const;
 
     enum : uint32_t {
@@ -59,18 +65,34 @@
         TRACE_COMPOSITION = 1 << 2,
         TRACE_EXTRA = 1 << 3,
         TRACE_HWC = 1 << 4,
-        TRACE_ALL = 0xffffffff
+        // Add non-geometry composition changes to the trace.
+        TRACE_BUFFERS = 1 << 5,
+        // Add entries from the drawing thread post composition.
+        TRACE_SYNC = 1 << 6,
+        TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
     };
-    void setTraceFlags(uint32_t flags);
-    bool flagIsSetLocked(uint32_t flags) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */ {
-        return (mTraceFlags & flags) == flags;
-    }
+    void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
+    bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
 
 private:
-    static constexpr auto kDefaultBufferCapInByte = 5_MB;
-    static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
+    class Runner;
+    static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
+    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
 
-    class LayersTraceBuffer { // ring buffer
+    SurfaceFlinger& mFlinger;
+    mutable std::mutex mTraceLock;
+    bool mEnabled = false;
+    std::unique_ptr<Runner> runner;
+
+    struct Config {
+        uint32_t flags = TRACE_CRITICAL | TRACE_INPUT;
+        size_t bufferSize = DEFAULT_BUFFER_SIZE;
+    } mConfig;
+
+    /*
+     * ring buffer.
+     */
+    class LayersTraceBuffer {
     public:
         size_t size() const { return mSizeInBytes; }
         size_t used() const { return mUsedInBytes; }
@@ -83,35 +105,59 @@
 
     private:
         size_t mUsedInBytes = 0U;
-        size_t mSizeInBytes = 0U;
+        size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
         std::queue<LayersTraceProto> mStorage;
     };
 
-    void mainLoop();
-    bool addFirstEntry();
-    LayersTraceProto traceWhenNotified();
-    LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+    /*
+     * Implements a synchronous way of adding trace entries. This must be called
+     * from the drawing thread.
+     */
+    class Runner {
+    public:
+        Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
+        virtual ~Runner() = default;
+        virtual status_t stop();
+        virtual status_t writeToFile();
+        virtual void notify(const char* where);
+        /* Cannot be called with a synchronous runner. */
+        virtual void notifyLocked(const char* /* where */) {}
+        void dump(std::string& result) const;
 
-    // Returns true if trace is enabled.
-    bool addTraceToBuffer(LayersTraceProto& entry);
-    void writeProtoFileLocked() REQUIRES(mTraceLock);
+    protected:
+        bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
+        SurfaceFlinger& mFlinger;
+        SurfaceTracing::Config mConfig;
+        SurfaceTracing::LayersTraceBuffer mBuffer;
+        uint32_t mMissedTraceEntries = 0;
+        LayersTraceProto traceLayers(const char* where);
+    };
 
-    SurfaceFlinger& mFlinger;
-    status_t mLastErr = NO_ERROR;
-    std::thread mThread;
-    std::condition_variable mCanStartTrace;
+    /*
+     * Implements asynchronous way to add trace entries called from a separate thread while holding
+     * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
+     * scheduled in time.
+     */
+    class AsyncRunner : public Runner {
+    public:
+        AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
+        virtual ~AsyncRunner() = default;
+        status_t stop() override;
+        status_t writeToFile() override;
+        void notify(const char* where) override;
+        void notifyLocked(const char* where);
 
-    std::mutex& mSfLock;
-    uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
-    const char* mWhere GUARDED_BY(mSfLock) = "";
-    uint32_t mMissedTraceEntries GUARDED_BY(mSfLock) = 0;
-    bool mTracingInProgress GUARDED_BY(mSfLock) = false;
-
-    mutable std::mutex mTraceLock;
-    LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
-    size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte;
-    bool mEnabled GUARDED_BY(mTraceLock) = false;
-    bool mWriteToFile GUARDED_BY(mTraceLock) = false;
+    private:
+        std::mutex& mSfLock;
+        std::condition_variable mCanStartTrace;
+        std::thread mThread;
+        const char* mWhere = "";
+        bool mWriteToFile = false;
+        bool mEnabled = false;
+        bool mAddEntry = false;
+        void loop();
+        bool traceWhenNotified(LayersTraceProto* outProto);
+    };
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 3901757..0a23da2 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libtimestats",
     srcs: [
         "TimeStats.cpp",
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 37194c6..fe9e737 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -33,6 +30,8 @@
 #include <algorithm>
 #include <chrono>
 
+#include "timestatsproto/TimeStatsHelper.h"
+
 namespace android {
 
 namespace impl {
@@ -115,6 +114,13 @@
                                        mMaxPulledHistogramBuckets);
     mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
                                              renderEngineTimingBytes.size());
+
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed);
     mStatsDelegate->statsEventBuild(event);
     clearGlobalLocked();
 
@@ -160,6 +166,13 @@
 
         mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
         mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->uid);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
 
         mStatsDelegate->statsEventBuild(event);
     }
@@ -397,11 +410,13 @@
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
         if (prevTimeRecord.ready) {
+            uid_t uid = layerRecord.uid;
             const std::string& layerName = layerRecord.layerName;
-            if (!mTimeStats.stats.count(layerName)) {
-                mTimeStats.stats[layerName].layerName = layerName;
+            if (!mTimeStats.stats.count({uid, layerName})) {
+                mTimeStats.stats[{uid, layerName}].uid = uid;
+                mTimeStats.stats[{uid, layerName}].layerName = layerName;
             }
-            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
+            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
             timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -462,8 +477,13 @@
             layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
 }
 
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+    return mTimeStats.stats.count({uid, layerName}) > 0 ||
+            mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+}
+
 void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                            nsecs_t postTime) {
+                            uid_t uid, nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -471,11 +491,12 @@
           postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+    if (!canAddNewAggregatedStats(uid, layerName)) {
         return;
     }
     if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
         layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerId].uid = uid;
         mTimeStatsTracker[layerId].layerName = layerName;
     }
     if (!mTimeStatsTracker.count(layerId)) return;
@@ -655,6 +676,66 @@
     flushAvailableRecordsToStatsLocked(layerId);
 }
 
+template <class T>
+static void updateJankPayload(T& t, int32_t reasons) {
+    t.jankPayload.totalFrames++;
+
+    static const constexpr int32_t kValidJankyReason =
+            TimeStats::JankType::SurfaceFlingerDeadlineMissed |
+            TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed |
+            TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display;
+    if (reasons & kValidJankyReason) {
+        t.jankPayload.totalJankyFrames++;
+        if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) {
+            t.jankPayload.totalSFLongCpu++;
+        }
+        if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
+            t.jankPayload.totalSFLongGpu++;
+        }
+        if ((reasons & TimeStats::JankType::Display) != 0) {
+            t.jankPayload.totalSFUnattributed++;
+        }
+        if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) {
+            t.jankPayload.totalAppUnattributed++;
+        }
+    }
+}
+
+void TimeStats::incrementJankyFrames(int32_t reasons) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons);
+}
+
+void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    // Only update layer stats if we're allowed to do so.
+    // As an implementation detail, we do this because this method is expected to be
+    // called from FrameTimeline, which is allowed to do jank analysis well after a frame is
+    // presented. This means that we can't rely on TimeStats to flush layer records over to the
+    // aggregated stats.
+    if (!canAddNewAggregatedStats(uid, layerName)) {
+        return;
+    }
+
+    // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences
+    // before TimeStats does.
+    if (!mTimeStats.stats.count({uid, layerName})) {
+        mTimeStats.stats[{uid, layerName}].uid = uid;
+        mTimeStats.stats[{uid, layerName}].layerName = layerName;
+    }
+
+    TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+    updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+}
+
 void TimeStats::onDestroy(int32_t layerId) {
     ATRACE_CALL();
     ALOGV("[%d]-onDestroy", layerId);
@@ -860,6 +941,7 @@
     mTimeStats.presentToPresent.hist.clear();
     mTimeStats.frameDuration.hist.clear();
     mTimeStats.renderEngineTiming.hist.clear();
+    mTimeStats.jankPayload = TimeStatsHelper::JankPayload();
     mTimeStats.refreshRateStats.clear();
     mPowerTime.prevTime = systemTime();
     mGlobalRecord.prevPresentTime = 0;
@@ -905,6 +987,3 @@
 } // namespace impl
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8de5d0c..4fa0a02 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,7 @@
 #pragma once
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <cstdint>
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
@@ -85,7 +86,7 @@
                                             const std::shared_ptr<FenceTime>& readyFence) = 0;
 
     virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                             nsecs_t postTime) = 0;
+                             uid_t uid, nsecs_t postTime) = 0;
     virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
     // Reasons why latching a particular buffer may be skipped
     enum class LatchSkipReason {
@@ -108,6 +109,40 @@
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+    // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry.
+    enum JankType {
+        // No Jank
+        None = 0x0,
+        // Jank not related to SurfaceFlinger or the App
+        Display = 0x1,
+        // SF took too long on the CPU
+        SurfaceFlingerDeadlineMissed = 0x2,
+        // SF took too long on the GPU
+        SurfaceFlingerGpuDeadlineMissed = 0x4,
+        // Either App or GPU took too long on the frame
+        AppDeadlineMissed = 0x8,
+        // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
+        // jank
+        // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+        PredictionExpired = 0x10,
+        // Latching a buffer early might cause an early present of the frame
+        SurfaceFlingerEarlyLatch = 0x20,
+    };
+
+    // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure
+    // responsible for computing jank in the system, this is expected to be called from
+    // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no
+    // jank reasons, then total frames are incremented but jank is not, for accurate accounting of
+    // janky frames.
+    virtual void incrementJankyFrames(int32_t reasons) = 0;
+    // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as
+    // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible
+    // for computing jank in the system, this is expected to be called from FrameTimeline, rather
+    // than directly from SurfaceFlinger or individual layers.
+    // If there are no jank reasons, then total frames are incremented but jank is not, for accurate
+    // accounting of janky frames.
+    virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0;
     // Clean up the layer record
     virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -142,6 +177,7 @@
     };
 
     struct LayerRecord {
+        uid_t uid;
         std::string layerName;
         // This is the index in timeRecords, at which the timestamps for that
         // specific frame are still not fully received. This is not waiting for
@@ -241,7 +277,7 @@
     void recordRenderEngineDuration(nsecs_t startTime,
                                     const std::shared_ptr<FenceTime>& readyFence) override;
 
-    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
+    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
                      nsecs_t postTime) override;
     void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
     void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
@@ -253,6 +289,8 @@
     void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence) override;
+    void incrementJankyFrames(int32_t reasons) override;
+    void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override;
     // Clean up the layer record
     void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -276,6 +314,7 @@
     void flushAvailableRecordsToStatsLocked(int32_t layerId);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
+    bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
 
     void enable();
     void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index b937f41..fae4e94 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libtimestats_proto",
     export_include_dirs: ["include"],
 
@@ -30,3 +30,15 @@
         "-Wno-unused-parameter",
     ],
 }
+
+// ====  java host library for timestats proto  ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library_host {
+    name: "host-timestats-proto",
+    srcs: [
+        "timestats.proto",
+    ],
+    proto: {
+        type: "full",
+    },
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index c90b1b8..0fb748f 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -77,17 +77,33 @@
     return result;
 }
 
+std::string TimeStatsHelper::JankPayload::toString() const {
+    std::string result;
+    StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames);
+    StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames);
+    StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu);
+    StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu);
+    StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed);
+    StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed);
+    return result;
+}
+
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
+    StringAppendF(&result, "uid = %d\n", uid);
     StringAppendF(&result, "layerName = %s\n", layerName.c_str());
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
     StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
+    result.append("Jank payload for this layer:\n");
+    result.append(jankPayload.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
-        StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
+        const float averageTime = iter->second.averageTime();
+        const float averageFPS = averageTime < 1.0f ? 0.0f : 1000.0f / averageTime;
+        StringAppendF(&result, "averageFPS = %.3f\n", averageFPS);
     }
     for (const auto& ele : deltas) {
         StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
@@ -108,9 +124,11 @@
     StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
     StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
     StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+    result.append("Global aggregated jank payload:\n");
+    result.append(jankPayload.toString());
     StringAppendF(&result, "displayConfigStats is as below:\n");
     for (const auto& [fps, duration] : refreshRateStats) {
-        StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+        StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
     }
     result.back() = '\n';
     StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0c75f96..033eb5d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -40,14 +40,28 @@
         std::string toString() const;
     };
 
+    struct JankPayload {
+        // note that transactions are counted for these frames.
+        int32_t totalFrames = 0;
+        int32_t totalJankyFrames = 0;
+        int32_t totalSFLongCpu = 0;
+        int32_t totalSFLongGpu = 0;
+        int32_t totalSFUnattributed = 0;
+        int32_t totalAppUnattributed = 0;
+
+        std::string toString() const;
+    };
+
     class TimeStatsLayer {
     public:
+        uid_t uid;
         std::string layerName;
         std::string packageName;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
         int32_t lateAcquireFrames = 0;
         int32_t badDesiredPresentFrames = 0;
+        JankPayload jankPayload;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
@@ -69,8 +83,17 @@
         Histogram presentToPresent;
         Histogram frameDuration;
         Histogram renderEngineTiming;
-        std::unordered_map<std::string, TimeStatsLayer> stats;
+
+        struct StatsHasher {
+            size_t operator()(const std::pair<uid_t, std::string>& p) const {
+                // Normally this isn't a very good hash function due to symmetry reasons,
+                // but these are distinct types so this should be good enough
+                return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+            }
+        };
+        std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats;
         std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+        JankPayload jankPayload;
 
         std::string toString(std::optional<uint32_t> maxLayers) const;
         SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 4e7f67d..eee4bec 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -21,12 +21,32 @@
 #include <cmath>
 #include <string>
 
+namespace std {
+template <class Rep, class Period>
+bool signbit(std::chrono::duration<Rep, Period> v) {
+    return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
+}
+} // namespace std
+
 namespace android {
 
+namespace {
+template <typename T>
+int64_t to_int64(T v) {
+    return int64_t(v);
+}
+
+template <class Rep, class Period>
+int64_t to_int64(std::chrono::duration<Rep, Period> v) {
+    return int64_t(v.count());
+}
+} // namespace
+
 template <typename T>
 class TracedOrdinal {
 public:
-    static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+    static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
+                          std::is_same<std::chrono::nanoseconds, T>(),
                   "Type is not supported. Please test it with systrace before adding "
                   "it to the list.");
 
@@ -37,7 +57,9 @@
         trace();
     }
 
-    operator T() const { return mData; }
+    T get() const { return mData; }
+
+    operator T() const { return get(); }
 
     TracedOrdinal& operator=(T other) {
         mData = other;
@@ -57,12 +79,12 @@
         }
 
         if (!std::signbit(mData)) {
-            ATRACE_INT64(mName.c_str(), int64_t(mData));
+            ATRACE_INT64(mName.c_str(), to_int64(mData));
             if (mHasGoneNegative) {
                 ATRACE_INT64(mNameNegative.c_str(), 0);
             }
         } else {
-            ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+            ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
             ATRACE_INT64(mName.c_str(), 0);
         }
     }
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 0cdff8f..ca24493 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -154,6 +154,9 @@
 
 status_t TransactionCompletedThread::finalizePendingCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles) {
+    if (handles.empty()) {
+        return NO_ERROR;
+    }
     std::lock_guard lock(mMutex);
     if (!mRunning) {
         ALOGE("cannot add presented callback handle because the callback thread isn't running");
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index d03cb7b..e2a28a2 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "liblayers_proto",
     export_include_dirs: ["include"],
 
@@ -33,7 +33,6 @@
         "-Wno-old-style-cast",
         "-Wno-undef",
     ],
-
 }
 
 java_library_static {
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 8fce0c9..aef670d 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -115,6 +115,7 @@
     }
     layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
     layer.shadowRadius = layerProto.shadow_radius();
+    layer.ownerUid = layerProto.owner_uid();
     return layer;
 }
 
@@ -276,7 +277,7 @@
 
 std::string LayerProtoParser::Layer::to_string() const {
     std::string result;
-    StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+    StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
     result.append(transparentRegion.to_string("TransparentRegion").c_str());
     result.append(visibleRegion.to_string("VisibleRegion").c_str());
     result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 52b9165..c48354f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
         LayerMetadata metadata;
         LayerProtoParser::FloatRect cornerRadiusCrop;
         float shadowRadius;
+        uid_t ownerUid;
 
         std::string to_string() const;
     };
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 7f1f542..9f25674 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -123,6 +123,11 @@
   bool is_relative_of = 51;
   // Layer's background blur radius in pixels.
   int32 background_blur_radius = 52;
+
+  uint32 owner_uid = 53;
+
+  // Regions of a layer, where blur should be applied.
+  repeated BlurRegion blur_regions = 54;
 }
 
 message PositionProto {
@@ -191,20 +196,34 @@
 
     uint32 surface_inset = 5;
     bool visible = 6;
-    bool can_receive_keys = 7;
-    bool has_focus = 8;
+    bool can_receive_keys = 7  [deprecated=true];
+    bool focusable = 8;
     bool has_wallpaper = 9;
 
     float global_scale_factor = 10;
-    float window_x_scale = 11;
-    float window_y_scale = 12;
+    float window_x_scale = 11 [deprecated=true];
+    float window_y_scale = 12 [deprecated=true];
 
     uint32 crop_layer_id = 13;
     bool replace_touchable_region_with_crop = 14;
     RectProto touchable_region_crop = 15;
+    TransformProto transform = 16;
 }
 
 message ColorTransformProto {
   // This will be a 4x4 matrix of float values
   repeated float val = 1;
 }
+
+message BlurRegion {
+    uint32 blur_radius = 1;
+    uint32 corner_radius_tl = 2;
+    uint32 corner_radius_tr = 3;
+    uint32 corner_radius_bl = 4;
+    float corner_radius_br = 5;
+    float alpha = 6;
+    int32 left = 7;
+    int32 top = 8;
+    int32 right = 9;
+    int32 bottom = 10;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index acf621e..990f3cf 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -42,7 +42,7 @@
 /* one window manager trace entry. */
 message LayersTraceProto {
     /* required: elapsed realtime in nanos since boot of when this entry was logged */
-    optional fixed64 elapsed_realtime_nanos = 1;
+    optional sfixed64 elapsed_realtime_nanos = 1;
 
     /* where the trace originated */
     optional string where = 2;
@@ -56,5 +56,5 @@
     optional bool excludes_composition_state = 5;
 
     /* Number of missed entries since the last entry was recorded. */
-    optional int32 missed_entries = 6;
+    optional uint32 missed_entries = 6;
 }
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index cfc301b..421484f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -72,7 +72,7 @@
 prop {
     api_name: "max_graphics_width"
     type: Integer
-    scope: System
+    scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.max_graphics_width"
 }
@@ -82,7 +82,7 @@
 prop {
     api_name: "max_graphics_height"
     type: Integer
-    scope: System
+    scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.max_graphics_height"
 }
@@ -328,7 +328,7 @@
 prop {
     api_name: "refresh_rate_switching"
     type: Boolean
-    scope: System
+    scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.refresh_rate_switching"
     deprecated: true
@@ -435,3 +435,13 @@
     access: Readonly
     prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
 }
+
+
+# Updates the DeviceProductInfo when a hoplug reconnect event is processed
+prop {
+    api_name: "update_device_product_info_on_hotplug_reconnect"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index ba60a7d..da66ece 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -124,6 +124,10 @@
     prop_name: "ro.surface_flinger.supports_background_blur"
   }
   prop {
+    api_name: "update_device_product_info_on_hotplug_reconnect"
+    prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+  }
+  prop {
     api_name: "use_color_management"
     prop_name: "ro.surface_flinger.use_color_management"
   }
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index b66e56e..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -36,6 +36,11 @@
     prop_name: "ro.surface_flinger.display_primary_white"
   }
   prop {
+    api_name: "display_update_imminent_timeout_ms"
+    type: Integer
+    prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
+  }
+  prop {
     api_name: "enable_protected_contents"
     prop_name: "ro.surface_flinger.protected_contents"
   }
@@ -57,6 +62,16 @@
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
   }
   prop {
+    api_name: "max_graphics_height"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_height"
+  }
+  prop {
+    api_name: "max_graphics_width"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_width"
+  }
+  prop {
     api_name: "max_virtual_display_dimension"
     type: Long
     prop_name: "ro.surface_flinger.max_virtual_display_dimension"
@@ -73,6 +88,11 @@
     enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
   }
   prop {
+    api_name: "refresh_rate_switching"
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+    deprecated: true
+  }
+  prop {
     api_name: "running_without_sync_framework"
     prop_name: "ro.surface_flinger.running_without_sync_framework"
   }
@@ -100,16 +120,29 @@
     prop_name: "ro.surface_flinger.support_kernel_idle_timer"
   }
   prop {
+    api_name: "supports_background_blur"
+    prop_name: "ro.surface_flinger.supports_background_blur"
+  }
+  prop {
     api_name: "use_color_management"
     prop_name: "ro.surface_flinger.use_color_management"
   }
   prop {
+    api_name: "use_content_detection_for_refresh_rate"
+    prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+  }
+  prop {
     api_name: "use_context_priority"
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
+    api_name: "use_frame_rate_api"
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+  }
+  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
+    deprecated: true
   }
   prop {
     api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index fe2af80..e8b24b4 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,18 +21,22 @@
         "CommonTypes_test.cpp",
         "Credentials_test.cpp",
         "DereferenceSurfaceControl_test.cpp",
+        "DetachChildren_test.cpp",
         "DisplayConfigs_test.cpp",
         "EffectLayer_test.cpp",
         "InvalidHandles_test.cpp",
         "LayerCallback_test.cpp",
         "LayerRenderTypeTransaction_test.cpp",
+        "LayerState_test.cpp",
         "LayerTransaction_test.cpp",
         "LayerTypeAndRenderTypeTransaction_test.cpp",
         "LayerTypeTransaction_test.cpp",
         "LayerUpdate_test.cpp",
         "MirrorLayer_test.cpp",
         "MultiDisplayLayerBounds_test.cpp",
+        "RefreshRateOverlay_test.cpp",
         "RelativeZ_test.cpp",
+        "ScreenCapture_test.cpp",
         "SetFrameRate_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
@@ -42,18 +46,19 @@
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
         "libtrace_proto",
+        "liblayers_proto",
+        "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-unstable-ndk_platform",
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.composer@2.1",
         "libandroid",
+        "libbase",
         "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
         "libgui",
-        "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libprotobuf-cpp-full",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 293738c..4868c12 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -88,7 +88,7 @@
     sp<Surface> mSurface;
 };
 
-/* Used to generate valid fences. It is not possible to create a dummy sync
+/* Used to generate valid fences. It is not possible to create a placeholder sync
  * fence for testing. Egl can generate buffers along with a valid fence.
  * The buffer cannot be guaranteed to be the same format across all devices so
  * a CPU filled buffer is used instead. The Egl fence is used along with the
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index c136708..9302463 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gtest/gtest.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
@@ -7,8 +27,8 @@
 #include <private/gui/ComposerService.h>
 #include <ui/DisplayConfig.h>
 #include <utils/String8.h>
-
 #include <functional>
+#include "utils/ScreenshotUtils.h"
 
 namespace android {
 
@@ -18,7 +38,6 @@
 namespace {
 const String8 DISPLAY_NAME("Credentials Display Test");
 const String8 SURFACE_NAME("Test Surface Name");
-const float FRAME_SCALE = 1.0f;
 } // namespace
 
 /**
@@ -79,26 +98,6 @@
                   t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
     }
 
-    void setupVirtualDisplay() {
-        mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
-        const ssize_t displayWidth = 100;
-        const ssize_t displayHeight = 100;
-
-        // Background surface
-        mVirtualSurfaceControl =
-                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
-                                               PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
-        ASSERT_TRUE(mVirtualSurfaceControl->isValid());
-
-        Transaction t;
-        t.setDisplayLayerStack(mVirtualDisplay, 0);
-        ASSERT_EQ(NO_ERROR,
-                  t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
-                          .show(mVirtualSurfaceControl)
-                          .apply());
-    }
-
     /**
      * Sets UID to imitate Graphic's process.
      */
@@ -146,6 +145,10 @@
         // Check as a non-supported user.
         setBinUID();
         ASSERT_EQ(unprivilegedValue, condition());
+
+        // Check as shell since shell has some additional permissions
+        seteuid(AID_SHELL);
+        ASSERT_EQ(unprivilegedValue, condition());
     }
 };
 
@@ -188,7 +191,7 @@
     Vector<DisplayConfig> configs;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
 
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
+    ASSERT_TRUE(SurfaceComposerClient::getActiveConfig(display) >= 0);
 
     ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
               SurfaceComposerClient::getActiveColorMode(display));
@@ -215,18 +218,21 @@
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     int32_t defaultConfig;
+    bool allowGroupSwitching;
     float primaryFpsMin;
     float primaryFpsMax;
     float appRequestFpsMin;
     float appRequestFpsMax;
     status_t res =
             SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
+                                                                &allowGroupSwitching,
                                                                 &primaryFpsMin, &primaryFpsMax,
                                                                 &appRequestFpsMin,
                                                                 &appRequestFpsMax);
     ASSERT_EQ(res, NO_ERROR);
     std::function<status_t()> condition = [=]() {
         return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
+                                                                   allowGroupSwitching,
                                                                    primaryFpsMin, primaryFpsMax,
                                                                    appRequestFpsMin,
                                                                    appRequestFpsMax);
@@ -243,11 +249,31 @@
 }
 
 TEST_F(CredentialsTest, CreateDisplayTest) {
+    // Only graphics and system processes can create a secure display.
     std::function<bool()> condition = [=]() {
         sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
         return testDisplay.get() != nullptr;
     };
-    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+
+    // Check with root.
+    seteuid(AID_ROOT);
+    ASSERT_FALSE(condition());
+
+    // Check as a Graphics user.
+    setGraphicsUID();
+    ASSERT_TRUE(condition());
+
+    // Check as a system user.
+    setSystemUID();
+    ASSERT_TRUE(condition());
+
+    // Check as a non-supported user.
+    setBinUID();
+    ASSERT_FALSE(condition());
+
+    // Check as shell since shell has some additional permissions
+    seteuid(AID_SHELL);
+    ASSERT_FALSE(condition());
 
     condition = [=]() {
         sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
@@ -260,9 +286,10 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
-        return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
-                                         ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
-                                         0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer);
+        DisplayCaptureArgs captureArgs;
+        captureArgs.displayToken = display;
+        ScreenCaptureResults captureResults;
+        return ScreenCapture::captureDisplay(captureArgs, captureResults);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -271,10 +298,12 @@
     setupBackgroundSurface();
     sp<GraphicBuffer> outBuffer;
     std::function<status_t()> condition = [=]() {
-        sp<GraphicBuffer> outBuffer;
-        return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
-                                               ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
-                                               Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
+        LayerCaptureArgs captureArgs;
+        captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+        captureArgs.sourceCrop = {0, 0, 1, 1};
+
+        ScreenCaptureResults captureResults;
+        return ScreenCapture::captureLayers(captureArgs, captureResults);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp
new file mode 100644
index 0000000..9c7b1fc
--- /dev/null
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class DetachChildren : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+
+        mMainSurface = createLayer(String8("Main Test Surface"), mMainSurfaceBounds.width(),
+                                   mMainSurfaceBounds.height(), 0, mBlackBgSurface.get());
+
+        ASSERT_TRUE(mMainSurface != nullptr);
+        ASSERT_TRUE(mMainSurface->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mMainSurface, mMainSurfaceColor);
+
+        asTransaction([&](Transaction& t) {
+            t.setLayer(mMainSurface, INT32_MAX - 1)
+                    .setPosition(mMainSurface, mMainSurfaceBounds.left, mMainSurfaceBounds.top)
+                    .show(mMainSurface);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mMainSurface = 0;
+    }
+
+    sp<SurfaceControl> mMainSurface;
+    Color mMainSurfaceColor = {195, 63, 63, 255};
+    Rect mMainSurfaceBounds = Rect(64, 64, 128, 128);
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(DetachChildren, RelativesAreNotDetached) {
+    Color relativeColor = {10, 10, 10, 255};
+    Rect relBounds = Rect(64, 64, 74, 74);
+
+    sp<SurfaceControl> relative =
+            createLayer(String8("relativeTestSurface"), relBounds.width(), relBounds.height(), 0);
+    TransactionUtils::fillSurfaceRGBA8(relative, relativeColor);
+
+    Transaction{}
+            .setRelativeLayer(relative, mMainSurface, 1)
+            .setPosition(relative, relBounds.left, relBounds.top)
+            .apply();
+
+    {
+        // The relative should be on top of the FG control.
+        mCapture = screenshot();
+        mCapture->expectColor(relBounds, relativeColor);
+    }
+    Transaction{}.detachChildren(mMainSurface).apply();
+
+    {
+        // Nothing should change at this point.
+        mCapture = screenshot();
+        mCapture->expectColor(relBounds, relativeColor);
+    }
+
+    Transaction{}.hide(relative).apply();
+
+    {
+        // Ensure that the relative was actually hidden, rather than
+        // being left in the detached but visible state.
+        mCapture = screenshot();
+        mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+    }
+}
+
+TEST_F(DetachChildren, DetachChildrenSameClient) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+    sp<SurfaceControl> child = createLayer(String8("Child surface"), childBounds.width(),
+                                           childBounds.height(), 0, mMainSurface.get());
+    ASSERT_TRUE(child->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(child, childColor);
+
+    asTransaction([&](Transaction& t) {
+        t.show(child);
+        t.setPosition(child, childBounds.left - mMainSurfaceBounds.left,
+                      childBounds.top - mMainSurfaceBounds.top);
+    });
+
+    {
+        mCapture = screenshot();
+        // Expect main color around the child surface
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+    asTransaction([&](Transaction& t) { t.hide(child); });
+
+    // Since the child has the same client as the parent, it will not get
+    // detached and will be hidden.
+    {
+        mCapture = screenshot();
+        mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+    }
+}
+
+TEST_F(DetachChildren, DetachChildrenDifferentClient) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+                          childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+    asTransaction([&](Transaction& t) {
+        t.show(childNewClient);
+        t.setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+                      childBounds.top - mMainSurfaceBounds.top);
+    });
+
+    {
+        mCapture = screenshot();
+        // Expect main color around the child surface
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+    asTransaction([&](Transaction& t) { t.hide(childNewClient); });
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+}
+
+TEST_F(DetachChildren, DetachChildrenThenAttach) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+                          childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+    Transaction()
+            .show(childNewClient)
+            .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+                         childBounds.top - mMainSurfaceBounds.top)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        // Expect main color around the child surface
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    Transaction().detachChildren(mMainSurface).apply();
+    Transaction().hide(childNewClient).apply();
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    Color newParentColor = Color::RED;
+    Rect newParentBounds = Rect(20, 20, 52, 52);
+    sp<SurfaceControl> newParentSurface =
+            createLayer(String8("New Parent Surface"), newParentBounds.width(),
+                        newParentBounds.height(), 0);
+    TransactionUtils::fillSurfaceRGBA8(newParentSurface, newParentColor);
+    Transaction()
+            .setLayer(newParentSurface, INT32_MAX - 1)
+            .show(newParentSurface)
+            .setPosition(newParentSurface, newParentBounds.left, newParentBounds.top)
+            .reparent(childNewClient, newParentSurface)
+            .apply();
+    {
+        mCapture = screenshot();
+        // Child is now hidden.
+        mCapture->expectColor(newParentBounds, newParentColor);
+    }
+}
+
+TEST_F(DetachChildren, DetachChildrenWithDeferredTransaction) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+                          childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+    Transaction()
+            .show(childNewClient)
+            .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+                         childBounds.top - mMainSurfaceBounds.top)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    Transaction()
+            .deferTransactionUntil_legacy(childNewClient, mMainSurface,
+                                          mMainSurface->getSurface()->getNextFrameNumber())
+            .apply();
+    Transaction().detachChildren(mMainSurface).apply();
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED,
+                                                      mMainSurfaceBounds.width(),
+                                                      mMainSurfaceBounds.height()));
+
+    // BufferLayer can still dequeue buffers even though there's a detached layer with a
+    // deferred transaction.
+    {
+        SCOPED_TRACE("new buffer");
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, Color::RED);
+        mCapture->expectColor(childBounds, childColor);
+    }
+}
+
+/**
+ * Tests that a deferring transaction on an already detached layer will be dropped gracefully and
+ * allow the barrier layer to dequeue buffers.
+ *
+ * Fixes b/150924737 - buffer cannot be latched because it waits for a detached layer
+ * to commit its pending states.
+ */
+TEST_F(DetachChildren, DeferredTransactionOnDetachedChildren) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+                          childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+    Transaction()
+            .show(childNewClient)
+            .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+                         childBounds.top - mMainSurfaceBounds.top)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    Transaction().detachChildren(mMainSurface).apply();
+    Transaction()
+            .setCrop_legacy(childNewClient, {0, 0, childBounds.width(), childBounds.height()})
+            .deferTransactionUntil_legacy(childNewClient, mMainSurface,
+                                          mMainSurface->getSurface()->getNextFrameNumber())
+            .apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED,
+                                                      mMainSurfaceBounds.width(),
+                                                      mMainSurfaceBounds.height()));
+
+    // BufferLayer can still dequeue buffers even though there's a detached layer with a
+    // deferred transaction.
+    {
+        SCOPED_TRACE("new buffer");
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, Color::RED);
+        mCapture->expectColor(childBounds, childColor);
+    }
+}
+
+TEST_F(DetachChildren, ReparentParentLayerOfDetachedChildren) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 94, 94);
+    Color grandchildColor = Color::RED;
+    Rect grandchildBounds = Rect(80, 80, 90, 90);
+
+    sp<SurfaceComposerClient> newClient1 = new SurfaceComposerClient;
+    sp<SurfaceComposerClient> newClient2 = new SurfaceComposerClient;
+
+    sp<SurfaceControl> childSurface =
+            createSurface(newClient1, "Child surface", childBounds.width(), childBounds.height(),
+                          PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    sp<SurfaceControl> grandchildSurface =
+            createSurface(newClient2, "Grandchild Surface", grandchildBounds.width(),
+                          grandchildBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, childSurface.get());
+
+    TransactionUtils::fillSurfaceRGBA8(childSurface, childColor);
+    TransactionUtils::fillSurfaceRGBA8(grandchildSurface, grandchildColor);
+
+    Transaction()
+            .show(childSurface)
+            .show(grandchildSurface)
+            .setPosition(childSurface, childBounds.left - mMainSurfaceBounds.left,
+                         childBounds.top - mMainSurfaceBounds.top)
+            .setPosition(grandchildSurface, grandchildBounds.left - childBounds.left,
+                         grandchildBounds.top - childBounds.top)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectBorder(grandchildBounds, childColor);
+        mCapture->expectColor(grandchildBounds, grandchildColor);
+    }
+
+    Transaction().detachChildren(childSurface).apply();
+
+    // Remove main surface offscreen
+    Transaction().reparent(mMainSurface, nullptr).apply();
+    {
+        mCapture = screenshot();
+        mCapture->expectColor(mMainSurfaceBounds, Color::BLACK);
+    }
+
+    Transaction().reparent(mMainSurface, mBlackBgSurface).apply();
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectBorder(grandchildBounds, childColor);
+        mCapture->expectColor(grandchildBounds, grandchildColor);
+    }
+
+    Transaction().hide(grandchildSurface).apply();
+
+    // grandchild is still detached so it will not hide
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectBorder(grandchildBounds, childColor);
+        mCapture->expectColor(grandchildBounds, grandchildColor);
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index debfe83..3a8b40f 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -14,16 +14,18 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
 
-#include <thread>
-#include "LayerTransactionTest.h"
+#include "utils/TransactionUtils.h"
+
 namespace android {
 
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
 ::testing::Environment* const binderEnv =
         ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
 
@@ -31,32 +33,54 @@
  * Test class for setting display configs and passing around refresh rate ranges.
  */
 class RefreshRateRangeTest : public ::testing::Test {
+private:
+    int32_t initialDefaultConfig;
+    bool initialAllowGroupSwitching;
+    float initialPrimaryMin;
+    float initialPrimaryMax;
+    float initialAppRequestMin;
+    float initialAppRequestMax;
+
 protected:
-    void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+    void SetUp() override {
+        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+        status_t res =
+                SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                    &initialDefaultConfig,
+                                                                    &initialAllowGroupSwitching,
+                                                                    &initialPrimaryMin,
+                                                                    &initialPrimaryMax,
+                                                                    &initialAppRequestMin,
+                                                                    &initialAppRequestMax);
+        ASSERT_EQ(res, NO_ERROR);
+    }
+
+    void TearDown() override {
+        status_t res =
+                SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                    initialDefaultConfig,
+                                                                    initialAllowGroupSwitching,
+                                                                    initialPrimaryMin,
+                                                                    initialPrimaryMax,
+                                                                    initialAppRequestMin,
+                                                                    initialAppRequestMax);
+        ASSERT_EQ(res, NO_ERROR);
+    }
+
+    void testSetAllowGroupSwitching(bool allowGroupSwitching);
 
     sp<IBinder> mDisplayToken;
 };
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
-    int32_t initialDefaultConfig;
-    float initialPrimaryMin;
-    float initialPrimaryMax;
-    float initialAppRequestMin;
-    float initialAppRequestMax;
-    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
-                                                                       &initialDefaultConfig,
-                                                                       &initialPrimaryMin,
-                                                                       &initialPrimaryMax,
-                                                                       &initialAppRequestMin,
-                                                                       &initialAppRequestMax);
-    ASSERT_EQ(res, NO_ERROR);
-
     Vector<DisplayConfig> configs;
-    res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+    status_t res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
     ASSERT_EQ(res, NO_ERROR);
+    ASSERT_GT(configs.size(), 0);
 
     for (size_t i = 0; i < configs.size(); i++) {
-        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                  static_cast<int32_t>(i), false,
                                                                   configs[i].refreshRate,
                                                                   configs[i].refreshRate,
                                                                   configs[i].refreshRate,
@@ -64,31 +88,58 @@
         ASSERT_EQ(res, NO_ERROR);
 
         int defaultConfig;
+        bool allowGroupSwitching;
         float primaryRefreshRateMin;
         float primaryRefreshRateMax;
         float appRequestRefreshRateMin;
         float appRequestRefreshRateMax;
         res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+                                                                  &allowGroupSwitching,
                                                                   &primaryRefreshRateMin,
                                                                   &primaryRefreshRateMax,
                                                                   &appRequestRefreshRateMin,
                                                                   &appRequestRefreshRateMax);
         ASSERT_EQ(res, NO_ERROR);
         ASSERT_EQ(defaultConfig, i);
+        ASSERT_EQ(allowGroupSwitching, false);
         ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate);
         ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate);
         ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate);
         ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate);
     }
+}
 
-    res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
-                                                              initialPrimaryMin, initialPrimaryMax,
-                                                              initialAppRequestMin,
-                                                              initialAppRequestMax);
+void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
+    status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 0,
+                                                                       allowGroupSwitching, 0.f,
+                                                                       90.f, 0.f, 90.f);
     ASSERT_EQ(res, NO_ERROR);
+    int defaultConfig;
+    bool newAllowGroupSwitching;
+    float primaryRefreshRateMin;
+    float primaryRefreshRateMax;
+    float appRequestRefreshRateMin;
+    float appRequestRefreshRateMax;
+
+    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+                                                              &newAllowGroupSwitching,
+                                                              &primaryRefreshRateMin,
+                                                              &primaryRefreshRateMax,
+                                                              &appRequestRefreshRateMin,
+                                                              &appRequestRefreshRateMax);
+    ASSERT_EQ(res, NO_ERROR);
+    ASSERT_EQ(defaultConfig, 0);
+    ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
+    ASSERT_EQ(primaryRefreshRateMin, 0.f);
+    ASSERT_EQ(primaryRefreshRateMax, 90.f);
+    ASSERT_EQ(appRequestRefreshRateMin, 0.f);
+    ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+}
+
+TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
+    testSetAllowGroupSwitching(true);
+    testSetAllowGroupSwitching(false);
+    testSetAllowGroupSwitching(true);
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 3dca391..fafb49e 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -51,7 +51,7 @@
     sp<SurfaceControl> effectLayer =
             mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
                                    PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
-                                   mParentLayer.get());
+                                   mParentLayer->getHandle());
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
@@ -72,7 +72,7 @@
                                    PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceEffect |
                                            ISurfaceComposerClient::eNoColorFill,
-                                   mParentLayer.get());
+                                   mParentLayer->getHandle());
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
@@ -93,7 +93,7 @@
                                    PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceEffect |
                                            ISurfaceComposerClient::eNoColorFill,
-                                   mParentLayer.get());
+                                   mParentLayer->getHandle());
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 42d1f5a..152d2d2 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <binder/Binder.h>
 
 #include <gtest/gtest.h>
@@ -22,6 +26,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
 #include <ui/Rect.h>
+#include "utils/ScreenshotUtils.h"
 
 namespace android {
 namespace {
@@ -51,16 +56,16 @@
     auto notSc = makeNotSurfaceControl();
     ASSERT_EQ(nullptr,
               mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
-                                  notSc.get())
+                                  notSc->getHandle())
                       .get());
 }
 
 TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<GraphicBuffer> outBuffer;
+    LayerCaptureArgs args;
+    args.layerHandle = mNotSc->getHandle();
 
-    ASSERT_EQ(NAME_NOT_FOUND,
-              sf->captureLayers(mNotSc->getHandle(), &outBuffer, Rect::EMPTY_RECT, 1.0f));
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 83e5060..52e1a4d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -179,19 +179,6 @@
     }
 }
 
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
-    Transaction()
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
 TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
     uint32_t transformHint = ui::Transform::ROT_INVALID;
     sp<SurfaceControl> layer;
@@ -211,16 +198,13 @@
 
     switch (layerType) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            Transaction()
-                    .setPosition(layerG, 16, 16)
-                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
-                    .apply();
+            Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             Transaction()
                     .setFrame(layerR, Rect(0, 0, 32, 32))
                     .setFrame(layerG, Rect(16, 16, 48, 48))
-                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
+                    .setRelativeLayer(layerG, layerR, 1)
                     .apply();
             break;
         default:
@@ -233,7 +217,7 @@
         shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
     }
 
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+    Transaction().setRelativeLayer(layerG, layerR, -1).apply();
     {
         SCOPED_TRACE("layerG below");
         auto shot = getScreenCapture();
@@ -266,7 +250,7 @@
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
             Transaction()
                     .setPosition(layerG, 8, 8)
-                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setRelativeLayer(layerG, layerR, 3)
                     .setPosition(layerB, 16, 16)
                     .setLayer(layerB, mLayerZBase + 2)
                     .apply();
@@ -275,7 +259,7 @@
             Transaction()
                     .setFrame(layerR, Rect(0, 0, 32, 32))
                     .setFrame(layerG, Rect(8, 8, 40, 40))
-                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setRelativeLayer(layerG, layerR, 3)
                     .setFrame(layerB, Rect(16, 16, 48, 48))
                     .setLayer(layerB, mLayerZBase + 2)
                     .apply();
@@ -303,7 +287,7 @@
     }
 
     // layerR = 4, layerG = layerR - 3, layerB = 2
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+    Transaction().setRelativeLayer(layerG, layerR, -3).apply();
     {
         SCOPED_TRACE("layerB < (layerG < layerR)");
         auto shot = getScreenCapture();
@@ -810,7 +794,7 @@
     // channel) should be less than one
     const uint8_t tolerance = 1;
     Transaction()
-            .reparent(colorLayer, parentLayer->getHandle())
+            .reparent(colorLayer, parentLayer)
             .setColor(colorLayer, color)
             .setAlpha(parentLayer, alpha)
             .setLayer(parentLayer, mLayerZBase + 1)
@@ -953,40 +937,6 @@
     }
 }
 
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    // XXX SCALE_CROP is not respected; calling setSize and
-    // setOverrideScalingMode in separate transactions does not work
-    // (b/69315456)
-    Transaction()
-            .setSize(layer, 64, 16)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    {
-        SCOPED_TRACE("SCALE_TO_WINDOW");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE, true /* filtered */);
-    }
-}
-
 TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
@@ -1225,7 +1175,7 @@
             child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
 
-    Transaction().reparent(child, parent->getHandle()).apply();
+    Transaction().reparent(child, parent).apply();
 
     // A layer will default to the frame of its parent
     auto shot = getScreenCapture();
@@ -1242,7 +1192,7 @@
             child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
 
-    Transaction().reparent(child, parent->getHandle()).apply();
+    Transaction().reparent(child, parent).apply();
 
     // A layer will default to the frame of its parent
     auto shot = getScreenCapture();
@@ -1272,7 +1222,7 @@
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    Transaction().reparent(child, parent->getHandle()).apply();
+    Transaction().reparent(child, parent).apply();
 
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
     Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
new file mode 100644
index 0000000..e66df4a
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+
+#include <gui/LayerState.h>
+
+namespace android {
+namespace test {
+
+TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
+    DisplayCaptureArgs args;
+    args.pixelFormat = ui::PixelFormat::RGB_565;
+    args.sourceCrop = Rect(0, 0, 500, 200);
+    args.frameScale = 2;
+    args.captureSecureLayers = true;
+    args.displayToken = new BBinder();
+    args.width = 10;
+    args.height = 20;
+    args.useIdentityTransform = true;
+
+    Parcel p;
+    args.write(p);
+    p.setDataPosition(0);
+
+    DisplayCaptureArgs args2;
+    args2.read(p);
+
+    ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+    ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+    ASSERT_EQ(args.frameScale, args2.frameScale);
+    ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+    ASSERT_EQ(args.displayToken, args2.displayToken);
+    ASSERT_EQ(args.width, args2.width);
+    ASSERT_EQ(args.height, args2.height);
+    ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
+}
+
+TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
+    LayerCaptureArgs args;
+    args.pixelFormat = ui::PixelFormat::RGB_565;
+    args.sourceCrop = Rect(0, 0, 500, 200);
+    args.frameScale = 2;
+    args.captureSecureLayers = true;
+    args.layerHandle = new BBinder();
+    args.excludeHandles = {new BBinder(), new BBinder()};
+    args.childrenOnly = false;
+
+    Parcel p;
+    args.write(p);
+    p.setDataPosition(0);
+
+    LayerCaptureArgs args2;
+    args2.read(p);
+
+    ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+    ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+    ASSERT_EQ(args.frameScale, args2.frameScale);
+    ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+    ASSERT_EQ(args.layerHandle, args2.layerHandle);
+    ASSERT_EQ(args.excludeHandles, args2.excludeHandles);
+    ASSERT_EQ(args.childrenOnly, args2.childrenOnly);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResults) {
+    ScreenCaptureResults results;
+    results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0);
+    results.capturedSecureLayers = true;
+    results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
+    results.result = BAD_VALUE;
+
+    Parcel p;
+    results.write(p);
+    p.setDataPosition(0);
+
+    ScreenCaptureResults results2;
+    results2.read(p);
+
+    // GraphicBuffer object is reallocated so compare the data in the graphic buffer
+    // rather than the object itself
+    ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth());
+    ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight());
+    ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
+    ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
+    ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
+    ASSERT_EQ(results.result, results2.result);
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index f3e11d8..da71dad 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -16,6 +16,10 @@
 
 #pragma once
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gtest/gtest.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
@@ -40,6 +44,8 @@
 
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+
+        mCaptureArgs.displayToken = mDisplay;
     }
 
     virtual void TearDown() {
@@ -73,8 +79,9 @@
                                              PixelFormat format, uint32_t flags,
                                              SurfaceControl* parent = nullptr,
                                              uint32_t* outTransformHint = nullptr) {
-        auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
-                                           LayerMetadata(), outTransformHint);
+        sp<IBinder> parentHandle = (parent) ? parent->getHandle() : nullptr;
+        auto layer = client->createSurface(String8(name), width, height, format, flags,
+                                           parentHandle, LayerMetadata(), outTransformHint);
         EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
         return layer;
     }
@@ -249,6 +256,9 @@
     sp<SurfaceControl> mBlackBgSurface;
     bool mColorManagementUsed;
 
+    DisplayCaptureArgs mCaptureArgs;
+    ScreenCaptureResults mCaptureResults;
+
 private:
     void SetUpDisplay() {
         mDisplay = mClient->getInternalDisplayToken();
@@ -294,3 +304,6 @@
 };
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 97cba63..ef992d6 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -18,7 +18,6 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
-#include <private/android_filesystem_config.h>
 #include <thread>
 #include "LayerTransactionTest.h"
 
@@ -26,43 +25,6 @@
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
 
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
-    sp<GraphicBuffer> outBuffer;
-    Transaction()
-            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
-            .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    UIDFaker f(AID_SYSTEM);
-
-    // By default the system can capture screenshots with secure layers but they
-    // will be blacked out
-    ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    {
-        SCOPED_TRACE("as system");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
-    // to receive them...we are expected to take care with the results.
-    bool outCapturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
-                                      ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
-                                      0, false, ui::ROTATION_0, true));
-    ASSERT_EQ(true, outCapturedSecureLayers);
-    ScreenCapture sc(outBuffer);
-    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
 TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
@@ -88,7 +50,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-    Transaction().reparent(layer, layer->getHandle()).apply();
+    Transaction().reparent(layer, layer).apply();
 
     {
         // We expect the transaction to be silently dropped, but for SurfaceFlinger
@@ -118,10 +80,9 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
     const auto producer = layer->getIGraphicBufferProducer();
-    const sp<IProducerListener> dummyListener(new DummyProducerListener);
+    const sp<IProducerListener> stubListener(new StubProducerListener);
     IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-    ASSERT_EQ(OK,
-              producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+    ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
 
     std::map<int, sp<GraphicBuffer>> slotMap;
     auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 7d4314f..c57ad43 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -87,10 +87,7 @@
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
-    Transaction()
-            .setPosition(layerG, 16, 16)
-            .setRelativeLayer(layerG, layerR->getHandle(), 1)
-            .apply();
+    Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
 
     Transaction().reparent(layerG, nullptr).apply();
 
@@ -154,10 +151,7 @@
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
-    Transaction()
-            .reparent(layerR, parent->getHandle())
-            .reparent(layerG, parent->getHandle())
-            .apply();
+    Transaction().reparent(layerR, parent).reparent(layerG, parent).apply();
     Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
     {
         SCOPED_TRACE("layerR");
@@ -241,7 +235,7 @@
     auto transaction = Transaction()
                                .setCornerRadius(parent, cornerRadius)
                                .setCrop_legacy(parent, Rect(0, 0, size, size))
-                               .reparent(child, parent->getHandle())
+                               .reparent(child, parent)
                                .setPosition(child, 0, size)
                                // Rotate by half PI
                                .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
@@ -283,14 +277,14 @@
         Transaction()
                 .setCornerRadius(parent, cornerRadius)
                 .setCrop_legacy(parent, Rect(0, 0, size, size))
-                .reparent(child, parent->getHandle())
+                .reparent(child, parent)
                 .setPosition(child, 0, size / 2)
                 .apply();
     } else {
         Transaction()
                 .setCornerRadius(parent, cornerRadius)
                 .setFrame(parent, Rect(0, 0, size, size))
-                .reparent(child, parent->getHandle())
+                .reparent(child, parent)
                 .setFrame(child, Rect(0, size / 2, size, size))
                 .apply();
     }
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 84780ba..f8a0bc1 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -54,15 +54,17 @@
     ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
 
-    Transaction().reparent(layerB, parent->getHandle()).apply();
+    Transaction().reparent(layerB, parent).apply();
 
     // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
+    Transaction().setRelativeLayer(layerG, layerR, -1).setLayer(layerB, -2).apply();
 
     std::unique_ptr<ScreenCapture> screenshot;
     // only layerB is in this range
-    sp<IBinder> parentHandle = parent->getHandle();
-    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = parent->getHandle();
+    captureArgs.sourceCrop = {0, 0, 32, 32};
+    ScreenCapture::captureLayers(&screenshot, captureArgs);
     screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
 }
 
@@ -86,10 +88,7 @@
             .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
             .apply();
 
-    Transaction()
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .setLayer(childLayer, 1)
-            .apply();
+    Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
 
     {
         SCOPED_TRACE("setLayer above");
@@ -99,10 +98,7 @@
         screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
     }
 
-    Transaction()
-            .setLayer(childLayer, 1)
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .apply();
+    Transaction().setLayer(childLayer, 1).setRelativeLayer(childLayer, parent, -1).apply();
 
     {
         SCOPED_TRACE("setRelative below");
@@ -139,7 +135,7 @@
             .setLayer(relativeParent, mLayerZBase)
             .apply();
 
-    Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+    Transaction().setRelativeLayer(childLayer, relativeParent, 1).apply();
 
     {
         SCOPED_TRACE("setLayer above");
@@ -165,17 +161,21 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
     sp<GraphicBuffer> outBuffer;
     Transaction()
             .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
             .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    DisplayCaptureArgs args;
+    args.displayToken = mDisplay;
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
-    ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
 }
+
 TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index cdd9d92..29473f2 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -103,41 +103,6 @@
     sp<SurfaceControl> mSyncSurfaceControl;
 };
 
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
-    TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
-    waitForPostedBuffers();
-
-    Transaction{}
-            .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
-            .setPosition(relative, 64, 64)
-            .apply();
-
-    {
-        // The relative should be on top of the FG control.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-    Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
-    {
-        // Nothing should change at this point.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-
-    Transaction{}.hide(relative).apply();
-
-    {
-        // Ensure that the relative was actually hidden, rather than
-        // being left in the detached but visible state.
-        ScreenCapture::captureScreen(&sc);
-        sc->expectFGColor(64, 64);
-    }
-}
-
 class GeometryLatchingTest : public LayerUpdateTest {
 protected:
     void EXPECT_INITIAL_STATE(const char* trace) {
@@ -208,13 +173,13 @@
     // set up two deferred transactions on different frames
     asTransaction([&](Transaction& t) {
         t.setAlpha(mFGSurfaceControl, 0.75);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl,
                                        mSyncSurfaceControl->getSurface()->getNextFrameNumber());
     });
 
     asTransaction([&](Transaction& t) {
         t.setPosition(mFGSurfaceControl, 128, 128);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl,
                                        mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
     });
 
@@ -515,9 +480,8 @@
         mCapture->expectFGColor(84, 84);
     }
 
-    asTransaction([&](Transaction& t) {
-        t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
-    });
+    asTransaction(
+            [&](Transaction& t) { t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl); });
 
     {
         mCapture = screenshot();
@@ -551,7 +515,7 @@
         mCapture->expectFGColor(64, 64);
     }
 
-    asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); });
+    asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl); });
 
     {
         SCOPED_TRACE("After reparenting grandchild");
@@ -566,9 +530,7 @@
     TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
 
     // draw grand child behind the foreground surface
-    asTransaction([&](Transaction& t) {
-        t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
-    });
+    asTransaction([&](Transaction& t) { t.setRelativeLayer(mGrandChild, mFGSurfaceControl, -1); });
 
     {
         SCOPED_TRACE("Child visible");
@@ -578,7 +540,7 @@
 
     asTransaction([&](Transaction& t) {
         t.reparent(mChild, nullptr);
-        t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+        t.reparentChildren(mChild, mFGSurfaceControl);
     });
 
     {
@@ -588,174 +550,6 @@
     }
 }
 
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChild); });
-
-    // Since the child has the same client as the parent, it will not get
-    // detached and will be hidden.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectFGColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
-    sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> mChildNewClient =
-            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
-                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(mChildNewClient->isValid());
-
-    TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
-    asTransaction([&](Transaction& t) {
-        t.hide(mChild);
-        t.show(mChildNewClient);
-        t.setPosition(mChildNewClient, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
-    // Nothing should have changed.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectChildColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
-    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> childNewClient =
-            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(childNewClient != nullptr);
-    ASSERT_TRUE(childNewClient->isValid());
-
-    TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
-    Transaction()
-            .hide(mChild)
-            .show(childNewClient)
-            .setPosition(childNewClient, 10, 10)
-            .setPosition(mFGSurfaceControl, 64, 64)
-            .apply();
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    Transaction().detachChildren(mFGSurfaceControl).apply();
-    Transaction().hide(childNewClient).apply();
-
-    // Nothing should have changed.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectChildColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-
-    sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
-    fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
-                   32);
-    Transaction()
-            .setLayer(newParentSurface, INT32_MAX - 1)
-            .show(newParentSurface)
-            .setPosition(newParentSurface, 20, 20)
-            .reparent(childNewClient, newParentSurface->getHandle())
-            .apply();
-    {
-        mCapture = screenshot();
-        // Child is now hidden.
-        mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
-    }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
-    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> childNewClient =
-            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(childNewClient != nullptr);
-    ASSERT_TRUE(childNewClient->isValid());
-
-    TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
-    Transaction()
-            .hide(mChild)
-            .show(childNewClient)
-            .setPosition(childNewClient, 10, 10)
-            .setPosition(mFGSurfaceControl, 64, 64)
-            .apply();
-
-    {
-        mCapture = screenshot();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color{195, 63, 63, 255});
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-
-    Transaction()
-            .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
-                                          mFGSurfaceControl->getSurface()->getNextFrameNumber())
-            .apply();
-    Transaction().detachChildren(mFGSurfaceControl).apply();
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
-    // BufferLayer can still dequeue buffers even though there's a detached layer with a
-    // deferred transaction.
-    {
-        SCOPED_TRACE("new buffer");
-        mCapture = screenshot();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color::RED);
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-}
-
 TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
     asTransaction([&](Transaction& t) {
         t.show(mChild);
@@ -772,7 +566,10 @@
     }
 
     asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        mFGSurfaceControl->getSurface()->setScalingMode(
+            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Resubmit buffer with new scaling mode
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
         // We cause scaling by 2.
         t.setSize(mFGSurfaceControl, 128, 128);
     });
@@ -879,7 +676,10 @@
     }
 
     asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        mFGSurfaceControl->getSurface()->setScalingMode(
+            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Resubmit buffer with new scaling mode
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
         // Set a scaling by 2.
         t.setSize(mFGSurfaceControl, 128, 128);
     });
@@ -911,7 +711,10 @@
 
     // Change the size of the foreground to 128 * 64 so we can test rotation as well.
     asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        mFGSurfaceControl->getSurface()->setScalingMode(
+            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Resubmit buffer with new scaling mode
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
         t.setSize(mFGSurfaceControl, 128, 64);
     });
     sp<Surface> s = mFGSurfaceControl->getSurface();
@@ -942,7 +745,7 @@
 
     // Show the child layer in a deferred transaction
     asTransaction([&](Transaction& t) {
-        t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+        t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl,
                                        mFGSurfaceControl->getSurface()->getNextFrameNumber());
         t.show(mChild);
     });
@@ -979,7 +782,7 @@
         mCapture->expectFGColor(84, 84);
     }
 
-    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl); });
 
     {
         mCapture = screenshot();
@@ -1041,7 +844,7 @@
         mCapture->checkPixel(10, 10, 63, 195, 63);
     }
 
-    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl); });
 
     {
         mCapture = screenshot();
@@ -1072,7 +875,7 @@
 
     Transaction t;
     t.setLayer(relative, INT32_MAX)
-            .setRelativeLayer(mChild, relative->getHandle(), 1)
+            .setRelativeLayer(mChild, relative, 1)
             .setPosition(mFGSurfaceControl, 0, 0)
             .apply(true);
 
@@ -1225,12 +1028,13 @@
 TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
     sp<SurfaceControl> boundlessLayer =
             mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   0 /* flags */, mFGSurfaceControl.get());
+                                   0 /* flags */, mFGSurfaceControl->getHandle());
     ASSERT_TRUE(boundlessLayer != nullptr);
     ASSERT_TRUE(boundlessLayer->isValid());
     sp<SurfaceControl> colorLayer =
             mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayer.get());
+                                   ISurfaceComposerClient::eFXSurfaceEffect,
+                                   boundlessLayer->getHandle());
     ASSERT_TRUE(colorLayer != nullptr);
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
@@ -1287,432 +1091,6 @@
     }
 }
 
-class ScreenCaptureTest : public LayerUpdateTest {
-protected:
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
-    auto bgHandle = mBGSurfaceControl->getHandle();
-    ScreenCapture::captureLayers(&mCapture, bgHandle);
-    mCapture->expectBGColor(0, 0);
-    // Doesn't capture FG layer which is at 64, 64
-    mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    // Captures mFGSurfaceControl layer and its child.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    // Captures mFGSurfaceControl's child
-    ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
-    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .show(child3)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    auto childHandle = child->getHandle();
-
-    // Captures child
-    ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
-    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
-    // Area outside of child's bounds is transparent.
-    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
-    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer above fg layer so should be shown above when computing all layers.
-            .setRelativeLayer(relative, fgHandle, 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer below fg layer but relative to child layer so it should be shown
-            // above child layer.
-            .setLayer(relative, -1)
-            .setRelativeLayer(relative, child->getHandle(), 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
-    // relative value should be taken into account, placing it above child layer.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    // Relative layer is showing on top of child layer
-    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
-    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<GraphicBuffer> outBuffer;
-    Rect sourceCrop(0, 0, 10, 10);
-    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-    ScreenCapture sc(outBuffer);
-
-    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
-    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
-    Rect layerCrop(0, 0, 10, 10);
-    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<GraphicBuffer> outBuffer;
-    Rect sourceCrop = Rect();
-    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-    ScreenCapture sc(outBuffer);
-
-    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
-    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<GraphicBuffer> outBuffer;
-    Rect sourceCrop = Rect();
-
-    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-}
-
-TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<GraphicBuffer> outBuffer;
-    Rect sourceCrop = Rect();
-    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-
-    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
-    SurfaceComposerClient::Transaction().apply(true);
-    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-    ScreenCapture sc(outBuffer);
-    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
-    void SetUp() override {
-        LayerUpdateTest::SetUp();
-
-        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
-                               mFGSurfaceControl.get());
-        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-        SurfaceComposerClient::Transaction().show(mChild).apply(true);
-    }
-
-    void verify(std::function<void()> verifyStartingState) {
-        // Verify starting state before a screenshot is taken.
-        verifyStartingState();
-
-        // Verify child layer does not inherit any of the properties of its
-        // parent when its screenshot is captured.
-        auto fgHandle = mFGSurfaceControl->getHandle();
-        ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-        mCapture->checkPixel(10, 10, 0, 0, 0);
-        mCapture->expectChildColor(0, 0);
-
-        // Verify all assumptions are still true after the screenshot is taken.
-        verifyStartingState();
-    }
-
-    std::unique_ptr<ScreenCapture> mCapture;
-    sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
-    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
-    // Even though the parent is hidden we should still capture the child.
-
-    // Before and after reparenting, verify child is properly hidden
-    // when rendering full-screen.
-    verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-    SurfaceComposerClient::Transaction()
-            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
-            .apply(true);
-
-    // Even though the parent is cropped out we should still capture the child.
-
-    // Before and after reparenting, verify child is cropped by parent.
-    verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
-    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
-    // We should not inherit the parent scaling.
-
-    // Before and after reparenting, verify child is properly scaled.
-    verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
-    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    // Captures mFGSurfaceControl, its child, and the grandchild.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-    mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    auto childHandle = child->getHandle();
-
-    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
-    // Captures only the child layer, and not the parent.
-    ScreenCapture::captureLayers(&mCapture, childHandle);
-    mCapture->expectChildColor(0, 0);
-    mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-    auto childHandle = child->getHandle();
-
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    auto grandchildHandle = grandchild->getHandle();
-
-    // Captures only the grandchild.
-    ScreenCapture::captureLayers(&mCapture, grandchildHandle);
-    mCapture->checkPixel(0, 0, 50, 50, 50);
-    mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    const Rect crop = Rect(0, 0, 30, 30);
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
-    // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
-    // area visible.
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
-    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
-    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
-    auto redLayerHandle = redLayer->getHandle();
-    Transaction().reparent(redLayer, nullptr).apply();
-    redLayer.clear();
-    SurfaceComposerClient::Transaction().apply(true);
-
-    sp<GraphicBuffer> outBuffer;
-
-    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index b49bd54..16826c1 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -68,7 +68,7 @@
 
     // Add mirrorLayer as child of mParentLayer so it's shown on the display
     Transaction()
-            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .reparent(mirrorLayer, mParentLayer)
             .setPosition(mirrorLayer, 500, 500)
             .show(mirrorLayer)
             .apply();
@@ -127,7 +127,7 @@
     }
 
     // Add grandchild layer to offscreen layer
-    Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+    Transaction().reparent(grandchild, mChildLayer).apply();
     {
         SCOPED_TRACE("Added Grandchild Layer");
         auto shot = screenshot();
@@ -138,7 +138,7 @@
     }
 
     // Add child layer
-    Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+    Transaction().reparent(mChildLayer, mParentLayer).apply();
     {
         SCOPED_TRACE("Added Child Layer");
         auto shot = screenshot();
@@ -157,7 +157,7 @@
 
     sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
     Transaction()
-            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .reparent(mirrorLayer, mParentLayer)
             .setPosition(mirrorLayer, 500, 500)
             .show(mirrorLayer)
             .apply();
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 06e8761..db0c56f 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -90,7 +90,7 @@
 };
 
 TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+    createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
     createColorLayer(1 /* layerStack */);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -111,9 +111,9 @@
     // Create a display and set its layer stack to the main display's layer stack so
     // the contents of the main display are mirrored on to the virtual display.
 
-    // Assumption here is that the new mirrored display has the same viewport as the
+    // Assumption here is that the new mirrored display has the same layer stack rect as the
     // primary display that it is mirroring.
-    createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+    createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
     createColorLayer(0 /* layerStack */);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
new file mode 100644
index 0000000..05858bf
--- /dev/null
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
+static constexpr int kRefreshRateOverlayCode = 1034;
+static constexpr int kRefreshRateOverlayEnable = 1;
+static constexpr int kRefreshRateOverlayDisable = 0;
+static constexpr int kRefreshRateOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowRefreshRatePreferenceController
+static_assert(kRefreshRateOverlayCode == 1034);
+static_assert(kRefreshRateOverlayEnable == 1);
+static_assert(kRefreshRateOverlayDisable == 0);
+static_assert(kRefreshRateOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    Parcel request;
+    request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+    request.writeInt32(command);
+    ASSERT_EQ(NO_ERROR,
+              IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+    Parcel reply;
+    sendCommandToSf(kRefreshRateOverlayQuery, reply);
+    return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+    static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+    static constexpr auto kIterations = 10;
+    for (int i = 0; i < kIterations; i++) {
+        if (enabled == isOverlayEnabled()) {
+            return;
+        }
+        std::this_thread::sleep_for(kTimeout / kIterations);
+    }
+}
+
+void toggleOverlay(bool enabled) {
+    if (enabled == isOverlayEnabled()) {
+        return;
+    }
+
+    Parcel reply;
+    const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable;
+    sendCommandToSf(command, reply);
+    waitForOverlay(enabled);
+    ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(RefreshRateOverlayTest, enableOverlay) {
+    toggleOverlay(true);
+}
+
+TEST(RefreshRateOverlayTest, disableOverlay) {
+    toggleOverlay(false);
+}
+
+TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
+    toggleOverlay(true);
+    toggleOverlay(false);
+
+    toggleOverlay(true);
+    toggleOverlay(false);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 1180cac..fde6e6e 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -70,10 +70,7 @@
     sp<SurfaceControl> childLayer =
             createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
 
-    Transaction{}
-            .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
-            .show(childLayer)
-            .apply();
+    Transaction{}.setRelativeLayer(childLayer, mForegroundLayer, 1).show(childLayer).apply();
 
     {
         // The childLayer should be in front of the FG control.
@@ -88,7 +85,7 @@
     // Background layer (RED)
     //   Child layer (WHITE)
     // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+    Transaction{}.reparent(childLayer, mBackgroundLayer).apply();
 
     {
         // The relative z info for child layer should be reset, leaving FG control on top.
@@ -118,7 +115,7 @@
             createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
 
     Transaction{}
-            .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+            .setRelativeLayer(childLevel3, childLevel2b, 1)
             .show(childLevel2a)
             .show(childLevel2b)
             .show(childLevel3)
@@ -140,7 +137,7 @@
     //     child level 2 back (BLUE)
     //       child level 3 (GREEN) (relative to child level 2b)
     //     child level 2 front (BLACK)
-    Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+    Transaction{}.reparent(childLevel1, mForegroundLayer).apply();
 
     {
         // Nothing should change at this point since relative z info was preserved.
@@ -162,7 +159,7 @@
             createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get());
 
     Transaction{}
-            .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1)
+            .setRelativeLayer(childLayer, relativeToLayer, 1)
             .show(childLayer)
             .show(relativeToLayer)
             .apply();
@@ -199,7 +196,7 @@
     // Background layer (RED)
     // Foregroud layer (GREEN)
     //   Child layer (BLUE)
-    Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply();
+    Transaction{}.reparent(childLayer, mForegroundLayer).apply();
 
     {
         // The relative z info for child layer should be reset, leaving the child layer on top.
@@ -207,6 +204,71 @@
         sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
     }
 }
+
+// Preserve the relative z order when a layer is reparented to a layer that's already offscreen
+TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    Color testLayerColor = {255, 100, 0, 255};
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1a (testLayerColor) (relative to child level 2b)
+    //   child level 1b (WHITE)
+    //     child level 2a (BLUE)
+    //     child level 2b (BLACK)
+    sp<SurfaceControl> childLevel1a =
+            createColorLayer("child level 1a", testLayerColor, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel1b =
+            createColorLayer("child level 1b", Color::WHITE, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel2a =
+            createColorLayer("child level 2a", Color::BLUE, childLevel1b.get());
+    sp<SurfaceControl> childLevel2b =
+            createColorLayer("child level 2b", Color::BLACK, childLevel1b.get());
+
+    Transaction{}
+            .setRelativeLayer(childLevel1a, childLevel2b, 1)
+            .show(childLevel1a)
+            .show(childLevel1b)
+            .show(childLevel2a)
+            .show(childLevel2b)
+            .apply();
+
+    {
+        // The childLevel1a should be in front of childLevel2b.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1a (testLayerColor) (relative to child level 2b)
+    Transaction{}.reparent(childLevel1b, nullptr).apply();
+
+    // // Background layer (RED)
+    // // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLevel1a, childLevel2a).apply();
+
+    {
+        // The childLevel1a and childLevel1b are no longer on screen
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::GREEN);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1b (WHITE)
+    //     child level 2a (BLUE)
+    //       child level 1a (testLayerColor) (relative to child level 2b)
+    //     child level 2b (BLACK)
+    Transaction{}.reparent(childLevel1b, mForegroundLayer).apply();
+
+    {
+        // Nothing should change at this point since relative z info was preserved.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+    }
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
new file mode 100644
index 0000000..7df3711
--- /dev/null
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <private/android_filesystem_config.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class ScreenCaptureTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        const ui::Size& resolution = config.resolution;
+
+        // Background surface
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+                                        resolution.getHeight(), 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+        // Foreground surface
+        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+
+            t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+            t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+                    .setPosition(mFGSurfaceControl, 64, 64)
+                    .show(mFGSurfaceControl);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+    }
+
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32,
+                                ISurfaceComposerClient::eSecure |
+                                        ISurfaceComposerClient::eFXSurfaceBufferQueue));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
+
+    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+
+    UIDFaker f(AID_SYSTEM);
+
+    // By default the system can capture screenshots with secure layers but they
+    // will be blacked out
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+
+    {
+        SCOPED_TRACE("as system");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+    // to receive them...we are expected to take care with the results.
+    DisplayCaptureArgs args;
+    args.displayToken = mDisplay;
+    args.captureSecureLayers = true;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+    ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+    ScreenCapture sc(mCaptureResults.buffer);
+    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) {
+    sp<SurfaceControl> parentLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            parentLayer = createLayer("parent-test", 32, 32,
+                                      ISurfaceComposerClient::eSecure |
+                                              ISurfaceComposerClient::eFXSurfaceBufferQueue));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
+
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 10, 10,
+                                                     ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     parentLayer.get()));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(childLayer, Color::BLUE, 10, 10));
+
+    Transaction().show(parentLayer).setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true);
+
+    UIDFaker f(AID_SYSTEM);
+
+    {
+        SCOPED_TRACE("as system");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 10, 10), Color::BLACK);
+    }
+
+    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+    // to receive them...we are expected to take care with the results.
+    DisplayCaptureArgs args;
+    args.displayToken = mDisplay;
+    args.captureSecureLayers = true;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+    ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+    ScreenCapture sc(mCaptureResults.buffer);
+    sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectBGColor(0, 0);
+    // Doesn't capture FG layer which is at 64, 64
+    mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl layer and its child.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl's child
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = fgHandle;
+    captureArgs.childrenOnly = true;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = fgHandle;
+    captureArgs.childrenOnly = true;
+    captureArgs.excludeHandles = {child2->getHandle()};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .show(child3)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = fgHandle;
+    captureArgs.childrenOnly = true;
+    captureArgs.excludeHandles = {child2->getHandle()};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures child
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = child->getHandle();
+    captureArgs.sourceCrop = {0, 0, 10, 20};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+    // Area outside of child's bounds is transparent.
+    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer above fg layer so should be shown above when computing all layers.
+            .setRelativeLayer(relative, mFGSurfaceControl, 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer below fg layer but relative to child layer so it should be shown
+            // above child layer.
+            .setLayer(relative, -1)
+            .setRelativeLayer(relative, child, 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+    // relative value should be taken into account, placing it above child layer.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectFGColor(10, 10);
+    // Relative layer is showing on top of child layer
+    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = child->getHandle();
+    captureArgs.sourceCrop = {0, 0, 10, 10};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    Rect layerCrop(0, 0, 10, 10);
+    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = child->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    LayerCaptureArgs args;
+    args.layerHandle = child->getHandle();
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+    sp<GraphicBuffer> outBuffer;
+
+    LayerCaptureArgs args;
+    args.layerHandle = child->getHandle();
+    args.childrenOnly = false;
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
+
+    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+    SurfaceComposerClient::Transaction().apply(true);
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
+    ScreenCapture sc(captureResults.buffer);
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    // Captures mFGSurfaceControl, its child, and the grandchild.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+    mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+    // Captures only the child layer, and not the parent.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = child->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectChildColor(0, 0);
+    mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    auto childHandle = child->getHandle();
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    // Captures only the grandchild.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = grandchild->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->checkPixel(0, 0, 50, 50, 50);
+    mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    // Capturing full screen should have both red and blue are visible.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = redLayer->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    captureArgs.sourceCrop = {0, 0, 30, 30};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
+    // area visible.
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    // Capturing full screen should have both red and blue are visible.
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = redLayer->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    captureArgs.frameScale = 0.5f;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+    auto redLayerHandle = redLayer->getHandle();
+    Transaction().reparent(redLayer, nullptr).apply();
+    redLayer.clear();
+    SurfaceComposerClient::Transaction().apply(true);
+
+    LayerCaptureArgs args;
+    args.layerHandle = redLayerHandle;
+
+    ScreenCaptureResults captureResults;
+    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
+    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
+}
+
+TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> secureLayer =
+            createLayer(String8("Secure surface"), 30, 30,
+                        ISurfaceComposerClient::eSecure |
+                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                        redLayer.get());
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(secureLayer, Color::BLUE, 30, 30));
+
+    auto redLayerHandle = redLayer->getHandle();
+    Transaction()
+            .show(redLayer)
+            .show(secureLayer)
+            .setLayerStack(redLayer, 0)
+            .setLayer(redLayer, INT32_MAX)
+            .apply();
+
+    LayerCaptureArgs args;
+    args.layerHandle = redLayerHandle;
+    args.childrenOnly = false;
+    ScreenCaptureResults captureResults;
+
+    // Call from outside system with secure layers will result in permission denied
+    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+
+    UIDFaker f(AID_SYSTEM);
+
+    // From system request, only red layer will be screenshot since the blue layer is secure.
+    // Black will be present where the secure layer is.
+    ScreenCapture::captureLayers(&mCapture, args);
+    mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLACK);
+    mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+
+    // Passing flag secure so the blue layer should be screenshot too.
+    args.captureSecureLayers = true;
+    ScreenCapture::captureLayers(&mCapture, args);
+    mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
+    mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
+    uid_t fakeUid = 12345;
+
+    DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                mBGSurfaceControl.get()));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+    // Make sure red layer with the background layer is screenshot.
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+    // From non system uid, can't request screenshot without a specified uid.
+    UIDFaker f(fakeUid);
+    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+
+    // Make screenshot request with current uid set. No layers were created with the current
+    // uid so screenshot will be black.
+    captureArgs.uid = fakeUid;
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+    sp<SurfaceControl> layerWithFakeUid;
+    // Create a new layer with the current uid
+    ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+                                    createLayer("new test layer", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                mBGSurfaceControl.get()));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+    Transaction()
+            .show(layerWithFakeUid)
+            .setLayer(layerWithFakeUid, INT32_MAX)
+            .setPosition(layerWithFakeUid, 128, 128)
+            .apply();
+
+    // Screenshot from the fakeUid caller with the uid requested allows the layer
+    // with that uid to be screenshotted. Everything else is black
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+    mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+
+    const Color layerColor = Color::RED;
+    const Rect bounds = Rect(10, 10, 40, 40);
+
+    Transaction()
+            .show(layer)
+            .hide(mFGSurfaceControl)
+            .setLayerStack(layer, 0)
+            .setLayer(layer, INT32_MAX)
+            .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+            .setCrop_legacy(layer, bounds)
+            .apply();
+
+    DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+
+    {
+        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        mCapture->expectColor(bounds, layerColor);
+        mCapture->expectBorder(bounds, {63, 63, 195, 255});
+    }
+
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+                      layer_state_t::eLayerSkipScreenshot)
+            .apply();
+
+    {
+        // Can't screenshot test layer since it now has flag
+        // eLayerSkipScreenshot
+        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        mCapture->expectColor(bounds, {63, 63, 195, 255});
+        mCapture->expectBorder(bounds, {63, 63, 195, 255});
+    }
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+    sp<SurfaceControl> layer;
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+    ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
+                                                     ISurfaceComposerClient::eFXSurfaceEffect,
+                                                     layer.get()));
+
+    const Color layerColor = Color::RED;
+    const Color childColor = Color::BLUE;
+    const Rect bounds = Rect(10, 10, 40, 40);
+    const Rect childBounds = Rect(20, 20, 30, 30);
+
+    Transaction()
+            .show(layer)
+            .show(childLayer)
+            .hide(mFGSurfaceControl)
+            .setLayerStack(layer, 0)
+            .setLayer(layer, INT32_MAX)
+            .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+            .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
+            .setCrop_legacy(layer, bounds)
+            .setCrop_legacy(childLayer, childBounds)
+            .apply();
+
+    DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+
+    {
+        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        mCapture->expectColor(childBounds, childColor);
+        mCapture->expectBorder(childBounds, layerColor);
+        mCapture->expectBorder(bounds, {63, 63, 195, 255});
+    }
+
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+                      layer_state_t::eLayerSkipScreenshot)
+            .apply();
+
+    {
+        // Can't screenshot child layer since the parent has the flag
+        // eLayerSkipScreenshot
+        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        mCapture->expectColor(childBounds, {63, 63, 195, 255});
+        mCapture->expectBorder(childBounds, {63, 63, 195, 255});
+        mCapture->expectBorder(bounds, {63, 63, 195, 255});
+    }
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithUid) {
+    uid_t fakeUid = 12345;
+
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                mBGSurfaceControl.get()));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+    captureArgs.childrenOnly = false;
+
+    // Make sure red layer with the background layer is screenshot.
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+    // From non system uid, can't request screenshot without a specified uid.
+    std::unique_ptr<UIDFaker> uidFaker = std::make_unique<UIDFaker>(fakeUid);
+
+    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+
+    // Make screenshot request with current uid set. No layers were created with the current
+    // uid so screenshot will be black.
+    captureArgs.uid = fakeUid;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+    mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+
+    sp<SurfaceControl> layerWithFakeUid;
+    // Create a new layer with the current uid
+    ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+                                    createLayer("new test layer", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                mBGSurfaceControl.get()));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+    Transaction()
+            .show(layerWithFakeUid)
+            .setLayer(layerWithFakeUid, INT32_MAX)
+            .setPosition(layerWithFakeUid, 128, 128)
+            // reparent a layer that was created with a different uid to the new layer.
+            .reparent(layer, layerWithFakeUid)
+            .apply();
+
+    // Screenshot from the fakeUid caller with the uid requested allows the layer
+    // with that uid to be screenshotted. The child layer is skipped since it was created
+    // from a different uid.
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+    mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+    // Clear fake calling uid so it's back to system.
+    uidFaker = nullptr;
+    // Screenshot from the test caller with the uid requested allows the layer
+    // with that uid to be screenshotted. The child layer is skipped since it was created
+    // from a different uid.
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+    mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+    // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
+    captureArgs.uid = -1;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+    mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
+    mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public ScreenCaptureTest {
+public:
+    void SetUp() override {
+        ScreenCaptureTest::SetUp();
+
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
+        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        SurfaceComposerClient::Transaction().show(mChild).apply(true);
+    }
+
+    void verify(std::function<void()> verifyStartingState) {
+        // Verify starting state before a screenshot is taken.
+        verifyStartingState();
+
+        // Verify child layer does not inherit any of the properties of its
+        // parent when its screenshot is captured.
+        LayerCaptureArgs captureArgs;
+        captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+        captureArgs.childrenOnly = true;
+        ScreenCapture::captureLayers(&mCapture, captureArgs);
+        mCapture->checkPixel(10, 10, 0, 0, 0);
+        mCapture->expectChildColor(0, 0);
+
+        // Verify all assumptions are still true after the screenshot is taken.
+        verifyStartingState();
+    }
+
+    std::unique_ptr<ScreenCapture> mCapture;
+    sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+    // Even though the parent is hidden we should still capture the child.
+
+    // Before and after reparenting, verify child is properly hidden
+    // when rendering full-screen.
+    verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+    SurfaceComposerClient::Transaction()
+            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+            .apply(true);
+
+    // Even though the parent is cropped out we should still capture the child.
+
+    // Before and after reparenting, verify child is cropped by parent.
+    verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+    // We should not inherit the parent scaling.
+
+    // Before and after reparenting, verify child is properly scaled.
+    verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 8d97f27..81e648a 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -39,7 +39,6 @@
 using Trace = surfaceflinger::Trace;
 using Increment = surfaceflinger::Increment;
 
-constexpr int32_t SCALING_UPDATE = 1;
 constexpr uint32_t BUFFER_UPDATES = 18;
 constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
 constexpr uint32_t SIZE_UPDATE = 134;
@@ -52,6 +51,7 @@
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
 const float SHADOW_RADIUS_UPDATE = 35.0f;
+std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
 
 const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
 constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -182,6 +182,7 @@
     bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
     bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
                                          bool foundBackgroundBlurRadius);
+    bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -220,8 +221,8 @@
     void cropUpdate(Transaction&);
     void cornerRadiusUpdate(Transaction&);
     void backgroundBlurRadiusUpdate(Transaction&);
+    void blurRegionsUpdate(Transaction&);
     void matrixUpdate(Transaction&);
-    void overrideScalingModeUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
     void layerStackUpdate(Transaction&);
     void hiddenFlagUpdate(Transaction&);
@@ -359,6 +360,12 @@
     t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
 }
 
+void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
+    BLUR_REGIONS_UPDATE.empty();
+    BLUR_REGIONS_UPDATE.push_back(BlurRegion());
+    t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
@@ -371,10 +378,6 @@
     t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
 }
 
-void SurfaceInterceptorTest::overrideScalingModeUpdate(Transaction& t) {
-    t.setOverrideScalingMode(mBGSurfaceControl, SCALING_UPDATE);
-}
-
 void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
     Region region(CROP_UPDATE);
     t.setTransparentRegionHint(mBGSurfaceControl, region);
@@ -397,16 +400,15 @@
 }
 
 void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
-    t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
-                                   DEFERRED_UPDATE);
+    t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl, DEFERRED_UPDATE);
 }
 
 void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
-    t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+    t.reparent(mBGSurfaceControl, mFGSurfaceControl);
 }
 
 void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
-    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z);
+    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
 }
 
 void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) {
@@ -414,7 +416,7 @@
 }
 
 void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
-    t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+    t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl);
 }
 
 void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
@@ -422,7 +424,7 @@
 }
 
 void SurfaceInterceptorTest::displayCreation(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
     SurfaceComposerClient::destroyDisplay(testDisplay);
 }
 
@@ -437,10 +439,10 @@
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
     runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
+    runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
-    runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
     runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
     runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
@@ -526,6 +528,17 @@
     return foundBackgroundBlur;
 }
 
+bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
+                                                    bool foundBlurRegions) {
+    bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
+    if (hasBlurRegions && !foundBlurRegions) {
+        foundBlurRegions = true;
+    } else if (hasBlurRegions && foundBlurRegions) {
+        []() { FAIL(); }();
+    }
+    return foundBlurRegions;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
@@ -562,17 +575,6 @@
     return foundMatrix;
 }
 
-bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
-        bool foundScalingMode) {
-    bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
-    if (hasScalingUpdate && !foundScalingMode) {
-        foundScalingMode = true;
-    } else if (hasScalingUpdate && foundScalingMode) {
-        [] () { FAIL(); }();
-    }
-    return foundScalingMode;
-}
-
 bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
         bool foundTransparentRegion) {
     auto traceRegion = change.transparent_region_hint().region(0);
@@ -725,12 +727,12 @@
                         case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
                             foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+                            foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
                             break;
-                        case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
-                            foundUpdate = scalingModeUpdateFound(change, foundUpdate);
-                            break;
                         case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
                             foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
                             break;
@@ -781,7 +783,6 @@
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
@@ -819,7 +820,7 @@
 
 bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
     bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
-            increment.display_creation().is_secure());
+                 !increment.display_creation().is_secure());
     if (isMatch && !foundDisplay) {
         foundDisplay = true;
     } else if (isMatch && foundDisplay) {
@@ -912,13 +913,13 @@
                 SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
+                SurfaceChange::SurfaceChangeCase::kBlurRegions);
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
-            SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index f0af363..01badf4 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
                 t.setDisplaySurface(vDisplay, producer);
                 t.setDisplayLayerStack(vDisplay, 0);
                 t.setDisplayProjection(vDisplay, displayState.orientation,
-                                       Rect(displayState.viewport), Rect(resolution));
+                                       Rect(displayState.layerStackSpaceRect), Rect(resolution));
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
                 BufferItem item;
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2861013..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -8,12 +8,12 @@
          "FakeComposerUtils.cpp",
          "SFFakeHwc_test.cpp"
     ],
+    require_root: true,
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer@2.1-resources",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
@@ -33,6 +33,7 @@
         "libutils",
     ],
     static_libs: [
+        "android.hardware.graphics.composer@2.1-resources",
         "libcompositionengine",
         "libgmock",
         "libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 96a7541..1cea25a 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -29,8 +29,8 @@
 #include "SurfaceFlinger.h" // Get the name of the service...
 
 #include <binder/IServiceManager.h>
-
 #include <cutils/properties.h>
+#include <hidl/ServiceManagement.h>
 
 #include <iomanip>
 #include <thread>
@@ -173,7 +173,7 @@
     property_set("debug.sf.hwc_service_name", "mock");
 
     // This allows tests/SF to register/load a HIDL service not listed in manifest files.
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+    android::hardware::details::setTrebleTestingOverride(true);
     property_set("debug.sf.treble_testing_override", "true");
 }
 
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index a03fd89..0a70f5c 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -72,6 +72,9 @@
 
 ///////////////////////////////////////////////
 
+constexpr PhysicalDisplayId kPrimaryDisplayId = PhysicalDisplayId::fromPort(PRIMARY_DISPLAY);
+constexpr PhysicalDisplayId kExternalDisplayId = PhysicalDisplayId::fromPort(EXTERNAL_DISPLAY);
+
 struct TestColor {
 public:
     uint8_t r;
@@ -272,6 +275,10 @@
         mFakeComposerClient->runVSyncAndWait();
     }
 
+    bool waitForHotplugEvent(Display displayId, bool connected) {
+        return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+    }
+
     bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
         int waitCount = 20;
         while (waitCount--) {
@@ -280,9 +287,8 @@
                 mReceivedDisplayEvents.pop_front();
 
                 ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-                         "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                         ", connected %d\t",
-                         event.header.displayId, event.hotplug.connected);
+                         "event hotplug: displayId %s, connected %d\t",
+                         to_string(event.header.displayId).c_str(), event.hotplug.connected);
 
                 if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
                     event.header.displayId == displayId && event.hotplug.connected == connected) {
@@ -295,7 +301,8 @@
         return false;
     }
 
-    bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+    bool waitForConfigChangedEvent(Display display, int32_t configId) {
+        PhysicalDisplayId displayId(display);
         int waitCount = 20;
         while (waitCount--) {
             while (!mReceivedDisplayEvents.empty()) {
@@ -303,9 +310,8 @@
                 mReceivedDisplayEvents.pop_front();
 
                 ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
-                         "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                         ", configId %d\t",
-                         event.header.displayId, event.config.configId);
+                         "event config: displayId %s, configId %d\t",
+                         to_string(event.header.displayId).c_str(), event.config.configId);
 
                 if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
                     event.header.displayId == displayId && event.config.configId == configId) {
@@ -335,7 +341,7 @@
         EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
         {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
             EXPECT_FALSE(display == nullptr);
 
             DisplayConfig config;
@@ -367,7 +373,7 @@
         EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
 
         {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
             EXPECT_TRUE(display == nullptr);
 
             DisplayConfig config;
@@ -396,7 +402,7 @@
         waitForDisplayTransaction();
         EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
         DisplayConfig config;
@@ -441,7 +447,7 @@
             const auto& config = configs[i];
             if (config.resolution.getWidth() == 800) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
@@ -503,7 +509,7 @@
         waitForDisplayTransaction();
         EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
         DisplayConfig config;
@@ -547,7 +553,7 @@
             const auto& config = configs[i];
             if (config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
@@ -619,7 +625,7 @@
         waitForDisplayTransaction();
         EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
         DisplayConfig config;
@@ -664,7 +670,8 @@
             if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::
-                                  setDesiredDisplayConfigSpecs(display, i, configs[i].refreshRate,
+                                  setDesiredDisplayConfigSpecs(display, i, false,
+                                                               configs[i].refreshRate,
                                                                configs[i].refreshRate,
                                                                configs[i].refreshRate,
                                                                configs[i].refreshRate));
@@ -710,7 +717,7 @@
             const auto& config = configs[i];
             if (config.refreshRate == 1e9f / 8'333'333) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
@@ -757,7 +764,7 @@
             const auto& config = configs[i];
             if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
                                                                               config.refreshRate,
@@ -808,7 +815,7 @@
 
         EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
         {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
             EXPECT_TRUE(display == nullptr);
 
             DisplayConfig config;
@@ -834,7 +841,7 @@
         EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
 
         {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
             EXPECT_FALSE(display == nullptr);
 
             DisplayConfig config;
@@ -988,7 +995,7 @@
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         ALOGI("TransactionTest::SetUp - display");
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
         ASSERT_FALSE(display == nullptr);
 
         DisplayConfig config;
@@ -1316,7 +1323,7 @@
         {
             TransactionScope ts(*sFakeComposer);
             ts.setAlpha(mFGSurfaceControl, 0.75);
-            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl,
                                             syncSurfaceControl->getSurface()->getNextFrameNumber());
         }
         EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
@@ -1324,7 +1331,7 @@
         {
             TransactionScope ts(*sFakeComposer);
             ts.setPosition(mFGSurfaceControl, 128, 128);
-            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl,
                                             syncSurfaceControl->getSurface()->getNextFrameNumber() +
                                                     1);
         }
@@ -1370,7 +1377,7 @@
             TransactionScope ts(*sFakeComposer);
             ts.setPosition(relativeSurfaceControl, 64, 64);
             ts.show(relativeSurfaceControl);
-            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
         }
         auto referenceFrame = mBaseFrame;
         // NOTE: All three layers will be visible as the surfaces are
@@ -1463,7 +1470,7 @@
         Base::SetUp();
         mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
                                                       PIXEL_FORMAT_RGBA_8888, 0,
-                                                      Base::mFGSurfaceControl.get());
+                                                      Base::mFGSurfaceControl->getHandle());
         fillSurfaceRGBA8(mChild, LIGHT_GRAY);
 
         Base::sFakeComposer->runVSyncAndWait();
@@ -1600,7 +1607,7 @@
 
         {
             TransactionScope ts(*Base::sFakeComposer);
-            ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
+            ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl);
         }
 
         auto referenceFrame2 = referenceFrame;
@@ -1647,7 +1654,7 @@
         sp<SurfaceControl> childNewClient =
                 newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
                                                  PIXEL_FORMAT_RGBA_8888, 0,
-                                                 Base::mFGSurfaceControl.get());
+                                                 Base::mFGSurfaceControl->getHandle());
         ASSERT_TRUE(childNewClient != nullptr);
         ASSERT_TRUE(childNewClient->isValid());
         fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
@@ -1685,30 +1692,6 @@
         EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
     }
 
-    void Test_InheritNonTransformScalingFromParent() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-        }
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setOverrideScalingMode(Base::mFGSurfaceControl,
-                                      NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-            // We cause scaling by 2.
-            ts.setSize(Base::mFGSurfaceControl, 128, 128);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
     // Regression test for b/37673612
     void Test_ChildrenWithParentBufferTransform() {
         {
@@ -1750,12 +1733,12 @@
         mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
                                                       PIXEL_FORMAT_RGBA_8888,
                                                       ISurfaceComposerClient::eHidden,
-                                                      Base::mFGSurfaceControl.get());
+                                                      Base::mFGSurfaceControl->getHandle());
 
         // Show the child layer in a deferred transaction
         {
             TransactionScope ts(*Base::sFakeComposer);
-            ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
+            ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl,
                                             Base::mFGSurfaceControl->getSurface()
                                                     ->getNextFrameNumber());
             ts.show(mChild);
@@ -1817,10 +1800,6 @@
     Test_DetachChildrenDifferentClient();
 }
 
-TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
-    Test_InheritNonTransformScalingFromParent();
-}
-
 // Regression test for b/37673612
 TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
     Test_ChildrenWithParentBufferTransform();
@@ -1841,7 +1820,7 @@
                 Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
                                                      PIXEL_FORMAT_RGBA_8888,
                                                      ISurfaceComposerClient::eFXSurfaceEffect,
-                                                     Base::mFGSurfaceControl.get());
+                                                     Base::mFGSurfaceControl->getHandle());
         {
             TransactionScope ts(*Base::sFakeComposer);
             ts.setColor(Base::mChild,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7574ff1..18f3745 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -14,7 +14,7 @@
 
 cc_test {
     name: "libsurfaceflinger_unittest",
-    defaults: ["libsurfaceflinger_defaults"],
+    defaults: ["surfaceflinger_defaults"],
     test_suites: ["device-tests"],
     sanitize: {
         // Using the address sanitizer not only helps uncover issues in the code
@@ -39,16 +39,29 @@
         "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
         "DisplayIdentificationTest.cpp",
+        "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
-        "EventControlThreadTest.cpp",
+        "DisplayDevice_GetBestColorModeTest.cpp",
+        "DisplayDevice_SetProjectionTest.cpp",
         "EventThreadTest.cpp",
+        "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
         "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
-        "PhaseOffsetsTest.cpp",
+        "MessageQueueTest.cpp",
         "PromiseTest.cpp",
+        "SurfaceFlinger_CreateDisplayTest.cpp",
+        "SurfaceFlinger_DestroyDisplayTest.cpp",
+        "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+        "SurfaceFlinger_HandleTransactionLockedTest.cpp",
+        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
+        "SurfaceFlinger_OnHotplugReceivedTest.cpp",
+        "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
+        "SurfaceFlinger_SetDisplayStateTest.cpp",
+        "SurfaceFlinger_SetPowerModeInternalTest.cpp",
+        "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
         "SetFrameRateTest.cpp",
@@ -58,42 +71,88 @@
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
         "FrameTracerTest.cpp",
+        "TimerTest.cpp",
         "TransactionApplicationTest.cpp",
         "StrongTypingTest.cpp",
         "VSyncDispatchTimerQueueTest.cpp",
         "VSyncDispatchRealtimeTest.cpp",
+        "VsyncModulatorTest.cpp",
         "VSyncPredictorTest.cpp",
         "VSyncReactorTest.cpp",
+        "VsyncConfigurationTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
-        "mock/MockDispSync.cpp",
-        "mock/MockEventControlThread.cpp",
         "mock/MockEventThread.cpp",
+        "mock/MockFrameTracer.cpp",
         "mock/MockMessageQueue.cpp",
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
-        "mock/MockFrameTracer.cpp",
+        "mock/MockVsyncController.cpp",
+        "mock/MockVSyncTracker.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
-        "libgmock",
-        "libcompositionengine",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.power@1.2",
+        "android.hardware.power@1.3",
         "libcompositionengine_mocks",
+        "libcompositionengine",
+        "libframetimeline",
+        "libgmock",
         "libgui_mocks",
+        "liblayers_proto",
         "libperfetto_client_experimental",
         "librenderengine_mocks",
+        "librenderengine",
+        "libserviceutils",
+        "libtimestats",
+        "libtimestats_proto",
+        "libtrace_proto",
         "perfetto_trace_protos",
     ],
     shared_libs: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.common@1.2",
+        "android.hardware.power-cpp",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libEGL",
+        "libfmq",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libgui",
+        "libhidlbase",
+        "libinput",
+        "liblog",
+        "libnativewindow",
+        "libprocessgroup",
+        "libprotobuf-cpp-lite",
         "libprotoutil",
+        "libstatslog",
         "libstatssocket",
-        "libsurfaceflinger",
-        "libtimestats",
-        "libtimestats_proto",
+        "libSurfaceFlingerProp",
+        "libsync",
+        "libui",
+        "libutils",
+        "libstatspull",
     ],
     header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+        "android.hardware.graphics.composer@2.2-command-buffer",
+        "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 95e2ef5..0911712 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -35,16 +35,17 @@
 #include <utils/String8.h>
 
 #include "BufferQueueLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayRenderArea.h"
 #include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/MockTimeStats.h"
+#include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -80,7 +81,7 @@
 constexpr hal::HWLayerId HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
 constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
@@ -133,25 +134,33 @@
         EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*eventThread, createEventConnection(_, _))
                 .WillOnce(Return(
-                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                        new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                  ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
         EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
                 .WillOnce(Return(
-                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                        new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                  ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
-        auto primaryDispSync = std::make_unique<mock::DispSync>();
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-        EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*primaryDispSync, getPeriod())
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-        EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
-        mFlinger.setupScheduler(std::move(primaryDispSync),
-                                std::make_unique<mock::EventControlThread>(),
-                                std::move(eventThread), std::move(sfEventThread));
+        constexpr ISchedulerCallback* kCallback = nullptr;
+        constexpr bool kHasMultipleConfigs = true;
+        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                std::move(eventThread), std::move(sfEventThread), kCallback,
+                                kHasMultipleConfigs);
+
+        // Layer history should be created if there are multiple configs.
+        ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
     }
 
     void setupForceGeometryDirty() {
@@ -182,6 +191,7 @@
     sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
             new compositionengine::mock::DisplaySurface();
     mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+    std::vector<sp<Layer>> mAuxiliaryLayers;
 
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
     ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
@@ -229,28 +239,27 @@
     LayerCase::setupForScreenCapture(this);
 
     const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
-    constexpr bool useIdentityTransform = true;
     constexpr bool forSystem = true;
     constexpr bool regionSampling = false;
 
-    DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
-                                 DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
-                                 ui::Transform::ROT_0);
+    auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+                                                ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
 
     auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
-        return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+        return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
+                                                   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 = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+    mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
                                              HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
 
     int fd = -1;
     status_t result =
-            mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
-                                             useIdentityTransform, forSystem, &fd, regionSampling);
+            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+                                            forSystem, &fd, regionSampling);
     if (fd >= 0) {
         close(fd);
     }
@@ -334,8 +343,6 @@
         EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
         EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
 
-        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
         EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
         EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
 
@@ -444,8 +451,6 @@
 
     template <typename Case>
     static void setupCommonCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
         // TODO: This seems like an unnecessary call if display is powered off.
         EXPECT_CALL(*test->mComposer,
                     setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
@@ -460,8 +465,6 @@
     static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {}
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
         // TODO: This seems like an unnecessary call if display is powered off.
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -544,7 +547,6 @@
         enqueueBuffer(test, layer);
         Mock::VerifyAndClearExpectations(test->mMessageQueue);
 
-        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
         bool ignoredRecomputeVisibleRegions;
         layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
         Mock::VerifyAndClear(test->mRenderEngine);
@@ -577,8 +579,6 @@
                     .Times(1);
             // TODO: Coverage of other values
             EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
-            // TODO: Coverage of other values
-            EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
 
             // These expectations retire on saturation as the code path these
             // expectations are for appears to make an extra call to them.
@@ -766,10 +766,9 @@
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
 };
 
-struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
-    using Base = BaseLayerProperties<SecureLayerProperties>;
-
-    static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+template <typename LayerProperties>
+struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> {
+    using Base = BaseLayerProperties<LayerProperties>;
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
@@ -810,6 +809,13 @@
     }
 };
 
+struct ParentSecureLayerProperties
+      : public CommonSecureLayerProperties<ParentSecureLayerProperties> {};
+
+struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerProperties> {
+    static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+};
+
 struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
     using Base = BaseLayerProperties<CursorLayerProperties>;
 
@@ -845,6 +851,13 @@
         Mock::VerifyAndClear(test->mRenderEngine);
         Mock::VerifyAndClearExpectations(test->mMessageQueue);
 
+        initLayerDrawingStateAndComputeBounds(test, layer);
+
+        return layer;
+    }
+
+    template <typename L>
+    static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
         layerDrawingState.active.w = 100;
@@ -852,8 +865,6 @@
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
         layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
-
-        return layer;
     }
 
     static void injectLayer(CompositionTest* test, sp<Layer> layer) {
@@ -891,7 +902,7 @@
     static FlingerLayerType createLayer(CompositionTest* test) {
         FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
             return new EffectLayer(
-                    LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer",
+                    LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                       LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                       LayerProperties::LAYER_FLAGS, LayerMetadata()));
         });
@@ -930,8 +941,7 @@
 
         FlingerLayerType layer =
                 Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
-                    sp<Client> client;
-                    LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer",
+                    LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                            LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                            LayerProperties::LAYER_FLAGS, LayerMetadata());
                     args.textureName = test->mFlinger.mutableTexturePool().back();
@@ -975,6 +985,49 @@
     }
 };
 
+template <typename LayerProperties>
+struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
+    using Base = BaseLayerVariant<LayerProperties>;
+    using FlingerLayerType = sp<ContainerLayer>;
+
+    static FlingerLayerType createLayer(CompositionTest* test) {
+        LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
+                               LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                               LayerProperties::LAYER_FLAGS, LayerMetadata());
+        FlingerLayerType layer = new ContainerLayer(args);
+        Base::template initLayerDrawingStateAndComputeBounds(test, layer);
+        return layer;
+    }
+};
+
+template <typename LayerVariant, typename ParentLayerVariant>
+struct ChildLayerVariant : public LayerVariant {
+    using Base = LayerVariant;
+    using FlingerLayerType = typename LayerVariant::FlingerLayerType;
+    using ParentBase = ParentLayerVariant;
+
+    static FlingerLayerType createLayer(CompositionTest* test) {
+        // Need to create child layer first. Otherwise layer history size will be 2.
+        FlingerLayerType layer = Base::createLayer(test);
+
+        typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test);
+        parentLayer->addChild(layer);
+        test->mFlinger.setLayerDrawingParent(layer, parentLayer);
+
+        test->mAuxiliaryLayers.push_back(parentLayer);
+
+        return layer;
+    }
+
+    static void cleanupInjectedLayers(CompositionTest* test) {
+        // Clear auxiliary layers first so that child layer can be successfully destroyed in the
+        // following call.
+        test->mAuxiliaryLayers.clear();
+
+        Base::cleanupInjectedLayers(test);
+    }
+};
+
 /* ------------------------------------------------------------------------
  * Variants to control how the composition type is changed
  */
@@ -1364,6 +1417,38 @@
 }
 
 /* ------------------------------------------------------------------------
+ *  Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display
+ */
+
+TEST_F(CompositionTest,
+       HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<InsecureDisplaySetupVariant,
+                            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+                                              ContainerLayerVariant<SecureLayerProperties>>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest,
+       HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<InsecureDisplaySetupVariant,
+                            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+                                              ContainerLayerVariant<SecureLayerProperties>>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
+    captureScreenComposition<
+            CompositionCase<InsecureDisplaySetupVariant,
+                            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+                                              ContainerLayerVariant<SecureLayerProperties>>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
  *  Cursor layers
  */
 
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index afebc40..54f4c7c 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -27,13 +27,99 @@
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/DispSyncSource.h"
-#include "mock/MockDispSync.h"
+#include "Scheduler/VSyncDispatch.h"
 
 namespace android {
 namespace {
 
 using namespace std::chrono_literals;
-using testing::Return;
+using namespace testing;
+
+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&));
+
+    MockVSyncDispatch() {
+        ON_CALL(*this, registerCallback)
+                .WillByDefault(
+                        [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
+                               std::string) {
+                            CallbackToken token(mNextToken);
+                            mNextToken++;
+
+                            mCallbacks.emplace(token, CallbackData(callback));
+                            ALOGD("registerCallback: %zu", token.value());
+                            return token;
+                        });
+
+        ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
+            ALOGD("unregisterCallback: %zu", token.value());
+            mCallbacks.erase(token);
+        });
+
+        ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
+            ALOGD("schedule: %zu", token.value());
+            if (mCallbacks.count(token) == 0) {
+                ALOGD("schedule: callback %zu not registered", token.value());
+                return scheduler::ScheduleResult::Error;
+            }
+
+            auto& callback = mCallbacks.at(token);
+            callback.scheduled = true;
+            callback.vsyncTime = timing.earliestVsync;
+            callback.targetWakeupTime =
+                    timing.earliestVsync - timing.workDuration - timing.readyDuration;
+            ALOGD("schedule: callback %zu scheduled", token.value());
+            return scheduler::ScheduleResult::Scheduled;
+        });
+
+        ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
+            ALOGD("cancel: %zu", token.value());
+            if (mCallbacks.count(token) == 0) {
+                ALOGD("cancel: callback %zu is not registered", token.value());
+                return scheduler::CancelResult::Error;
+            }
+
+            auto& callback = mCallbacks.at(token);
+            callback.scheduled = false;
+            ALOGD("cancel: callback %zu cancelled", token.value());
+            return scheduler::CancelResult::Cancelled;
+        });
+    }
+
+    void triggerCallbacks() {
+        ALOGD("triggerCallbacks");
+        for (auto& [token, callback] : mCallbacks) {
+            if (callback.scheduled) {
+                ALOGD("triggerCallbacks: callback %zu", token.value());
+                callback.scheduled = false;
+                callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
+            } else {
+                ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
+            }
+        }
+    }
+
+private:
+    struct CallbackData {
+        explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
+              : func(std::move(func)) {}
+
+        std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
+        bool scheduled = false;
+        nsecs_t vsyncTime = 0;
+        nsecs_t targetWakeupTime = 0;
+        nsecs_t readyTime = 0;
+    };
+
+    std::unordered_map<CallbackToken, CallbackData> mCallbacks;
+    size_t mNextToken;
+};
 
 class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
 protected:
@@ -43,15 +129,19 @@
     void createDispSync();
     void createDispSyncSource();
 
-    void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+    void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+                      nsecs_t deadlineTimestamp) override;
 
-    std::unique_ptr<mock::DispSync> mDispSync;
-    std::unique_ptr<DispSyncSource> mDispSyncSource;
+    std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
+    std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
 
-    AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
+    AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder;
 
-    static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+    static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
+    static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
     static constexpr int mIterations = 100;
+    const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
+    const std::string mName = "DispSyncSourceTest";
 };
 
 DispSyncSourceTest::DispSyncSourceTest() {
@@ -66,20 +156,21 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) {
+void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+                                      nsecs_t deadlineTimestamp) {
     ALOGD("onVSyncEvent: %" PRId64, when);
 
-    mVSyncEventCallRecorder.recordCall(when);
+    mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp);
 }
 
 void DispSyncSourceTest::createDispSync() {
-    mDispSync = std::make_unique<mock::DispSync>();
+    mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
 }
 
 void DispSyncSourceTest::createDispSyncSource() {
-    createDispSync();
-    mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
-                                                       "DispSyncSourceTest");
+    mDispSyncSource =
+            std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
+                                                        mReadyDuration, true, mName.c_str());
     mDispSyncSource->setCallback(this);
 }
 
@@ -89,57 +180,119 @@
 
 TEST_F(DispSyncSourceTest, createDispSync) {
     createDispSync();
-    EXPECT_TRUE(mDispSync);
+    EXPECT_TRUE(mVSyncDispatch);
 }
 
 TEST_F(DispSyncSourceTest, createDispSyncSource) {
+    createDispSync();
+
+    InSequence seq;
+    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
+            .WillOnce(Return(scheduler::CancelResult::Cancelled));
+    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
     createDispSyncSource();
+
     EXPECT_TRUE(mDispSyncSource);
 }
 
 TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
+    createDispSync();
+
+    InSequence seq;
+    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
     createDispSyncSource();
+
     EXPECT_TRUE(mDispSyncSource);
 
     // DispSyncSource starts with Vsync disabled
-    mDispSync->triggerCallback();
+    mVSyncDispatch->triggerCallbacks();
     EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(DispSyncSourceTest, waitForCallbacks) {
+    createDispSync();
+
+    InSequence seq;
+    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch,
+                schedule(_, Truly([&](auto timings) {
+                             return timings.workDuration == mWorkDuration.count() &&
+                                     timings.readyDuration == mReadyDuration.count();
+                         })))
+            .Times(mIterations + 1);
+    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
     createDispSyncSource();
+
     EXPECT_TRUE(mDispSyncSource);
 
     mDispSyncSource->setVSyncEnabled(true);
-    EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
     for (int i = 0; i < mIterations; i++) {
-        mDispSync->triggerCallback();
-        EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+        mVSyncDispatch->triggerCallbacks();
+        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+        ASSERT_TRUE(callbackData.has_value());
+        const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+        EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
     }
 }
 
-TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) {
+TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
+    createDispSync();
+
+    InSequence seq;
+    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch,
+                schedule(_, Truly([&](auto timings) {
+                             return timings.workDuration == mWorkDuration.count() &&
+                                     timings.readyDuration == mReadyDuration.count();
+                         })))
+            .Times(1);
+
     createDispSyncSource();
+
     EXPECT_TRUE(mDispSyncSource);
 
     mDispSyncSource->setVSyncEnabled(true);
-    EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
+    EXPECT_CALL(*mVSyncDispatch,
+                schedule(_, Truly([&](auto timings) {
+                             return timings.workDuration == mWorkDuration.count() &&
+                                     timings.readyDuration == mReadyDuration.count();
+                         })))
+            .Times(mIterations);
     for (int i = 0; i < mIterations; i++) {
-        mDispSync->triggerCallback();
-        EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+        mVSyncDispatch->triggerCallbacks();
+        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+        ASSERT_TRUE(callbackData.has_value());
+        const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+        EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
     }
 
-    EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666));
-    mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count());
+    const auto newDuration = mWorkDuration / 2;
+    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+                                              return timings.workDuration == newDuration.count() &&
+                                                      timings.readyDuration == 0;
+                                          })))
+            .Times(1);
+    mDispSyncSource->setDuration(newDuration, 0ns);
 
-    EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count());
-
+    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+                                              return timings.workDuration == newDuration.count() &&
+                                                      timings.readyDuration == 0;
+                                          })))
+            .Times(mIterations);
     for (int i = 0; i < mIterations; i++) {
-        mDispSync->triggerCallback();
-        EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+        mVSyncDispatch->triggerCallbacks();
+        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+        ASSERT_TRUE(callbackData.has_value());
+        const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+        EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count());
     }
+
+    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
new file mode 100644
index 0000000..2e53cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 GetBestColorModeTest : public DisplayTransactionTest {
+public:
+    void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+    void addHwcColorModesMapping(ui::ColorMode colorMode,
+                                 std::vector<ui::RenderIntent> renderIntents) {
+        mHwcColorModes[colorMode] = renderIntents;
+    }
+
+    void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+    void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+    void getBestColorMode() {
+        auto displayDevice =
+                injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+                    injector.setHwcColorModes(mHwcColorModes);
+                    injector.setHasWideColorGamut(mHasWideColorGamut);
+                    injector.setNativeWindow(mNativeWindow);
+                });
+
+        displayDevice->getCompositionDisplay()
+                ->getDisplayColorProfile()
+                ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+                                   &mOutColorMode, &mOutRenderIntent);
+    }
+
+    ui::Dataspace mOutDataspace;
+    ui::ColorMode mOutColorMode;
+    ui::RenderIntent mOutRenderIntent;
+
+private:
+    ui::Dataspace mInputDataspace;
+    ui::RenderIntent mInputRenderIntent;
+    bool mHasWideColorGamut = false;
+    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
new file mode 100644
index 0000000..9fe30f8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+    static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080;  // arbitrary
+    static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+            HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+    DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+                                   ui::Rotation physicalOrientation)
+          : mFlingerDisplaySize(flingerDisplaySize),
+            mHardwareDisplaySize(hardwareDisplaySize),
+            mPhysicalOrientation(physicalOrientation),
+            mDisplayDevice(createDisplayDevice()) {}
+
+    sp<DisplayDevice> createDisplayDevice() {
+        return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+            injector.setPhysicalOrientation(mPhysicalOrientation);
+        });
+    }
+
+    ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+    void setProjectionForRotation0() {
+        // A logical rotation of 0 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation90() {
+        // A logical rotation of 90 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
+                                      Rect(swapWH(mFlingerDisplaySize)));
+    }
+
+    void setProjectionForRotation180() {
+        // A logical rotation of 180 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation270() {
+        // A logical rotation of 270 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
+                                      Rect(swapWH(mFlingerDisplaySize)));
+    }
+
+    void expectStateForHardwareTransform0() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform90() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+        // size width and height swapped
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform180() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform270() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+        // size width and height swapped
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    const ui::Size mFlingerDisplaySize;
+    const ui::Size mHardwareDisplaySize;
+    const ui::Rotation mPhysicalOrientation;
+    const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed0()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed90()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed180()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed270()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform180();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
new file mode 100644
index 0000000..be7609a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include <ui/DisplayId.h>
+#include "DisplayIdGenerator.h"
+
+namespace android {
+
+template <typename T>
+void testNextId(DisplayIdGenerator<T>& generator) {
+    constexpr int kNumIds = 5;
+    std::vector<T> ids;
+    for (int i = 0; i < kNumIds; i++) {
+        const auto id = generator.nextId();
+        ASSERT_TRUE(id);
+        ids.push_back(*id);
+    }
+
+    // All IDs should be different.
+    for (size_t i = 0; i < kNumIds; i++) {
+        for (size_t j = i + 1; j < kNumIds; j++) {
+            EXPECT_NE(ids[i], ids[j]);
+        }
+    }
+}
+
+TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) {
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator;
+    testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, nextIdHalVirtual) {
+    RandomDisplayIdGenerator<HalVirtualDisplayId> generator;
+    testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, markUnused) {
+    constexpr size_t kMaxIdsCount = 5;
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+    const auto id = generator.nextId();
+    EXPECT_TRUE(id);
+
+    for (int i = 1; i < kMaxIdsCount; i++) {
+        EXPECT_TRUE(generator.nextId());
+    }
+
+    EXPECT_FALSE(generator.nextId());
+
+    generator.markUnused(*id);
+    EXPECT_TRUE(generator.nextId());
+}
+
+TEST(DisplayIdGeneratorTest, maxIdsCount) {
+    constexpr size_t kMaxIdsCount = 5;
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+    for (int i = 0; i < kMaxIdsCount; i++) {
+        EXPECT_TRUE(generator.nextId());
+    }
+
+    EXPECT_FALSE(generator.nextId());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index cc6a60c..02ce079 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -396,10 +396,10 @@
     }
 }
 
-TEST(DisplayIdentificationTest, getFallbackDisplayId) {
+TEST(DisplayIdentificationTest, fromPort) {
     // Manufacturer ID should be invalid.
-    ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
-    ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu)));
+    ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0)));
+    ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu)));
 }
 
 TEST(DisplayIdentificationTest, getVirtualDisplayId) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9130b04..f0311bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,157 +14,22 @@
  * 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 "LibSurfaceFlingerUnittests"
 
-#include <type_traits>
-
-#include <android/hardware/power/Boost.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/mock/Display.h>
-#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/DisplaySurface.h>
-#include <compositionengine/mock/RenderSurface.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/mock/GraphicBufferConsumer.h>
-#include <gui/mock/GraphicBufferProducer.h>
-#include <log/log.h>
-#include <renderengine/mock/RenderEngine.h>
-#include <ui/DebugUtils.h>
-
-#include "DisplayIdentificationTest.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
-#include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
-#include "mock/system/window/MockNativeWindow.h"
+#include "DisplayTransactionTestHelpers.h"
 
 namespace android {
-namespace {
 
-namespace hal = android::hardware::graphics::composer::hal;
-
-using android::hardware::power::Boost;
-
-using testing::_;
 using testing::AnyNumber;
 using testing::DoAll;
 using testing::Mock;
-using testing::ResultOf;
 using testing::Return;
-using testing::ReturnRefOfCopy;
 using testing::SetArgPointee;
-using testing::StrictMock;
 
-using hal::ColorMode;
-using hal::Connection;
-using hal::DisplayCapability;
-using hal::DisplayType;
-using hal::Error;
-using hal::Hdr;
-using hal::HWDisplayId;
-using hal::IComposer;
-using hal::IComposerClient;
-using hal::PerFrameMetadataKey;
-using hal::PowerMode;
-using hal::RenderIntent;
+using android::hardware::graphics::composer::hal::HWDisplayId;
 
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
-using HWC2Display = TestableSurfaceFlinger::HWC2Display;
-
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
-constexpr int32_t DEFAULT_DPI = 320;
-constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
-
-constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
-
-/* ------------------------------------------------------------------------
- * Boolean avoidance
- *
- * To make calls and template instantiations more readable, we define some
- * local enums along with an implicit bool conversion.
- */
-
-#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
-
-BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
-BOOL_SUBSTITUTE(Primary);
-BOOL_SUBSTITUTE(Secure);
-BOOL_SUBSTITUTE(Virtual);
-
-/* ------------------------------------------------------------------------
- *
- */
-
-class DisplayTransactionTest : public testing::Test {
-public:
-    DisplayTransactionTest();
-    ~DisplayTransactionTest() override;
-
-    // --------------------------------------------------------------------
-    // Mock/Fake injection
-
-    void injectMockScheduler();
-    void injectMockComposer(int virtualDisplayCount);
-    void injectFakeBufferQueueFactory();
-    void injectFakeNativeWindowSurfaceFactory();
-    sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
-
-    // --------------------------------------------------------------------
-    // Postcondition helpers
-
-    bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId);
-    bool hasTransactionFlagSet(int flag);
-    bool hasDisplayDevice(sp<IBinder> displayToken);
-    sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
-    bool hasCurrentDisplayState(sp<IBinder> displayToken);
-    const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
-    bool hasDrawingDisplayState(sp<IBinder> displayToken);
-    const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
-
-    // --------------------------------------------------------------------
-    // Test instances
-
-    TestableSurfaceFlinger mFlinger;
-    sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
-    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
-    Hwc2::mock::PowerAdvisor mPowerAdvisor;
-
-    // These mocks are created by the test, but are destroyed by SurfaceFlinger
-    // by virtue of being stored into a std::unique_ptr. However we still need
-    // to keep a reference to them for use in setting up call expectations.
-    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
-    Hwc2::mock::Composer* mComposer = nullptr;
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
-    mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
-
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync;
-    mock::EventControlThread* mEventControlThread = new mock::EventControlThread;
-    mock::EventThread* mEventThread = new mock::EventThread;
-    mock::EventThread* mSFEventThread = new mock::EventThread;
-
-    // These mocks are created only when expected to be created via a factory.
-    sp<mock::GraphicBufferConsumer> mConsumer;
-    sp<mock::GraphicBufferProducer> mProducer;
-    surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-};
 
 DisplayTransactionTest::DisplayTransactionTest() {
     const ::testing::TestInfo* const test_info =
@@ -191,7 +56,7 @@
     injectMockScheduler();
     mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
-    mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
+    mFlinger.mutableInterceptor() = mSurfaceInterceptor;
 
     injectMockComposer(0);
 }
@@ -205,18 +70,20 @@
 void DisplayTransactionTest::injectMockScheduler() {
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
     EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
-                            std::unique_ptr<EventControlThread>(mEventControlThread),
+    mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
+                            std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
                             std::unique_ptr<EventThread>(mEventThread),
-                            std::unique_ptr<EventThread>(mSFEventThread));
+                            std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
 }
 
 void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
@@ -253,7 +120,7 @@
 
 sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
         std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
-    constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
     constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
     constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
     constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -327,3336 +194,4 @@
     return mFlinger.mutableDrawingState().displays.valueFor(displayToken);
 }
 
-/* ------------------------------------------------------------------------
- *
- */
-
-template <typename PhysicalDisplay>
-struct PhysicalDisplayId {};
-
-template <DisplayId::Type displayId>
-using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
-
-struct NoDisplayId {};
-
-template <typename>
-struct IsPhysicalDisplayId : std::bool_constant<false> {};
-
-template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
-
-template <typename>
-struct DisplayIdGetter;
-
-template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
-    static std::optional<DisplayId> get() {
-        if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
-            return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
-                                                ? LEGACY_DISPLAY_TYPE_PRIMARY
-                                                : LEGACY_DISPLAY_TYPE_EXTERNAL);
-        }
-
-        const auto info =
-                parseDisplayIdentificationData(PhysicalDisplay::PORT,
-                                               PhysicalDisplay::GET_IDENTIFICATION_DATA());
-        return info ? std::make_optional(info->id) : std::nullopt;
-    }
-};
-
-template <DisplayId::Type displayId>
-struct DisplayIdGetter<VirtualDisplayId<displayId>> {
-    static std::optional<DisplayId> get() { return DisplayId{displayId}; }
-};
-
-template <>
-struct DisplayIdGetter<NoDisplayId> {
-    static std::optional<DisplayId> get() { return {}; }
-};
-
-template <typename>
-struct DisplayConnectionTypeGetter {
-    static constexpr std::optional<DisplayConnectionType> value;
-};
-
-template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
-    static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
-};
-
-template <typename>
-struct HwcDisplayIdGetter {
-    static constexpr std::optional<HWDisplayId> value;
-};
-
-constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
-
-template <DisplayId::Type displayId>
-struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
-    static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
-};
-
-template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
-    static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
-};
-
-// DisplayIdType can be:
-//     1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
-//     2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
-//     3) NoDisplayId for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
-          Secure secure, Primary primary, int grallocUsage>
-struct DisplayVariant {
-    using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
-    using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
-    using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
-
-    // The display width and height
-    static constexpr int WIDTH = width;
-    static constexpr int HEIGHT = height;
-
-    static constexpr int GRALLOC_USAGE = grallocUsage;
-
-    // Whether the display is virtual or physical
-    static constexpr Virtual VIRTUAL =
-            IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
-
-    // When creating native window surfaces for the framebuffer, whether those should be critical
-    static constexpr Critical CRITICAL = critical;
-
-    // When creating native window surfaces for the framebuffer, whether those should be async
-    static constexpr Async ASYNC = async;
-
-    // Whether the display should be treated as secure
-    static constexpr Secure SECURE = secure;
-
-    // Whether the display is primary
-    static constexpr Primary PRIMARY = primary;
-
-    static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
-        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
-        if (auto displayId = DISPLAY_ID::get()) {
-            ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
-        } else {
-            ceDisplayArgs.setUseHwcVirtualDisplays(false);
-        }
-        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
-
-        auto compositionDisplay =
-                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
-                                                       ceDisplayArgs.build());
-
-        auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                                  CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
-                                                  static_cast<bool>(PRIMARY));
-
-        injector.setSecure(static_cast<bool>(SECURE));
-        injector.setNativeWindow(test->mNativeWindow);
-
-        // Creating a DisplayDevice requires getting default dimensions from the
-        // native window along with some other initial setup.
-        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
-        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
-                .WillRepeatedly(Return(0));
-
-        return injector;
-    }
-
-    // Called by tests to set up any native window creation call expectations.
-    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
-                .WillOnce(Return(test->mNativeWindow));
-
-        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
-        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
-                .WillRepeatedly(Return(0));
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
-                .WillRepeatedly(Return(0));
-    }
-
-    static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
-        EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
-        EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
-                .WillRepeatedly(Return(NO_ERROR));
-        EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
-                .WillRepeatedly(Return(NO_ERROR));
-        EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
-                .WillRepeatedly(Return(NO_ERROR));
-    }
-
-    static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
-    }
-};
-
-template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
-          typename PhysicalDisplay = void>
-struct HwcDisplayVariant {
-    // The display id supplied by the HWC
-    static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
-
-    // The HWC display type
-    static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
-
-    // The HWC active configuration id
-    static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
-    static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON;
-
-    static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
-        test->mFlinger.mutablePendingHotplugEvents().emplace_back(
-                HotplugEvent{HWC_DISPLAY_ID, connection});
-    }
-
-    // Called by tests to inject a HWC display setup
-    static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
-        const auto displayId = DisplayVariant::DISPLAY_ID::get();
-        ASSERT_TRUE(displayId);
-        FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
-                               static_cast<bool>(DisplayVariant::PRIMARY))
-                .setHwcDisplayId(HWC_DISPLAY_ID)
-                .setWidth(DisplayVariant::WIDTH)
-                .setHeight(DisplayVariant::HEIGHT)
-                .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
-                .setPowerMode(INIT_POWER_MODE)
-                .inject(&test->mFlinger, test->mComposer);
-    }
-
-    // Called by tests to inject a HWC display setup
-    static void injectHwcDisplay(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
-                .WillOnce(Return(Error::NONE));
-        injectHwcDisplayWithNoDefaultCapabilities(test);
-    }
-
-    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
-            DisplayTransactionTest* test) {
-        const ::testing::TestInfo* const test_info =
-                ::testing::UnitTest::GetInstance()->current_test_info();
-
-        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
-                                                   PhysicalDisplay::CONNECTION_TYPE})
-                                     .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
-                                     .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
-                                     .setPowerAdvisor(&test->mPowerAdvisor)
-                                     .setName(std::string("Injected display for ") +
-                                              test_info->test_case_name() + "." + test_info->name())
-                                     .build();
-
-        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
-                                                      ceDisplayArgs);
-    }
-
-    static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
-        constexpr auto CONNECTION_TYPE =
-                PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
-                ? IComposerClient::DisplayConnectionType::INTERNAL
-                : IComposerClient::DisplayConnectionType::EXTERNAL;
-
-        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
-
-        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
-                .WillOnce(Return(hal::Error::NONE));
-        EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::WIDTH, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::HEIGHT, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_X, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_Y, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::CONFIG_GROUP, _))
-                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
-
-        if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
-            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
-                    .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
-                                    SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
-                                    Return(Error::NONE)));
-        } else {
-            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
-                    .WillOnce(Return(Error::UNSUPPORTED));
-        }
-    }
-
-    // Called by tests to set up HWC call expectations
-    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
-    }
-};
-
-// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
-constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
-        GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-
-template <typename PhysicalDisplay, int width, int height, Critical critical>
-struct PhysicalDisplayVariant
-      : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
-                       Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
-                          DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
-                                         critical, Async::FALSE, Secure::TRUE,
-                                         PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-                          PhysicalDisplay> {};
-
-template <bool hasIdentificationData>
-struct PrimaryDisplay {
-    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
-    static constexpr Primary PRIMARY = Primary::TRUE;
-    static constexpr uint8_t PORT = 255;
-    static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
-    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
-    static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
-};
-
-template <bool hasIdentificationData>
-struct ExternalDisplay {
-    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
-    static constexpr Primary PRIMARY = Primary::FALSE;
-    static constexpr uint8_t PORT = 254;
-    static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
-    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
-    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-struct TertiaryDisplay {
-    static constexpr Primary PRIMARY = Primary::FALSE;
-    static constexpr uint8_t PORT = 253;
-    static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
-    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
-        PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
-
-// An external display is physical display that is not critical.
-using ExternalDisplayVariant =
-        PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
-
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
-
-// A virtual display not supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
-
-template <int width, int height, Secure secure>
-struct NonHwcVirtualDisplayVariant
-      : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
-                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
-    using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
-                                Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
-
-    static void injectHwcDisplay(DisplayTransactionTest*) {}
-
-    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
-            DisplayTransactionTest* test) {
-        const ::testing::TestInfo* const test_info =
-                ::testing::UnitTest::GetInstance()->current_test_info();
-
-        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setPixels({Base::WIDTH, Base::HEIGHT})
-                                     .setIsSecure(static_cast<bool>(Base::SECURE))
-                                     .setPowerAdvisor(&test->mPowerAdvisor)
-                                     .setName(std::string("Injected display for ") +
-                                              test_info->test_case_name() + "." + test_info->name())
-                                     .build();
-
-        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
-                                                      ceDisplayArgs);
-    }
-
-    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
-    }
-
-    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
-        Base::setupNativeWindowSurfaceCreationCallExpectations(test);
-        EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
-    }
-};
-
-// A virtual display supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
-
-template <int width, int height, Secure secure>
-struct HwcVirtualDisplayVariant
-      : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
-                       Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
-        HwcDisplayVariant<
-                HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
-                DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
-                               secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
-    using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
-                                secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
-    using Self = HwcVirtualDisplayVariant<width, height, secure>;
-
-    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
-            DisplayTransactionTest* test) {
-        const ::testing::TestInfo* const test_info =
-                ::testing::UnitTest::GetInstance()->current_test_info();
-
-        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setUseHwcVirtualDisplays(false)
-                                     .setPixels({Base::WIDTH, Base::HEIGHT})
-                                     .setIsSecure(static_cast<bool>(Base::SECURE))
-                                     .setPowerAdvisor(&test->mPowerAdvisor)
-                                     .setName(std::string("Injected display for ") +
-                                              test_info->test_case_name() + "." + test_info->name())
-                                     .build();
-
-        auto compositionDisplay =
-                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
-                                                       ceDisplayArgs);
-        compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
-
-        // Insert display data so that the HWC thinks it created the virtual display.
-        if (const auto displayId = Base::DISPLAY_ID::get()) {
-            test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-        }
-
-        return compositionDisplay;
-    }
-
-    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
-        Base::setupNativeWindowSurfaceCreationCallExpectations(test);
-        EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
-    }
-
-    static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
-                .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
-    }
-};
-
-// For this variant, SurfaceFlinger should not configure itself with wide
-// display support, so the display should not be configured for wide-color
-// support.
-struct WideColorSupportNotConfiguredVariant {
-    static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
-    static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = false;
-        test->mFlinger.mutableUseColorManagement() = false;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
-    }
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
-        EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
-        EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
-    }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, and the display should respond with an non-empty list of supported
-// color modes. Wide-color support should be configured.
-template <typename Display>
-struct WideColorP3ColorimetricSupportedVariant {
-    static constexpr bool WIDE_COLOR_SUPPORTED = true;
-
-    static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableUseColorManagement() = true;
-        test->mFlinger.mutableHasWideColorDisplay() = true;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
-    }
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
-
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
-                .WillOnce(DoAll(SetArgPointee<2>(
-                                        std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
-                                 RenderIntent::COLORIMETRIC))
-                .WillOnce(Return(Error::NONE));
-    }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, but the display should respond with an empty list of supported color
-// modes. Wide-color support for the display should not be configured.
-template <typename Display>
-struct WideColorNotSupportedVariant {
-    static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
-    static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableUseColorManagement() = true;
-        test->mFlinger.mutableHasWideColorDisplay() = true;
-    }
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
-    }
-};
-
-// For this variant, the display is not a HWC display, so no HDR support should
-// be configured.
-struct NonHwcDisplayHdrSupportVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = false;
-    static constexpr bool HDR10_SUPPORTED = false;
-    static constexpr bool HDR_HLG_SUPPORTED = false;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
-    }
-};
-
-template <typename Display>
-struct Hdr10PlusSupportedVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = true;
-    static constexpr bool HDR10_SUPPORTED = true;
-    static constexpr bool HDR_HLG_SUPPORTED = false;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
-                                        Hdr::HDR10_PLUS,
-                                        Hdr::HDR10,
-                                })),
-                                Return(Error::NONE)));
-    }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HDR10, so HDR10 support should be configured.
-template <typename Display>
-struct Hdr10SupportedVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = false;
-    static constexpr bool HDR10_SUPPORTED = true;
-    static constexpr bool HDR_HLG_SUPPORTED = false;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
-                                Return(Error::NONE)));
-    }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HLG, so HLG support should be configured.
-template <typename Display>
-struct HdrHlgSupportedVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = false;
-    static constexpr bool HDR10_SUPPORTED = false;
-    static constexpr bool HDR_HLG_SUPPORTED = true;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
-                .WillOnce(
-                        DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
-    }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
-template <typename Display>
-struct HdrDolbyVisionSupportedVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = false;
-    static constexpr bool HDR10_SUPPORTED = false;
-    static constexpr bool HDR_HLG_SUPPORTED = false;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
-                                Return(Error::NONE)));
-    }
-};
-
-// For this variant, the composer should respond with am empty list of HDR
-// modes, so no HDR support should be configured.
-template <typename Display>
-struct HdrNotSupportedVariant {
-    static constexpr bool HDR10_PLUS_SUPPORTED = false;
-    static constexpr bool HDR10_SUPPORTED = false;
-    static constexpr bool HDR_HLG_SUPPORTED = false;
-    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
-    }
-};
-
-struct NonHwcPerFrameMetadataSupportVariant {
-    static constexpr int PER_FRAME_METADATA_KEYS = 0;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
-    }
-};
-
-template <typename Display>
-struct NoPerFrameMetadataSupportVariant {
-    static constexpr int PER_FRAME_METADATA_KEYS = 0;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
-                .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
-    }
-};
-
-template <typename Display>
-struct Smpte2086PerFrameMetadataSupportVariant {
-    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
-                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
-                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
-                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
-                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
-                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
-                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
-                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
-                        PerFrameMetadataKey::WHITE_POINT_X,
-                        PerFrameMetadataKey::WHITE_POINT_Y,
-                        PerFrameMetadataKey::MAX_LUMINANCE,
-                        PerFrameMetadataKey::MIN_LUMINANCE,
-                })));
-    }
-};
-
-template <typename Display>
-struct Cta861_3_PerFrameMetadataSupportVariant {
-    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
-                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
-                        PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
-                        PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
-                })));
-    }
-};
-
-template <typename Display>
-struct Hdr10_Plus_PerFrameMetadataSupportVariant {
-    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
-                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
-                        PerFrameMetadataKey::HDR10_PLUS_SEI,
-                })));
-    }
-};
-/* ------------------------------------------------------------------------
- * Typical display configurations to test
- */
-
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
-          typename PerFrameMetadataSupportPolicy>
-struct Case {
-    using Display = DisplayPolicy;
-    using WideColorSupport = WideColorSupportPolicy;
-    using HdrSupport = HdrSupportPolicy;
-    using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
-};
-
-using SimplePrimaryDisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             HdrNotSupportedVariant<PrimaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using SimpleExternalDisplayCase =
-        Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
-             HdrNotSupportedVariant<ExternalDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
-using SimpleTertiaryDisplayCase =
-        Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
-             HdrNotSupportedVariant<TertiaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
-using NonHwcVirtualDisplayCase =
-        Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
-             WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
-             NonHwcPerFrameMetadataSupportVariant>;
-using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
-using HwcVirtualDisplayCase =
-        Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
-             HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
-using WideColorP3ColorimetricDisplayCase =
-        Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
-             HdrNotSupportedVariant<PrimaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10PlusDisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             Hdr10SupportedVariant<PrimaryDisplayVariant>,
-             Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10DisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             Hdr10SupportedVariant<PrimaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrHlgDisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             HdrHlgSupportedVariant<PrimaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrDolbyVisionDisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
-             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrSmpte2086DisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             HdrNotSupportedVariant<PrimaryDisplayVariant>,
-             Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrCta861_3_DisplayCase =
-        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
-             HdrNotSupportedVariant<PrimaryDisplayVariant>,
-             Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-
-/* ------------------------------------------------------------------------
- *
- * SurfaceFlinger::onHotplugReceived
- */
-
-TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
-    constexpr int currentSequenceId = 123;
-    constexpr HWDisplayId hwcDisplayId1 = 456;
-    constexpr HWDisplayId hwcDisplayId2 = 654;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the current sequence id for accepted events
-    mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
-    // Set the main thread id so that the current thread does not appear to be
-    // the main thread.
-    mFlinger.mutableMainThreadId() = std::thread::id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate two hotplug events (a connect and a disconnect)
-    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
-    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // All events should be in the pending event queue.
-    const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
-    ASSERT_EQ(2u, pendingEvents.size());
-    EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
-    EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
-    EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
-    EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
-}
-
-TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) {
-    constexpr int currentSequenceId = 123;
-    constexpr int otherSequenceId = 321;
-    constexpr HWDisplayId displayId = 456;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the current sequence id for accepted events
-    mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
-    // Set the main thread id so that the current thread does not appear to be
-    // the main thread.
-    mFlinger.mutableMainThreadId() = std::thread::id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We do not expect any calls to invalidate().
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Call with an unexpected sequence id
-    mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should not be set
-    EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // There should be no pending events
-    EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
-    constexpr int currentSequenceId = 123;
-    constexpr HWDisplayId displayId1 = 456;
-
-    // --------------------------------------------------------------------
-    // Note:
-    // --------------------------------------------------------------------
-    // This test case is a bit tricky. We want to verify that
-    // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
-    // don't really want to provide coverage for everything the later function
-    // does as there are specific tests for it.
-    // --------------------------------------------------------------------
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the current sequence id for accepted events
-    mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
-    // Set the main thread id so that the current thread does appear to be the
-    // main thread.
-    mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate a disconnect on a display id that is not connected. This should
-    // be enqueued by onHotplugReceived(), and dequeued by
-    // processDisplayHotplugEventsLocked(), but then ignored as invalid.
-    mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // There should be no event queued on return, as it should have been
-    // processed.
-    EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::createDisplay
- */
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
-    const String8 name("virtual.test");
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display should have been added to the current state
-    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
-    const auto& display = getCurrentDisplayState(displayToken);
-    EXPECT_TRUE(display.isVirtual());
-    EXPECT_FALSE(display.isSecure);
-    EXPECT_EQ(name.string(), display.displayName);
-
-    // --------------------------------------------------------------------
-    // Cleanup conditions
-
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) {
-    const String8 name("virtual.test");
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display should have been added to the current state
-    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
-    const auto& display = getCurrentDisplayState(displayToken);
-    EXPECT_TRUE(display.isVirtual());
-    EXPECT_TRUE(display.isSecure);
-    EXPECT_EQ(name.string(), display.displayName);
-
-    // --------------------------------------------------------------------
-    // Cleanup conditions
-
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::destroyDisplay
- */
-
-TEST_F(DisplayTransactionTest, destroyDisplayClearsCurrentStateForDisplay) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A virtual display exists
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.destroyDisplay(existing.token());
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display should have been removed from the current state
-    EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
-
-    // Ths display should still exist in the drawing state
-    EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
-
-    // The display transaction needed flasg should be set
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-}
-
-TEST_F(DisplayTransactionTest, destroyDisplayHandlesUnknownDisplay) {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    sp<BBinder> displayToken = new BBinder();
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.destroyDisplay(displayToken);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::resetDisplayState
- */
-
-TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // vsync is enabled and available
-    mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true;
-    mFlinger.scheduler()->mutableHWVsyncAvailable() = true;
-
-    // A display exists
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // The call disable vsyncs
-    EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
-
-    // The call ends any display resyncs
-    EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.resetDisplayState();
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // vsyncs should be off and not available.
-    EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled());
-    EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable());
-
-    // The display should have been removed from the display map.
-    EXPECT_FALSE(hasDisplayDevice(existing.token()));
-
-    // The display should still exist in the current state
-    EXPECT_TRUE(hasCurrentDisplayState(existing.token()));
-
-    // The display should have been removed from the drawing state
-    EXPECT_FALSE(hasDrawingDisplayState(existing.token()));
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::notifyPowerBoost
- */
-
-TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
-    mFlinger.scheduler()->replaceTouchTimer(100);
-    std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
-    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
-
-    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
-    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
-    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
-    std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
-    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
-    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
-    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
-    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
-    std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
-    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::GetBestColorMode
- */
-class GetBestColorModeTest : public DisplayTransactionTest {
-public:
-    void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
-
-    void addHwcColorModesMapping(ui::ColorMode colorMode,
-                                 std::vector<ui::RenderIntent> renderIntents) {
-        mHwcColorModes[colorMode] = renderIntents;
-    }
-
-    void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
-
-    void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
-
-    void getBestColorMode() {
-        auto displayDevice =
-                injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
-                    injector.setHwcColorModes(mHwcColorModes);
-                    injector.setHasWideColorGamut(mHasWideColorGamut);
-                    injector.setNativeWindow(mNativeWindow);
-                });
-
-        displayDevice->getCompositionDisplay()
-                ->getDisplayColorProfile()
-                ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
-                                   &mOutColorMode, &mOutRenderIntent);
-    }
-
-    ui::Dataspace mOutDataspace;
-    ui::ColorMode mOutColorMode;
-    ui::RenderIntent mOutRenderIntent;
-
-private:
-    ui::Dataspace mInputDataspace;
-    ui::RenderIntent mInputRenderIntent;
-    bool mHasWideColorGamut = false;
-    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-};
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
-    addHwcColorModesMapping(ui::ColorMode::SRGB,
-                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
-    setInputDataspace(ui::Dataspace::DISPLAY_P3);
-    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
-    setHasWideColorGamut(true);
-
-    getBestColorMode();
-
-    ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
-    ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
-    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
-    addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
-                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
-    addHwcColorModesMapping(ui::ColorMode::SRGB,
-                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
-    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
-                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
-    setInputDataspace(ui::Dataspace::DISPLAY_P3);
-    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
-    setHasWideColorGamut(true);
-
-    getBestColorMode();
-
-    ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
-    ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
-    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
-    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
-                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
-    setInputDataspace(ui::Dataspace::DISPLAY_P3);
-    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
-    setHasWideColorGamut(true);
-
-    getBestColorMode();
-
-    ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
-    ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
-    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::setProjection
- */
-
-class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
-public:
-    static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080;  // arbitrary
-    static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
-
-    static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
-    static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
-    static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
-    static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
-            HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
-
-    DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
-                                   ui::Rotation physicalOrientation)
-          : mFlingerDisplaySize(flingerDisplaySize),
-            mHardwareDisplaySize(hardwareDisplaySize),
-            mPhysicalOrientation(physicalOrientation),
-            mDisplayDevice(createDisplayDevice()) {}
-
-    sp<DisplayDevice> createDisplayDevice() {
-        return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
-            injector.setPhysicalOrientation(mPhysicalOrientation);
-        });
-    }
-
-    ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
-
-    void setProjectionForRotation0() {
-        // A logical rotation of 0 uses the SurfaceFlinger display size
-        mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
-                                      Rect(mFlingerDisplaySize));
-    }
-
-    void setProjectionForRotation90() {
-        // A logical rotation of 90 uses the SurfaceFlinger display size with
-        // the width/height swapped.
-        mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
-                                      Rect(SwapWH(mFlingerDisplaySize)));
-    }
-
-    void setProjectionForRotation180() {
-        // A logical rotation of 180 uses the SurfaceFlinger display size
-        mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
-                                      Rect(mFlingerDisplaySize));
-    }
-
-    void setProjectionForRotation270() {
-        // A logical rotation of 270 uses the SurfaceFlinger display size with
-        // the width/height swapped.
-        mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
-                                      Rect(SwapWH(mFlingerDisplaySize)));
-    }
-
-    void expectStateForHardwareTransform0() {
-        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
-        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
-                                mHardwareDisplaySize.height),
-                  compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
-        EXPECT_EQ(false, compositionState.needsFiltering);
-    }
-
-    void expectStateForHardwareTransform90() {
-        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
-        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
-                                mHardwareDisplaySize.height),
-                  compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
-        // For 90, the frame and viewport have the hardware display size width and height swapped
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
-        EXPECT_EQ(false, compositionState.needsFiltering);
-    }
-
-    void expectStateForHardwareTransform180() {
-        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
-        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
-                                mHardwareDisplaySize.height),
-                  compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
-        EXPECT_EQ(false, compositionState.needsFiltering);
-    }
-
-    void expectStateForHardwareTransform270() {
-        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
-        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
-                                mHardwareDisplaySize.height),
-                  compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
-        // For 270, the frame and viewport have the hardware display size width and height swapped
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
-        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
-        EXPECT_EQ(false, compositionState.needsFiltering);
-    }
-
-    const ui::Size mFlingerDisplaySize;
-    const ui::Size mHardwareDisplaySize;
-    const ui::Rotation mPhysicalOrientation;
-    const sp<DisplayDevice> mDisplayDevice;
-};
-
-struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
-    DisplayDeviceSetProjectionTest_Installed0()
-          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::ROTATION_0) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
-    setProjectionForRotation0();
-    expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
-    setProjectionForRotation90();
-    expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
-    setProjectionForRotation180();
-    expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
-    setProjectionForRotation270();
-    expectStateForHardwareTransform270();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
-    DisplayDeviceSetProjectionTest_Installed90()
-          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
-                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::ROTATION_90) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
-    setProjectionForRotation0();
-    expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
-    setProjectionForRotation90();
-    expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
-    setProjectionForRotation180();
-    expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
-    setProjectionForRotation270();
-    expectStateForHardwareTransform0();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
-    DisplayDeviceSetProjectionTest_Installed180()
-          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::ROTATION_180) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
-    setProjectionForRotation0();
-    expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
-    setProjectionForRotation90();
-    expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
-    setProjectionForRotation180();
-    expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
-    setProjectionForRotation270();
-    expectStateForHardwareTransform90();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
-    DisplayDeviceSetProjectionTest_Installed270()
-          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
-                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                           ui::ROTATION_270) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
-    setProjectionForRotation0();
-    expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
-    setProjectionForRotation90();
-    expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
-    setProjectionForRotation180();
-    expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
-    setProjectionForRotation270();
-    expectStateForHardwareTransform180();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::getDisplayNativePrimaries
- */
-
-class GetDisplayNativePrimaries : public DisplayTransactionTest {
-public:
-    GetDisplayNativePrimaries();
-    void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
-    void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
-
-private:
-    static constexpr float mStartingTestValue = 1.0f;
-};
-
-GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
-    SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
-    injectFakeNativeWindowSurfaceFactory();
-}
-
-void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
-        ui::DisplayPrimaries& primaries) {
-    float startingVal = mStartingTestValue;
-    primaries.red.X = startingVal++;
-    primaries.red.Y = startingVal++;
-    primaries.red.Z = startingVal++;
-    primaries.green.X = startingVal++;
-    primaries.green.Y = startingVal++;
-    primaries.green.Z = startingVal++;
-    primaries.blue.X = startingVal++;
-    primaries.blue.Y = startingVal++;
-    primaries.blue.Z = startingVal++;
-    primaries.white.X = startingVal++;
-    primaries.white.Y = startingVal++;
-    primaries.white.Z = startingVal++;
-}
-
-void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
-        const ui::DisplayPrimaries& primaries) {
-    float startingVal = mStartingTestValue;
-    EXPECT_EQ(primaries.red.X, startingVal++);
-    EXPECT_EQ(primaries.red.Y, startingVal++);
-    EXPECT_EQ(primaries.red.Z, startingVal++);
-    EXPECT_EQ(primaries.green.X, startingVal++);
-    EXPECT_EQ(primaries.green.Y, startingVal++);
-    EXPECT_EQ(primaries.green.Z, startingVal++);
-    EXPECT_EQ(primaries.blue.X, startingVal++);
-    EXPECT_EQ(primaries.blue.Y, startingVal++);
-    EXPECT_EQ(primaries.blue.Z, startingVal++);
-    EXPECT_EQ(primaries.white.X, startingVal++);
-    EXPECT_EQ(primaries.white.Y, startingVal++);
-    EXPECT_EQ(primaries.white.Z, startingVal++);
-}
-
-TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
-    ui::DisplayPrimaries primaries;
-    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
-}
-
-TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
-    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
-    injector.inject();
-    auto internalDisplayToken = injector.token();
-
-    ui::DisplayPrimaries expectedPrimaries;
-    populateDummyDisplayNativePrimaries(expectedPrimaries);
-    mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
-
-    ui::DisplayPrimaries primaries;
-    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
-
-    checkDummyDisplayNativePrimaries(primaries);
-}
-
-TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
-    sp<BBinder> notInternalDisplayToken = new BBinder();
-
-    ui::DisplayPrimaries primaries;
-    populateDummyDisplayNativePrimaries(primaries);
-    EXPECT_EQ(NAME_NOT_FOUND,
-              mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
-
-    // Check primaries argument wasn't modified in case of failure
-    checkDummyDisplayNativePrimaries(primaries);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setupNewDisplayDeviceInternal
- */
-
-class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
-public:
-    template <typename T>
-    void setupNewDisplayDeviceInternalTest();
-};
-
-template <typename Case>
-void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
-    const sp<BBinder> displayToken = new BBinder();
-    const sp<compositionengine::mock::DisplaySurface> displaySurface =
-            new compositionengine::mock::DisplaySurface();
-    const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Wide color displays support is configured appropriately
-    Case::WideColorSupport::injectConfigChange(this);
-
-    // The display is setup with the HWC.
-    Case::Display::injectHwcDisplay(this);
-
-    // SurfaceFlinger will use a test-controlled factory for native window
-    // surfaces.
-    injectFakeNativeWindowSurfaceFactory();
-
-    // A compositionengine::Display has already been created
-    auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // Various native window calls will be made.
-    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-    Case::WideColorSupport::setupComposerCallExpectations(this);
-    Case::HdrSupport::setupComposerCallExpectations(this);
-    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    DisplayDeviceState state;
-    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = Case::Display::DISPLAY_ID::get();
-        ASSERT_TRUE(displayId);
-        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
-        ASSERT_TRUE(hwcDisplayId);
-        state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
-    }
-
-    state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
-    auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
-                                                         displaySurface, producer);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    ASSERT_TRUE(device != nullptr);
-    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
-    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
-    EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
-    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-    EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
-    EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
-    EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
-    EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
-    EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
-    EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
-    EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
-    // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
-    // remapped, and the test only ever sets up one config. If there were an error
-    // looking up the remapped index, device->getActiveConfig() would be -1 instead.
-    EXPECT_EQ(0, device->getActiveConfig().value());
-    EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
-              device->getSupportedPerFrameMetadata());
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
-    setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
-    setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
-    setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
-    setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
-    setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
-    setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
-    setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
-    setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
-    setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
-    setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
-    setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
- */
-
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
-    template <typename Case>
-    void setupCommonPreconditions();
-
-    template <typename Case, bool connected>
-    static void expectHotplugReceived(mock::EventThread*);
-
-    template <typename Case>
-    void setupCommonCallExpectationsForConnectProcessing();
-
-    template <typename Case>
-    void setupCommonCallExpectationsForDisconnectProcessing();
-
-    template <typename Case>
-    void processesHotplugConnectCommon();
-
-    template <typename Case>
-    void ignoresHotplugConnectCommon();
-
-    template <typename Case>
-    void processesHotplugDisconnectCommon();
-
-    template <typename Case>
-    void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
-
-    template <typename Case>
-    void verifyPhysicalDisplayIsConnected();
-
-    void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
-};
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
-    // Wide color displays support is configured appropriately
-    Case::WideColorSupport::injectConfigChange(this);
-
-    // SurfaceFlinger will use a test-controlled factory for BufferQueues
-    injectFakeBufferQueueFactory();
-
-    // SurfaceFlinger will use a test-controlled factory for native window
-    // surfaces.
-    injectFakeNativeWindowSurfaceFactory();
-}
-
-template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
-    const auto convert = [](auto physicalDisplayId) {
-        return std::make_optional(DisplayId{physicalDisplayId});
-    };
-
-    EXPECT_CALL(*eventThread,
-                onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
-            .Times(1);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
-    Case::Display::setupHwcHotplugCallExpectations(this);
-
-    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
-    Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
-    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
-    Case::WideColorSupport::setupComposerCallExpectations(this);
-    Case::HdrSupport::setupComposerCallExpectations(this);
-    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-    expectHotplugReceived<Case, true>(mEventThread);
-    expectHotplugReceived<Case, true>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
-    expectHotplugReceived<Case, false>(mEventThread);
-    expectHotplugReceived<Case, false>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
-    // The display device should have been set up in the list of displays.
-    ASSERT_TRUE(hasDisplayDevice(displayToken));
-    const auto& device = getDisplayDevice(displayToken);
-    EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
-    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-
-    std::optional<DisplayDeviceState::Physical> expectedPhysical;
-    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = Case::Display::DISPLAY_ID::get();
-        ASSERT_TRUE(displayId);
-        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
-        ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {.id = *displayId,
-                            .type = *connectionType,
-                            .hwcDisplayId = *hwcDisplayId};
-    }
-
-    // The display should have been set up in the current display state
-    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
-    const auto& current = getCurrentDisplayState(displayToken);
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
-    EXPECT_EQ(expectedPhysical, current.physical);
-
-    // The display should have been set up in the drawing display state
-    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
-    const auto& draw = getDrawingDisplayState(displayToken);
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-    EXPECT_EQ(expectedPhysical, draw.physical);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
-    // HWComposer should have an entry for the display
-    EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
-    // SF should have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
-    auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
-
-    verifyDisplayIsConnected<Case>(displayToken);
-}
-
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
-    EXPECT_FALSE(hasDisplayDevice(displayToken));
-    EXPECT_FALSE(hasCurrentDisplayState(displayToken));
-    EXPECT_FALSE(hasDrawingDisplayState(displayToken));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    setupCommonPreconditions<Case>();
-
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillOnce(Return(false));
-
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    verifyPhysicalDisplayIsConnected<Case>();
-
-    // --------------------------------------------------------------------
-    // Cleanup conditions
-
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    setupCommonPreconditions<Case>();
-
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    setupCommonPreconditions<Case>();
-
-    // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
-    // The display is already completely set up.
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
-            .Times(0);
-
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
-    // SF should not have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
-    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest,
-       processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
-    // Inject an external display.
-    ExternalDisplayVariant::injectHwcDisplay(this);
-
-    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
-    // Inject a primary display.
-    PrimaryDisplayVariant::injectHwcDisplay(this);
-
-    processesHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
-    // Inject both a primary and external display.
-    PrimaryDisplayVariant::injectHwcDisplay(this);
-    ExternalDisplayVariant::injectHwcDisplay(this);
-
-    // TODO: This is an unnecessary call.
-    EXPECT_CALL(*mComposer,
-                getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
-            .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
-                            SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
-                            Return(Error::NONE)));
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
-    ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfExternalForVrComposer) {
-    // Inject a primary display.
-    PrimaryDisplayVariant::injectHwcDisplay(this);
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(true));
-
-    ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
-    processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
-    processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    setupCommonPreconditions<Case>();
-
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-    // A hotplug disconnect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
-    // SF should not have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    setupCommonPreconditions<Case>();
-
-    // The display is already completely set up.
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
-
-    // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-    // A hotplug connect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
-    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
-
-    // A new display should be connected in its place
-
-    verifyPhysicalDisplayIsConnected<Case>();
-
-    // --------------------------------------------------------------------
-    // Cleanup conditions
-
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
-    using Case = HwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // The HWC supports at least one virtual display
-    injectMockComposer(1);
-
-    setupCommonPreconditions<Case>();
-
-    // A virtual display was added to the current state, and it has a
-    // surface(producer)
-    sp<BBinder> displayToken = new BBinder();
-
-    DisplayDeviceState state;
-    state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
-    sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
-    state.surface = surface;
-    mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
-    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
-    EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
-    EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
-    EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
-                                  Return(NO_ERROR)));
-    EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
-
-    EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
-
-    EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
-    EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
-
-    Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
-    Case::WideColorSupport::setupComposerCallExpectations(this);
-    Case::HdrSupport::setupComposerCallExpectations(this);
-    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display device should have been set up in the list of displays.
-    verifyDisplayIsConnected<Case>(displayToken);
-
-    // --------------------------------------------------------------------
-    // Cleanup conditions
-
-    EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
-    // Cleanup
-    mFlinger.mutableCurrentState().displays.removeItem(displayToken);
-    mFlinger.mutableDrawingState().displays.removeItem(displayToken);
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
-    using Case = HwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // The HWC supports at least one virtual display
-    injectMockComposer(1);
-
-    setupCommonPreconditions<Case>();
-
-    // A virtual display was added to the current state, but it does not have a
-    // surface.
-    sp<BBinder> displayToken = new BBinder();
-
-    DisplayDeviceState state;
-    state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
-    mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // There will not be a display device set up.
-    EXPECT_FALSE(hasDisplayDevice(displayToken));
-
-    // The drawing display state will be set from the current display state.
-    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
-    const auto& draw = getDrawingDisplayState(displayToken);
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
-    using Case = HwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A virtual display is set up but is removed from the current state.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
-    mFlinger.mutableCurrentState().displays.removeItem(existing.token());
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    constexpr uint32_t oldLayerStack = 0u;
-    constexpr uint32_t newLayerStack = 123u;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a change to the layerStack state
-    display.mutableDrawingDisplayState().layerStack = oldLayerStack;
-    display.mutableCurrentDisplayState().layerStack = newLayerStack;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    constexpr ui::Rotation oldTransform = ui::ROTATION_0;
-    constexpr ui::Rotation newTransform = ui::ROTATION_180;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a change to the orientation state
-    display.mutableDrawingDisplayState().orientation = oldTransform;
-    display.mutableCurrentDisplayState().orientation = newTransform;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    const Rect oldViewport(0, 0, 0, 0);
-    const Rect newViewport(0, 0, 123, 456);
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().viewport = oldViewport;
-    display.mutableCurrentDisplayState().viewport = newViewport;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    const Rect oldFrame(0, 0, 0, 0);
-    const Rect newFrame(0, 0, 123, 456);
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().frame = oldFrame;
-    display.mutableCurrentDisplayState().frame = newFrame;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    constexpr int oldWidth = 0;
-    constexpr int oldHeight = 10;
-    constexpr int newWidth = 123;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new compositionengine::mock::DisplaySurface();
-    sp<GraphicBuffer> buf = new GraphicBuffer();
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.setNativeWindow(nativeWindow);
-    display.setDisplaySurface(displaySurface);
-    // Setup injection expections
-    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
-    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
-    display.inject();
-
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().width = oldWidth;
-    display.mutableDrawingDisplayState().height = oldHeight;
-    display.mutableCurrentDisplayState().width = newWidth;
-    display.mutableCurrentDisplayState().height = oldHeight;
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
-    using Case = NonHwcVirtualDisplayCase;
-
-    constexpr int oldWidth = 0;
-    constexpr int oldHeight = 10;
-    constexpr int newHeight = 123;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new compositionengine::mock::DisplaySurface();
-    sp<GraphicBuffer> buf = new GraphicBuffer();
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.setNativeWindow(nativeWindow);
-    display.setDisplaySurface(displaySurface);
-    // Setup injection expections
-    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
-    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
-    display.inject();
-
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().width = oldWidth;
-    display.mutableDrawingDisplayState().height = oldHeight;
-    display.mutableCurrentDisplayState().width = oldWidth;
-    display.mutableCurrentDisplayState().height = newHeight;
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setDisplayStateLocked
- */
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // We have an unknown display token not associated with a known display
-    sp<BBinder> displayToken = new BBinder();
-
-    // The requested display state references the unknown display.
-    DisplayState state;
-    state.what = DisplayState::eLayerStackChanged;
-    state.token = displayToken;
-    state.layerStack = 456;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The display token still doesn't match anything known.
-    EXPECT_FALSE(hasCurrentDisplayState(displayToken));
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is already set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // No changes are made to the display
-    DisplayState state;
-    state.what = 0;
-    state.token = display.token();
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is already set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a surface that can be set.
-    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
-    // The current display state has the surface set
-    display.mutableCurrentDisplayState().surface = surface;
-
-    // The incoming request sets the same surface
-    DisplayState state;
-    state.what = DisplayState::eSurfaceChanged;
-    state.token = display.token();
-    state.surface = surface;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The current display state is unchanged.
-    EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is already set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // There is a surface that can be set.
-    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
-    // The current display state does not have a surface
-    display.mutableCurrentDisplayState().surface = nullptr;
-
-    // The incoming request sets a surface
-    DisplayState state;
-    state.what = DisplayState::eSurfaceChanged;
-    state.token = display.token();
-    state.surface = surface;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display layer stack state is set to the new value
-    EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is already set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 456u;
-
-    // The incoming request sets the same layer stack
-    DisplayState state;
-    state.what = DisplayState::eLayerStackChanged;
-    state.token = display.token();
-    state.layerStack = 456u;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The current display state is unchanged
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 654u;
-
-    // The incoming request sets a different layer stack
-    DisplayState state;
-    state.what = DisplayState::eLayerStackChanged;
-    state.token = display.token();
-    state.layerStack = 456u;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The desired display state has been set to the new value.
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
-    using Case = SimplePrimaryDisplayCase;
-    constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
-    const Rect initialFrame = {1, 2, 3, 4};
-    const Rect initialViewport = {5, 6, 7, 8};
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The current display state projection state is all set
-    display.mutableCurrentDisplayState().orientation = initialOrientation;
-    display.mutableCurrentDisplayState().frame = initialFrame;
-    display.mutableCurrentDisplayState().viewport = initialViewport;
-
-    // The incoming request sets the same projection state
-    DisplayState state;
-    state.what = DisplayState::eDisplayProjectionChanged;
-    state.token = display.token();
-    state.orientation = initialOrientation;
-    state.frame = initialFrame;
-    state.viewport = initialViewport;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The current display state is unchanged
-    EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
-
-    EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame);
-    EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
-    using Case = SimplePrimaryDisplayCase;
-    constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
-    constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The current display state has an orientation set
-    display.mutableCurrentDisplayState().orientation = initialOrientation;
-
-    // The incoming request sets a different orientation
-    DisplayState state;
-    state.what = DisplayState::eDisplayProjectionChanged;
-    state.token = display.token();
-    state.orientation = desiredOrientation;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display state has the new value.
-    EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
-    using Case = SimplePrimaryDisplayCase;
-    const Rect initialFrame = {0, 0, 0, 0};
-    const Rect desiredFrame = {5, 6, 7, 8};
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The current display state does not have a frame
-    display.mutableCurrentDisplayState().frame = initialFrame;
-
-    // The incoming request sets a frame
-    DisplayState state;
-    state.what = DisplayState::eDisplayProjectionChanged;
-    state.token = display.token();
-    state.frame = desiredFrame;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display state has the new value.
-    EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) {
-    using Case = SimplePrimaryDisplayCase;
-    const Rect initialViewport = {0, 0, 0, 0};
-    const Rect desiredViewport = {5, 6, 7, 8};
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The current display state does not have a viewport
-    display.mutableCurrentDisplayState().viewport = initialViewport;
-
-    // The incoming request sets a viewport
-    DisplayState state;
-    state.what = DisplayState::eDisplayProjectionChanged;
-    state.token = display.token();
-    state.viewport = desiredViewport;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display state has the new value.
-    EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
-    using Case = SimplePrimaryDisplayCase;
-    constexpr uint32_t initialWidth = 1024;
-    constexpr uint32_t initialHeight = 768;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The current display state has a size set
-    display.mutableCurrentDisplayState().width = initialWidth;
-    display.mutableCurrentDisplayState().height = initialHeight;
-
-    // The incoming request sets the same display size
-    DisplayState state;
-    state.what = DisplayState::eDisplaySizeChanged;
-    state.token = display.token();
-    state.width = initialWidth;
-    state.height = initialHeight;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The current display state is unchanged
-    EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
-    EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
-    using Case = SimplePrimaryDisplayCase;
-    constexpr uint32_t initialWidth = 0;
-    constexpr uint32_t desiredWidth = 1024;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display does not yet have a width
-    display.mutableCurrentDisplayState().width = initialWidth;
-
-    // The incoming request sets a display width
-    DisplayState state;
-    state.what = DisplayState::eDisplaySizeChanged;
-    state.token = display.token();
-    state.width = desiredWidth;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display state has the new value.
-    EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
-    using Case = SimplePrimaryDisplayCase;
-    constexpr uint32_t initialHeight = 0;
-    constexpr uint32_t desiredHeight = 768;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display does not yet have a height
-    display.mutableCurrentDisplayState().height = initialHeight;
-
-    // The incoming request sets a display height
-    DisplayState state;
-    state.what = DisplayState::eDisplaySizeChanged;
-    state.token = display.token();
-    state.height = desiredHeight;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags indicate a transaction is needed
-    EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
-    // The current display state has the new value.
-    EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::onInitializeDisplays
- */
-
-TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A primary display is set up
-    Case::Display::injectHwcDisplay(this);
-    auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
-    primaryDisplay.inject();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect the surface interceptor to possibly be used, but we treat it as
-    // disabled since it is called as a side effect rather than directly by this
-    // function.
-    EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
-    // We expect a call to get the active display config.
-    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.onInitializeDisplays();
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The primary display should have a current state
-    ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
-    const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
-    // The layer stack state should be set to zero
-    EXPECT_EQ(0u, primaryDisplayState.layerStack);
-    // The orientation state should be set to zero
-    EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
-    // The frame state should be set to INVALID
-    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
-
-    // The viewport state should be set to INVALID
-    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport);
-
-    // The width and height should both be zero
-    EXPECT_EQ(0u, primaryDisplayState.width);
-    EXPECT_EQ(0u, primaryDisplayState.height);
-
-    // The display should be set to PowerMode::ON
-    ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
-    auto displayDevice = primaryDisplay.mutableDisplayDevice();
-    EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
-
-    // The display refresh period should be set in the frame tracker.
-    FrameStats stats;
-    mFlinger.getAnimFrameTracker().getStats(&stats);
-    EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // The compositor timing should be set to default values
-    const auto& compositorTiming = mFlinger.getCompositorTiming();
-    EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
-    EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
-    EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setPowerModeInternal
- */
-
-// Used when we simulate a display that supports doze.
-template <typename Display>
-struct DozeIsSupportedVariant {
-    static constexpr bool DOZE_SUPPORTED = true;
-    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
-            IComposerClient::PowerMode::DOZE;
-    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
-            IComposerClient::PowerMode::DOZE_SUSPEND;
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(
-                                        std::vector<DisplayCapability>({DisplayCapability::DOZE})),
-                                Return(Error::NONE)));
-    }
-};
-
-template <typename Display>
-// Used when we simulate a display that does not support doze.
-struct DozeNotSupportedVariant {
-    static constexpr bool DOZE_SUPPORTED = false;
-    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
-            IComposerClient::PowerMode::ON;
-    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
-            IComposerClient::PowerMode::ON;
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
-                                Return(Error::NONE)));
-    }
-};
-
-struct EventThreadBaseSupportedVariant {
-    static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
-        // The event control thread should not be notified.
-        EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
-
-        // The event thread should not be notified.
-        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
-        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
-    }
-};
-
-struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
-    static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // These calls are only expected for the primary display.
-
-        // Instead expect no calls.
-        setupEventAndEventControlThreadNoCallExpectations(test);
-    }
-
-    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // These calls are only expected for the primary display.
-
-        // Instead expect no calls.
-        setupEventAndEventControlThreadNoCallExpectations(test);
-    }
-};
-
-struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
-    static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // The event control thread should be notified to enable vsyncs
-        EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1);
-
-        // The event thread should be notified that the screen was acquired.
-        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
-    }
-
-    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // There should be a call to setVsyncEnabled(false)
-        EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
-
-        // The event thread should not be notified that the screen was released.
-        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
-    }
-};
-
-struct DispSyncIsSupportedVariant {
-    static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
-        EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
-    }
-
-    static void setupEndResyncCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1);
-    }
-};
-
-struct DispSyncNotSupportedVariant {
-    static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
-    static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// There are a large number of transitions we could test, however we only test a
-// selected subset which provides complete test coverage of the implementation.
-// --------------------------------------------------------------------
-
-template <PowerMode initialPowerMode, PowerMode targetPowerMode>
-struct TransitionVariantCommon {
-    static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
-    static constexpr auto TARGET_POWER_MODE = targetPowerMode;
-
-    static void verifyPostconditions(DisplayTransactionTest*) {}
-};
-
-struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
-        Case::setupRepaintEverythingCallExpectations(test);
-    }
-
-    static void verifyPostconditions(DisplayTransactionTest* test) {
-        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
-    }
-};
-
-struct TransitionOffToDozeSuspendVariant
-      : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
-        Case::setupRepaintEverythingCallExpectations(test);
-    }
-
-    static void verifyPostconditions(DisplayTransactionTest* test) {
-        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
-    }
-};
-
-struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
-        Case::DispSync::setupEndResyncCallExpectations(test);
-        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
-    }
-
-    static void verifyPostconditions(DisplayTransactionTest* test) {
-        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-    }
-};
-
-struct TransitionDozeSuspendToOffVariant
-      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
-        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
-    }
-
-    static void verifyPostconditions(DisplayTransactionTest* test) {
-        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-    }
-};
-
-struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
-        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
-    }
-};
-
-struct TransitionDozeSuspendToDozeVariant
-      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
-        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
-    }
-};
-
-struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
-        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
-    }
-};
-
-struct TransitionDozeSuspendToOnVariant
-      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
-        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
-    }
-};
-
-struct TransitionOnToDozeSuspendVariant
-      : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
-        Case::DispSync::setupEndResyncCallExpectations(test);
-        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-    }
-};
-
-struct TransitionOnToUnknownVariant
-      : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
-    template <typename Case>
-    static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
-        Case::setupNoComposerPowerModeCallExpectations(test);
-    }
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// Rather than testing the cartesian product of of
-// DozeIsSupported/DozeNotSupported with all other options, we use one for one
-// display type, and the other for another display type.
-// --------------------------------------------------------------------
-
-template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
-          typename DispSyncVariant, typename TransitionVariant>
-struct DisplayPowerCase {
-    using Display = DisplayVariant;
-    using Doze = DozeVariant;
-    using EventThread = EventThreadVariant;
-    using DispSync = DispSyncVariant;
-    using Transition = TransitionVariant;
-
-    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
-        Display::injectHwcDisplayWithNoDefaultCapabilities(test);
-        auto display = Display::makeFakeExistingDisplayInjector(test);
-        display.inject();
-        display.mutableDisplayDevice()->setPowerMode(mode);
-        return display;
-    }
-
-    static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
-        test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
-    }
-
-    static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
-    }
-
-    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
-                                                        PowerMode mode) {
-        EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
-        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
-                .Times(1);
-    }
-
-    static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
-        // Any calls to get the active config will return a default value.
-        EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
-                                      Return(Error::NONE)));
-
-        // Any calls to get whether the display supports dozing will return the value set by the
-        // policy variant.
-        EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
-
-        EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
-    }
-
-    static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
-    }
-};
-
-// A sample configuration for the primary display.
-// In addition to having event thread support, we emulate doze support.
-template <typename TransitionVariant>
-using PrimaryDisplayPowerCase =
-        DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
-                         EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
-                         TransitionVariant>;
-
-// A sample configuration for the external display.
-// In addition to not having event thread support, we emulate not having doze
-// support.
-template <typename TransitionVariant>
-using ExternalDisplayPowerCase =
-        DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
-                         EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
-                         TransitionVariant>;
-
-class SetPowerModeInternalTest : public DisplayTransactionTest {
-public:
-    template <typename Case>
-    void transitionDisplayCommon();
-};
-
-template <PowerMode PowerMode>
-struct PowerModeInitialVSyncEnabled : public std::false_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
-
-template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    Case::Doze::setupComposerCallExpectations(this);
-    auto display =
-            Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
-    Case::setInitialPrimaryHWVsyncEnabled(this,
-                                          PowerModeInitialVSyncEnabled<
-                                                  Case::Transition::INITIAL_POWER_MODE>::value);
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
-    Case::Transition::template setupCallExpectations<Case>(this);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
-                                  Case::Transition::TARGET_POWER_MODE);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    Case::Transition::verifyPostconditions(this);
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // A primary display device is set up
-    Case::Display::injectHwcDisplay(this);
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display is already set to PowerMode::ON
-    display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
-    using Case = HwcVirtualDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Insert display data so that the HWC thinks it created the virtual display.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
-    // A virtual display device is set up
-    Case::Display::injectHwcDisplay(this);
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The display is set to PowerMode::ON
-    getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
-    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
-    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-} // namespace
 } // 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/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
new file mode 100644
index 0000000..01cdb28
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -0,0 +1,756 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <type_traits>
+#include "DisplayIdentificationTest.h"
+
+#include <binder/IPCThreadState.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/MockNativeWindowSurface.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+// TODO: Do not polute the android namespace
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+using hal::ColorMode;
+using hal::Connection;
+using hal::DisplayCapability;
+using hal::DisplayType;
+using hal::Error;
+using hal::Hdr;
+using hal::HWDisplayId;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PerFrameMetadataKey;
+using hal::PowerMode;
+
+class DisplayTransactionTest : public testing::Test {
+public:
+    ~DisplayTransactionTest() override;
+
+    // --------------------------------------------------------------------
+    // Mock/Fake injection
+
+    void injectMockScheduler();
+    void injectMockComposer(int virtualDisplayCount);
+    void injectFakeBufferQueueFactory();
+    void injectFakeNativeWindowSurfaceFactory();
+    sp<DisplayDevice> injectDefaultInternalDisplay(
+            std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+
+    // --------------------------------------------------------------------
+    // Postcondition helpers
+
+    bool hasPhysicalHwcDisplay(hal::HWDisplayId hwcDisplayId);
+    bool hasTransactionFlagSet(int flag);
+    bool hasDisplayDevice(sp<IBinder> displayToken);
+    sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
+    bool hasCurrentDisplayState(sp<IBinder> displayToken);
+    const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
+    bool hasDrawingDisplayState(sp<IBinder> displayToken);
+    const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
+
+    // --------------------------------------------------------------------
+    // Test instances
+
+    TestableSurfaceFlinger mFlinger;
+    sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
+
+    // These mocks are created by the test, but are destroyed by SurfaceFlinger
+    // by virtue of being stored into a std::unique_ptr. However we still need
+    // to keep a reference to them for use in setting up call expectations.
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    Hwc2::mock::Composer* mComposer = nullptr;
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
+
+    mock::VsyncController* mVsyncController = new mock::VsyncController;
+    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
+    mock::SchedulerCallback mSchedulerCallback;
+    mock::EventThread* mEventThread = new mock::EventThread;
+    mock::EventThread* mSFEventThread = new mock::EventThread;
+
+    // These mocks are created only when expected to be created via a factory.
+    sp<mock::GraphicBufferConsumer> mConsumer;
+    sp<mock::GraphicBufferProducer> mProducer;
+    surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
+
+protected:
+    DisplayTransactionTest();
+};
+
+constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
+constexpr int32_t DEFAULT_DPI = 320;
+constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+
+constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
+
+/* ------------------------------------------------------------------------
+ * Boolean avoidance
+ *
+ * To make calls and template instantiations more readable, we define some
+ * local enums along with an implicit bool conversion.
+ */
+
+#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
+
+BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
+BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
+
+template <typename PhysicalDisplay>
+struct PhysicalDisplayIdType {};
+
+template <uint64_t displayId>
+using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
+
+struct GpuVirtualDisplayIdType {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+    static PhysicalDisplayId get() {
+        if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+            return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+                                                       ? LEGACY_DISPLAY_TYPE_PRIMARY
+                                                       : LEGACY_DISPLAY_TYPE_EXTERNAL);
+        }
+
+        const auto info =
+                parseDisplayIdentificationData(PhysicalDisplay::PORT,
+                                               PhysicalDisplay::GET_IDENTIFICATION_DATA());
+        return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
+    }
+};
+
+template <uint64_t displayId>
+struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+    static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+};
+
+template <>
+struct DisplayIdGetter<GpuVirtualDisplayIdType> {
+    static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+};
+
+template <typename>
+struct DisplayConnectionTypeGetter {
+    static constexpr std::optional<DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+    static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
+};
+
+template <typename>
+struct HwcDisplayIdGetter {
+    static constexpr std::optional<HWDisplayId> value;
+};
+
+constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+    static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+    static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
+// DisplayIdType can be:
+//     1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+//     2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
+//     3) GpuVirtualDisplayIdType for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+          Secure secure, Primary primary, int grallocUsage>
+struct DisplayVariant {
+    using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+    using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+    using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+
+    // The display width and height
+    static constexpr int WIDTH = width;
+    static constexpr int HEIGHT = height;
+
+    static constexpr int GRALLOC_USAGE = grallocUsage;
+
+    // Whether the display is virtual or physical
+    static constexpr Virtual VIRTUAL =
+            IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
+
+    // When creating native window surfaces for the framebuffer, whether those should be critical
+    static constexpr Critical CRITICAL = critical;
+
+    // When creating native window surfaces for the framebuffer, whether those should be async
+    static constexpr Async ASYNC = async;
+
+    // Whether the display should be treated as secure
+    static constexpr Secure SECURE = secure;
+
+    // Whether the display is primary
+    static constexpr Primary PRIMARY = primary;
+
+    static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+        if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
+            ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+        } else {
+            // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
+            // from calling into HWComposer. This way all virtual displays will get
+            // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
+            // In this case we later override it by calling display.setDisplayIdForTesting().
+            ceDisplayArgs.setUseHwcVirtualDisplays(false);
+
+            GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
+                                                           .value_or(GpuVirtualDisplayId(0));
+
+            ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                    .WillByDefault(Return(desiredDisplayId));
+
+            auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
+            ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
+        }
+        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs.build());
+
+        if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
+            // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
+            // override it with the correct HalVirtualDisplayId.
+            compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
+        }
+
+        auto injector =
+                TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
+                                                                  compositionDisplay,
+                                                                  CONNECTION_TYPE::value,
+                                                                  HWC_DISPLAY_ID_OPT::value,
+                                                                  static_cast<bool>(PRIMARY));
+
+        injector.setSecure(static_cast<bool>(SECURE));
+        injector.setNativeWindow(test->mNativeWindow);
+
+        // Creating a DisplayDevice requires getting default dimensions from the
+        // native window along with some other initial setup.
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+                .WillRepeatedly(Return(0));
+
+        return injector;
+    }
+
+    // Called by tests to set up any native window creation call expectations.
+    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
+                .WillOnce(Return(test->mNativeWindow));
+
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+                .WillRepeatedly(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+                .WillRepeatedly(Return(0));
+    }
+
+    static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
+        EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
+                .WillRepeatedly(Return(NO_ERROR));
+        EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
+                .WillRepeatedly(Return(NO_ERROR));
+        EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
+                .WillRepeatedly(Return(NO_ERROR));
+    }
+
+    static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
+    }
+};
+
+template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
+          typename PhysicalDisplay = void>
+struct HwcDisplayVariant {
+    // The display id supplied by the HWC
+    static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
+
+    // The HWC display type
+    static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
+
+    // The HWC active configuration id
+    static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+    static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
+
+    static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
+        test->mFlinger.mutablePendingHotplugEvents().emplace_back(
+                TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection});
+    }
+
+    // Called by tests to inject a HWC display setup
+    static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
+        const auto displayId = DisplayVariant::DISPLAY_ID::get();
+        ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
+        TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+                                                       static_cast<bool>(DisplayVariant::PRIMARY))
+                .setHwcDisplayId(HWC_DISPLAY_ID)
+                .setWidth(DisplayVariant::WIDTH)
+                .setHeight(DisplayVariant::HEIGHT)
+                .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
+                .setPowerMode(INIT_POWER_MODE)
+                .inject(&test->mFlinger, test->mComposer);
+    }
+
+    // Called by tests to inject a HWC display setup
+    static void injectHwcDisplay(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+                .WillOnce(Return(Error::NONE));
+        injectHwcDisplayWithNoDefaultCapabilities(test);
+    }
+
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPhysical({DisplayVariant::DISPLAY_ID::get(),
+                                                   PhysicalDisplay::CONNECTION_TYPE})
+                                     .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
+    static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
+        constexpr auto CONNECTION_TYPE =
+                PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
+                ? IComposerClient::DisplayConnectionType::INTERNAL
+                : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+
+        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
+                .WillOnce(Return(hal::Error::NONE));
+        EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
+                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::WIDTH, _))
+                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::HEIGHT, _))
+                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
+                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::DPI_X, _))
+                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::DPI_Y, _))
+                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::CONFIG_GROUP, _))
+                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+
+        if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+                    .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+                                    SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+                                    Return(Error::NONE)));
+        } else {
+            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+                    .WillOnce(Return(Error::UNSUPPORTED));
+        }
+    }
+
+    // Called by tests to set up HWC call expectations
+    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+    }
+};
+
+// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
+constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
+        GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+
+template <typename PhysicalDisplay, int width, int height, Critical critical>
+struct PhysicalDisplayVariant
+      : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+                       Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+                       GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+                          DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+                                         critical, Async::FALSE, Secure::TRUE,
+                                         PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+                          PhysicalDisplay> {};
+
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
+    static constexpr Primary PRIMARY = Primary::TRUE;
+    static constexpr uint8_t PORT = 255;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
+    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+    static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
+    static constexpr Primary PRIMARY = Primary::FALSE;
+    static constexpr uint8_t PORT = 254;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+    static constexpr Primary PRIMARY = Primary::FALSE;
+    static constexpr uint8_t PORT = 253;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
+    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+// A primary display is a physical display that is critical
+using PrimaryDisplayVariant =
+        PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+
+// An external display is physical display that is not critical.
+using ExternalDisplayVariant =
+        PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+
+// A virtual display not supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+
+template <int width, int height, Secure secure>
+struct NonHwcVirtualDisplayVariant
+      : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
+                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+    using Base =
+            DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
+                           secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+    static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                .WillByDefault(Return(Base::DISPLAY_ID::get()));
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .setGpuVirtualDisplayIdGenerator(
+                                             test->mFlinger.gpuVirtualDisplayIdGenerator())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
+    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+    }
+
+    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+        Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+        EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+    }
+};
+
+// A virtual display supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
+
+template <int width, int height, Secure secure>
+struct HwcVirtualDisplayVariant
+      : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+                       secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+        HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+                          DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
+                                         Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
+                                         GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+    using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+                                Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+    using Self = HwcVirtualDisplayVariant<width, height, secure>;
+
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        // In order to prevent compostition engine calling into HWComposer, we
+        // 1. turn off the use of HWC virtual displays,
+        // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
+        // 3. override the ID by calling setDisplayIdForTesting()
+
+        ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                .WillByDefault(Return(GpuVirtualDisplayId(0)));
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setUseHwcVirtualDisplays(false)
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .setGpuVirtualDisplayIdGenerator(
+                                             test->mFlinger.gpuVirtualDisplayIdGenerator())
+                                     .build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs);
+        compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+        // Insert display data so that the HWC thinks it created the virtual display.
+        if (const auto displayId = Base::DISPLAY_ID::get();
+            HalVirtualDisplayId::tryCast(displayId)) {
+            test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+        }
+
+        return compositionDisplay;
+    }
+
+    static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+        Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+        EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+    }
+
+    static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+                .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+    }
+};
+
+// For this variant, the display is not a HWC display, so no HDR support should
+// be configured.
+struct NonHwcDisplayHdrSupportVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
+    static constexpr bool HDR10_SUPPORTED = false;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
+    }
+};
+
+// For this variant, the composer should respond with am empty list of HDR
+// modes, so no HDR support should be configured.
+template <typename Display>
+struct HdrNotSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
+    static constexpr bool HDR10_SUPPORTED = false;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
+    }
+};
+
+struct NonHwcPerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = 0;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
+    }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = 0;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
+    }
+};
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, but the display should respond with an empty list of supported color
+// modes. Wide-color support for the display should not be configured.
+template <typename Display>
+struct WideColorNotSupportedVariant {
+    static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+    static void injectConfigChange(DisplayTransactionTest* test) {
+        test->mFlinger.mutableUseColorManagement() = true;
+        test->mFlinger.mutableHasWideColorDisplay() = true;
+    }
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+    }
+};
+
+// For this variant, SurfaceFlinger should not configure itself with wide
+// display support, so the display should not be configured for wide-color
+// support.
+struct WideColorSupportNotConfiguredVariant {
+    static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+    static void injectConfigChange(DisplayTransactionTest* test) {
+        test->mFlinger.mutableHasWideColorDisplay() = false;
+        test->mFlinger.mutableUseColorManagement() = false;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+    }
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
+        EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
+        EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * Typical display configurations to test
+ */
+
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+          typename PerFrameMetadataSupportPolicy>
+struct Case {
+    using Display = DisplayPolicy;
+    using WideColorSupport = WideColorSupportPolicy;
+    using HdrSupport = HdrSupportPolicy;
+    using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
+};
+
+using SimplePrimaryDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             HdrNotSupportedVariant<PrimaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using SimpleExternalDisplayCase =
+        Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
+             HdrNotSupportedVariant<ExternalDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
+using SimpleTertiaryDisplayCase =
+        Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
+             HdrNotSupportedVariant<TertiaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
+
+using NonHwcVirtualDisplayCase =
+        Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
+             WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+             NonHwcPerFrameMetadataSupportVariant>;
+using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
+using HwcVirtualDisplayCase =
+        Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
+             HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
+
+} // 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/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
deleted file mode 100644
index 9dc4193..0000000
--- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <log/log.h>
-
-#include "AsyncCallRecorder.h"
-#include "Scheduler/EventControlThread.h"
-
-namespace android {
-namespace {
-
-using namespace std::chrono_literals;
-using testing::_;
-
-class EventControlThreadTest : public testing::Test {
-protected:
-    EventControlThreadTest();
-    ~EventControlThreadTest() override;
-
-    void createThread();
-
-    void expectVSyncEnableCallbackCalled(bool enable);
-
-    AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
-
-    std::unique_ptr<EventControlThread> mThread;
-};
-
-EventControlThreadTest::EventControlThreadTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-EventControlThreadTest::~EventControlThreadTest() {
-    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());
-}
-
-void EventControlThreadTest::createThread() {
-    mThread = std::make_unique<android::impl::EventControlThread>(
-            mVSyncSetEnabledCallRecorder.getInvocable());
-}
-
-void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) {
-    auto args = mVSyncSetEnabledCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(std::get<0>(args.value()), expectedEnabled);
-}
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) {
-    createThread();
-
-    // On thread start, there should be an automatic explicit call to disable
-    // vsyncs
-    expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) {
-    createThread();
-    expectVSyncEnableCallbackCalled(false);
-
-    mThread->setVsyncEnabled(false);
-
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) {
-    createThread();
-    expectVSyncEnableCallbackCalled(false);
-
-    mThread->setVsyncEnabled(true);
-
-    expectVSyncEnableCallbackCalled(true);
-
-    mThread->setVsyncEnabled(false);
-
-    expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) {
-    createThread();
-    expectVSyncEnableCallbackCalled(false);
-
-    mThread->setVsyncEnabled(true);
-
-    expectVSyncEnableCallbackCalled(true);
-
-    mThread->setVsyncEnabled(true);
-
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index b90b566..3aafd45 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -36,9 +36,9 @@
 
 namespace {
 
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL;
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
 
 class MockVSyncSource : public VSyncSource {
 public:
@@ -46,7 +46,9 @@
 
     MOCK_METHOD1(setVSyncEnabled, void(bool));
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
-    MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+    MOCK_METHOD2(setDuration,
+                 void(std::chrono::nanoseconds workDuration,
+                      std::chrono::nanoseconds readyDuration));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
@@ -57,9 +59,11 @@
 protected:
     class MockEventThreadConnection : public EventThreadConnection {
     public:
-        MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
+        MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
+                                  ResyncCallback&& resyncCallback,
                                   ISurfaceComposer::ConfigChanged configChanged)
-              : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
+              : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
+                                      configChanged) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
     };
 
@@ -71,10 +75,12 @@
 
     void createThread(std::unique_ptr<VSyncSource>);
     sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
-                                                   ISurfaceComposer::ConfigChanged configChanged);
+                                                   ISurfaceComposer::ConfigChanged configChanged,
+                                                   uid_t ownerUid = mConnectionUid);
 
     void expectVSyncSetEnabledCallReceived(bool expectedState);
-    void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+    void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
+                                            std::chrono::nanoseconds expectedReadyDuration);
     VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
     void expectInterceptCallReceived(nsecs_t expectedTimestamp);
     void expectVsyncEventReceivedByConnection(const char* name,
@@ -86,18 +92,26 @@
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                       int32_t expectedConfigId,
                                                       nsecs_t expectedVsyncPeriod);
+    void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
 
     AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
     AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
-    AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+    AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
+            mVSyncSetDurationCallRecorder;
     AsyncCallRecorder<void (*)()> mResyncCallRecorder;
     AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+    AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
+    ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
 
     MockVSyncSource* mVSyncSource;
     VSyncSource::Callback* mCallback = nullptr;
     std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
+    sp<MockEventThreadConnection> mThrottledConnection;
+
+    static constexpr uid_t mConnectionUid = 443;
+    static constexpr uid_t mThrottledConnectionUid = 177;
 };
 
 EventThreadTest::EventThreadTest() {
@@ -114,12 +128,15 @@
     EXPECT_CALL(*mVSyncSource, setCallback(_))
             .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
 
-    EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
-            .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+    EXPECT_CALL(*mVSyncSource, setDuration(_, _))
+            .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable()));
 
     createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
                                    ISurfaceComposer::eConfigChangedDispatch);
+    mThrottledConnection =
+            createConnection(mThrottledConnectionEventCallRecorder,
+                             ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
 
     // A display must be connected for VSYNC events to be delivered.
     mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -136,8 +153,15 @@
 }
 
 void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+    const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+        mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
+        return (uid == mThrottledConnectionUid);
+    };
+
     mThread = std::make_unique<impl::EventThread>(std::move(source),
-                                                  mInterceptVSyncCallRecorder.getInvocable());
+                                                  /*tokenManager=*/nullptr,
+                                                  mInterceptVSyncCallRecorder.getInvocable(),
+                                                  throttleVsync);
 
     // EventThread should register itself as VSyncSource callback.
     mCallback = expectVSyncSetCallbackCallReceived();
@@ -145,10 +169,11 @@
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
-        ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
+        ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
+        uid_t ownerUid) {
     sp<MockEventThreadConnection> connection =
-            new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
-                                          configChanged);
+            new MockEventThreadConnection(mThread.get(), ownerUid,
+                                          mResyncCallRecorder.getInvocable(), configChanged);
     EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
     return connection;
 }
@@ -159,10 +184,12 @@
     EXPECT_EQ(expectedState, std::get<0>(args.value()));
 }
 
-void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) {
-    auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall();
+void EventThreadTest::expectVSyncSetDurationCallReceived(
+        std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) {
+    auto args = mVSyncSetDurationCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
+    EXPECT_EQ(expectedDuration, std::get<0>(args.value()));
+    EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value()));
 }
 
 VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
@@ -176,6 +203,13 @@
     EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
 }
 
+void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
+    auto args = mThrottleVsyncCallRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value());
+    EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+    EXPECT_EQ(uid, std::get<1>(args.value()));
+}
+
 void EventThreadTest::expectVsyncEventReceivedByConnection(
         const char* name, ConnectionEventRecorder& connectionEventRecorder,
         nsecs_t expectedTimestamp, unsigned expectedCount) {
@@ -229,7 +263,7 @@
 TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
     EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
@@ -258,15 +292,17 @@
 
     // Use the received callback to signal a first vsync event.
     // The interceptor should receive the event, as well as the connection.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
+    expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the the connection should
+    // The interceptor should receive the event, but the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
+    EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should also detect that at this point that it does not need
@@ -299,7 +335,7 @@
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the second connection. The first connection should not
     // get the event.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 0);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -314,18 +350,21 @@
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the connection.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
+    expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
+    expectThrottleVsyncReceived(123, mConnectionUid);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    mCallback->onVSyncEvent(789, 777);
+    mCallback->onVSyncEvent(789, 777, 111);
     expectInterceptCallReceived(789);
+    expectThrottleVsyncReceived(777, mConnectionUid);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
 
@@ -336,22 +375,25 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The second event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
+    EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The third event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(789, 777);
+    mCallback->onVSyncEvent(789, 777, 744);
     expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The fourth event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(101112, 7847);
+    mCallback->onVSyncEvent(101112, 7847, 86);
     expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
@@ -366,7 +408,7 @@
     mConnection = nullptr;
 
     // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -386,13 +428,13 @@
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an error.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor and not by the
     // connection.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
@@ -401,31 +443,31 @@
 }
 
 TEST_F(EventThreadTest, tracksEventConnections) {
-    EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
     ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
     sp<MockEventThreadConnection> errorConnection =
             createConnection(errorConnectionEventRecorder,
                              ISurfaceComposer::eConfigChangedSuppress);
     mThread->setVsyncRate(1, errorConnection);
-    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
     ConnectionEventRecorder secondConnectionEventRecorder{0};
     sp<MockEventThreadConnection> secondConnection =
             createConnection(secondConnectionEventRecorder,
                              ISurfaceComposer::eConfigChangedSuppress);
     mThread->setVsyncRate(1, secondConnection);
-    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+    EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
 
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an error.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
     expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
                                          1u);
-    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
 }
 
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
@@ -440,13 +482,13 @@
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an non-fatal error.
-    mCallback->onVSyncEvent(123, 456);
+    mCallback->onVSyncEvent(123, 456, 789);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor, and by the connection,
     // which still then returns an non-fatal error.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
@@ -455,8 +497,8 @@
 }
 
 TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
-    mThread->setPhaseOffset(321);
-    expectVSyncSetPhaseOffsetCallReceived(321);
+    mThread->setDuration(321ns, 456ns);
+    expectVSyncSetDurationCallReceived(321ns, 456ns);
 }
 
 TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
@@ -507,5 +549,35 @@
     ASSERT_FALSE(args.has_value());
 }
 
+TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+    // Signal that we want the next vsync event to be posted to the throttled connection
+    mThread->requestNextVsync(mThrottledConnection);
+
+    // EventThread should immediately request a resync.
+    EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+    // EventThread should enable vsync callbacks.
+    expectVSyncSetEnabledCallReceived(true);
+
+    // Use the received callback to signal a first vsync event.
+    // The interceptor should receive the event, but not the connection.
+    mCallback->onVSyncEvent(123, 456, 789);
+    expectInterceptCallReceived(123);
+    expectThrottleVsyncReceived(456, mThrottledConnectionUid);
+    mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+
+    // Use the received callback to signal a second vsync event.
+    // The interceptor should receive the event, but the connection should
+    // not as it was only interested in the first.
+    mCallback->onVSyncEvent(456, 123, 0);
+    expectInterceptCallReceived(456);
+    expectThrottleVsyncReceived(123, mThrottledConnectionUid);
+    EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+    // EventThread should not change the vsync state as it didn't send the event
+    // yet
+    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
deleted file mode 100644
index b50ddf5..0000000
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/PhaseOffsets.h"
-
-namespace android::scheduler {
-
-struct FakePhaseOffsets : PhaseConfiguration {
-    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
-
-    Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
-
-    Offsets getCurrentOffsets() const override {
-        return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
-    }
-
-    void setRefreshRateFps(float) override {}
-    void dump(std::string&) const override {}
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
new file mode 100644
index 0000000..4cd1e0a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+namespace android::scheduler {
+
+struct FakePhaseOffsets : VsyncConfiguration {
+    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+    static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+    VsyncConfigSet getConfigsForRefreshRate(float) const override { return getCurrentConfigs(); }
+
+    VsyncConfigSet getCurrentConfigs() const override {
+        return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS}};
+    }
+
+    void setRefreshRateFps(float) override {}
+    void dump(std::string&) const override {}
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
new file mode 100644
index 0000000..03c6f70
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gmock/gmock-spec-builders.h"
+#include "mock/MockTimeStats.h"
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <FrameTimeline/FrameTimeline.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <cinttypes>
+
+using namespace std::chrono_literals;
+using testing::Contains;
+
+MATCHER_P(HasBit, bit, "") {
+    return (arg & bit) != 0;
+}
+
+namespace android::frametimeline {
+
+class FrameTimelineTest : public testing::Test {
+public:
+    FrameTimelineTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~FrameTimelineTest() {
+        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());
+    }
+
+    void SetUp() override {
+        mTimeStats = std::make_shared<mock::TimeStats>();
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
+        mTokenManager = &mFrameTimeline->mTokenManager;
+        maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
+        maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+    }
+
+    void flushTokens(nsecs_t flushTime) {
+        std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
+        mTokenManager->flushTokens(flushTime);
+    }
+
+    SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
+        std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+        return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+    }
+
+    std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
+        std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+        return mFrameTimeline->mDisplayFrames[idx];
+    }
+
+    static bool compareTimelineItems(const TimelineItem& a, const TimelineItem& b) {
+        return a.startTime == b.startTime && a.endTime == b.endTime &&
+                a.presentTime == b.presentTime;
+    }
+
+    const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+        return mTokenManager->mPredictions;
+    }
+
+    uint32_t getNumberOfDisplayFrames() {
+        std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+        return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
+    }
+
+    std::shared_ptr<mock::TimeStats> mTimeStats;
+    std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
+    impl::TokenManager* mTokenManager;
+    FenceToFenceTimeMap fenceFactory;
+    uint32_t* maxDisplayFrames;
+    nsecs_t maxTokenRetentionTime;
+};
+
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+static constexpr const uid_t sUidOne = 0;
+static constexpr pid_t sPidOne = 10;
+static constexpr pid_t sPidTwo = 20;
+
+TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
+    int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+    EXPECT_EQ(getPredictions().size(), 1);
+    flushTokens(systemTime() + maxTokenRetentionTime);
+    int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
+
+    // token1 should have expired
+    EXPECT_EQ(getPredictions().size(), 1);
+    EXPECT_EQ(predictions.has_value(), false);
+
+    predictions = mTokenManager->getPredictionsForToken(token2);
+    EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                                    sLayerNameOne, std::nullopt);
+    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
+                                                                    sLayerNameOne, std::nullopt);
+    EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
+    EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, std::nullopt);
+    EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
+    int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+    flushTokens(systemTime() + maxTokenRetentionTime);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, token1);
+
+    EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
+    int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, token1);
+
+    EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
+    EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+    int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                                    sLayerNameOne, token1);
+
+    // Set up the display frame
+    mFrameTimeline->setSfWakeUp(token1, 20);
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfPresent(25, presentFence1);
+    presentFence1->signalForTest(30);
+
+    // Trigger a flush by calling setSfPresent for the next frame
+    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfPresent(55, presentFence2);
+
+    auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
+    EXPECT_EQ(droppedSurfaceFrame.getPresentState(), SurfaceFrame::PresentState::Dropped);
+    EXPECT_EQ(droppedSurfaceFrame.getActuals().presentTime, 0);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken1);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
+                                                       sLayerNameTwo, surfaceFrameToken1);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22);
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame2),
+                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+    presentFence1->signalForTest(42);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+    EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
+    EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame3 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken2);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52);
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+    EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
+    EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
+}
+
+TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
+    // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
+    int frameTimeFactor = 0;
+    for (size_t i = 0; i < *maxDisplayFrames; i++) {
+        auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+        int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+                {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+        int64_t sfToken = mTokenManager->generateTokenForPredictions(
+                {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                           sLayerNameOne, surfaceFrameToken);
+        mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+        presentFence->signalForTest(32 + frameTimeFactor);
+        frameTimeFactor += 30;
+    }
+    auto displayFrame0 = getDisplayFrame(0);
+
+    // The 0th Display Frame should have actuals 22, 27, 32
+    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
+              true);
+
+    // Add one more display frame
+    auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+            {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+    int64_t sfToken = mTokenManager->generateTokenForPredictions(
+            {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken);
+    mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+    presentFence->signalForTest(32 + frameTimeFactor);
+    displayFrame0 = getDisplayFrame(0);
+
+    // The window should have slided by 1 now and the previous 0th display frame
+    // should have been removed from the deque
+    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
+              true);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", std::nullopt);
+    surfaceFrame->setActualQueueTime(123);
+    surfaceFrame->setAcquireFenceTime(456);
+    EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", std::nullopt);
+    surfaceFrame->setActualQueueTime(456);
+    surfaceFrame->setAcquireFenceTime(123);
+    EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+    auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    presentFence->signalForTest(2);
+
+    // Size shouldn't exceed maxDisplayFrames - 64
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                           sLayerNameOne, std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+    // Increase the size to 256
+    mFrameTimeline->setMaxDisplayFrames(256);
+    EXPECT_EQ(*maxDisplayFrames, 256);
+
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                           sLayerNameOne, std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+    // Shrink the size to 128
+    mFrameTimeline->setMaxDisplayFrames(128);
+    EXPECT_EQ(*maxDisplayFrames, 128);
+
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                           sLayerNameOne, std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne,
+                                     HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken1);
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+                                 presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken1);
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+                                 presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne,
+                                     HasBit(TimeStats::JankType::AppDeadlineMissed)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+                                                       sLayerNameOne, surfaceFrameToken1);
+    surfaceFrame1->setAcquireFenceTime(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+                                 presentFence1);
+}
+
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index 68cb52f..a119e27 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -77,6 +77,22 @@
         return tracingSession;
     }
 
+    std::vector<perfetto::protos::TracePacket> readGraphicsFramePacketsBlocking(
+            perfetto::TracingSession* tracingSession) {
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        perfetto::protos::Trace trace;
+        EXPECT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+        std::vector<perfetto::protos::TracePacket> packets;
+        for (const auto& packet : trace.packet()) {
+            if (!packet.has_graphics_frame_event()) {
+                continue;
+            }
+            packets.emplace_back(packet);
+        }
+        return packets;
+    }
+
     std::unique_ptr<FrameTracer> mFrameTracer;
     FenceToFenceTimeMap fenceFactory;
 };
@@ -142,40 +158,29 @@
         auto tracingSession = getTracingSessionForTest();
 
         tracingSession->StartBlocking();
-        // Clean up irrelevant traces.
-        tracingSession->ReadTraceBlocking();
-
         mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
         // Create second trace packet to finalize the previous one.
         mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
-        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-        EXPECT_EQ(raw_trace.size(), 0);
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 0);
     }
 
     {
         auto tracingSession = getTracingSessionForTest();
 
         tracingSession->StartBlocking();
-        // Clean up irrelevant traces.
-        tracingSession->ReadTraceBlocking();
-
         mFrameTracer->traceNewLayer(layerId, layerName);
         mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
         // Create second trace packet to finalize the previous one.
         mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
-        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-        ASSERT_GT(raw_trace.size(), 0);
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 1);
 
-        perfetto::protos::Trace trace;
-        ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
-        ASSERT_FALSE(trace.packet().empty());
-        EXPECT_EQ(trace.packet().size(), 1);
-
-        const auto& packet = trace.packet().Get(0);
+        const auto& packet = packets[0];
         ASSERT_TRUE(packet.has_timestamp());
         EXPECT_EQ(packet.timestamp(), timestamp);
         ASSERT_TRUE(packet.has_graphics_frame_event());
@@ -205,24 +210,21 @@
         fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING);
         auto tracingSession = getTracingSessionForTest();
         tracingSession->StartBlocking();
-        // Clean up irrelevant traces.
-        tracingSession->ReadTraceBlocking();
         // Trace.
         mFrameTracer->traceNewLayer(layerId, layerName);
         mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
         // Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
         mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
-        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-        EXPECT_EQ(raw_trace.size(), 0);
+
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 0);
     }
 
     {
         auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         auto tracingSession = getTracingSessionForTest();
         tracingSession->StartBlocking();
-        // Clean up irrelevant traces.
-        tracingSession->ReadTraceBlocking();
         mFrameTracer->traceNewLayer(layerId, layerName);
         mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
         const nsecs_t timestamp = systemTime();
@@ -231,15 +233,10 @@
         mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
-        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-        ASSERT_GT(raw_trace.size(), 0);
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
 
-        perfetto::protos::Trace trace;
-        ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
-        ASSERT_FALSE(trace.packet().empty());
-        EXPECT_EQ(trace.packet().size(), 2); // Two packets because of the extra trace made above.
-
-        const auto& packet = trace.packet().Get(1);
+        const auto& packet = packets[1];
         ASSERT_TRUE(packet.has_timestamp());
         EXPECT_EQ(packet.timestamp(), timestamp);
         ASSERT_TRUE(packet.has_graphics_frame_event());
@@ -266,8 +263,6 @@
     auto tracingSession = getTracingSessionForTest();
 
     tracingSession->StartBlocking();
-    // Clean up irrelevant traces.
-    tracingSession->ReadTraceBlocking();
     mFrameTracer->traceNewLayer(layerId, layerName);
 
     // traceFence called after fence signalled.
@@ -288,22 +283,17 @@
     mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
-    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-    ASSERT_GT(raw_trace.size(), 0);
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 2);
 
-    perfetto::protos::Trace trace;
-    ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
-    ASSERT_FALSE(trace.packet().empty());
-    EXPECT_EQ(trace.packet().size(), 2);
-
-    const auto& packet1 = trace.packet().Get(0);
+    const auto& packet1 = packets[0];
     ASSERT_TRUE(packet1.has_timestamp());
     EXPECT_EQ(packet1.timestamp(), signalTime1);
     ASSERT_TRUE(packet1.has_graphics_frame_event());
     ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
     ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
 
-    const auto& packet2 = trace.packet().Get(1);
+    const auto& packet2 = packets[1];
     ASSERT_TRUE(packet2.has_timestamp());
     EXPECT_EQ(packet2.timestamp(), signalTime2);
     ASSERT_TRUE(packet2.has_graphics_frame_event());
@@ -323,8 +313,6 @@
     auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
-    // Clean up irrelevant traces.
-    tracingSession->ReadTraceBlocking();
     mFrameTracer->traceNewLayer(layerId, layerName);
     mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
@@ -332,8 +320,8 @@
     mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
-    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-    EXPECT_EQ(raw_trace.size(), 0);
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 0);
 }
 
 TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
@@ -347,8 +335,6 @@
     auto tracingSession = getTracingSessionForTest();
 
     tracingSession->StartBlocking();
-    // Clean up irrelevant traces.
-    tracingSession->ReadTraceBlocking();
     mFrameTracer->traceNewLayer(layerId, layerName);
 
     // traceFence called after fence signalled.
@@ -369,15 +355,10 @@
     mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
-    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-    ASSERT_GT(raw_trace.size(), 0);
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 2);
 
-    perfetto::protos::Trace trace;
-    ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
-    ASSERT_FALSE(trace.packet().empty());
-    EXPECT_EQ(trace.packet().size(), 2);
-
-    const auto& packet1 = trace.packet().Get(0);
+    const auto& packet1 = packets[0];
     ASSERT_TRUE(packet1.has_timestamp());
     EXPECT_EQ(packet1.timestamp(), startTime1);
     ASSERT_TRUE(packet1.has_graphics_frame_event());
@@ -386,7 +367,7 @@
     const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
     EXPECT_EQ(buffer_event1.duration_ns(), duration);
 
-    const auto& packet2 = trace.packet().Get(1);
+    const auto& packet2 = packets[1];
     ASSERT_TRUE(packet2.has_timestamp());
     EXPECT_EQ(packet2.timestamp(), startTime2);
     ASSERT_TRUE(packet2.has_graphics_frame_event());
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 91b304c..fa12315 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -23,7 +23,13 @@
 
 #include <vector>
 
+// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be
+// virtual in case StrictMock<T> is used as a polymorphic base class. That is not the case here.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
 #include <gmock/gmock.h>
+#pragma clang diagnostic pop
+
 #include <gui/LayerMetadata.h>
 #include <log/log.h>
 
@@ -45,27 +51,20 @@
 using ::testing::SetArgPointee;
 using ::testing::StrictMock;
 
-struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
-    ~MockHWC2ComposerCallback() = default;
-
-    MOCK_METHOD3(onHotplugReceived,
-                 void(int32_t sequenceId, hal::HWDisplayId display, hal::Connection connection));
-    MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId display));
+struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
+    MOCK_METHOD3(onHotplugReceived, void(int32_t sequenceId, hal::HWDisplayId, hal::Connection));
+    MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId));
     MOCK_METHOD4(onVsyncReceived,
-                 void(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
-                      std::optional<hal::VsyncPeriodNanos> vsyncPeriod));
+                 void(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+                      std::optional<hal::VsyncPeriodNanos>));
     MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
-                 void(int32_t sequenceId, hal::HWDisplayId display,
-                      const hal::VsyncPeriodChangeTimeline& updatedTimeline));
-    MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId display));
+                 void(int32_t sequenceId, hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
+    MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId));
 };
 
-struct HWComposerTest : public testing::Test {
+struct HWComposerSetConfigurationTest : testing::Test {
     Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
-};
-
-struct HWComposerSetConfigurationTest : public HWComposerTest {
-    StrictMock<MockHWC2ComposerCallback> mCallback;
+    MockHWC2ComposerCallback mCallback;
 };
 
 TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cae317b..0fbe8ec 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -30,6 +30,7 @@
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
 
 using testing::_;
 using testing::Return;
@@ -49,6 +50,8 @@
 
     LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
 
+    void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
     impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
     const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
 
@@ -73,7 +76,13 @@
                                          .setConfigGroup(0)
                                          .build()},
                                 HwcConfigIndexType(0)};
-    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
+
+    mock::NoOpSchedulerCallback mSchedulerCallback;
+    static constexpr bool kUseContentDetectionV2 = false;
+
+    TestableScheduler* const mScheduler =
+            new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+
     TestableSurfaceFlinger mFlinger;
 
     const nsecs_t mTime = systemTime();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index f376b4a..cb376cd 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -27,6 +27,7 @@
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
 
 using testing::_;
 using testing::Return;
@@ -38,7 +39,9 @@
     static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
     static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
     static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
-    static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME;
+    static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
+    static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+            LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
 
     static constexpr float LO_FPS = 30.f;
     static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -48,6 +51,8 @@
 
     LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
 
+    void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
     impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
     const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
 
@@ -84,6 +89,23 @@
         return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
     }
 
+    void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+                               float desiredRefreshRate, int numFrames) {
+        const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+        impl::LayerHistoryV2::Summary summary;
+        for (int i = 0; i < numFrames; i++) {
+            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            time += framePeriod;
+
+            summary = history().summarize(time);
+        }
+
+        ASSERT_EQ(1, summary.size());
+        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+        ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+                << "Frame rate is " << frameRate;
+    }
+
     Hwc2::mock::Display mDisplay;
     RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
                                          .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
@@ -94,9 +116,14 @@
                                          .setConfigGroup(0)
                                          .build()},
                                 HwcConfigIndexType(0)};
-    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
-    TestableSurfaceFlinger mFlinger;
 
+    mock::NoOpSchedulerCallback mSchedulerCallback;
+    static constexpr bool kUseContentDetectionV2 = true;
+
+    TestableScheduler* const mScheduler =
+            new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+
+    TestableSurfaceFlinger mFlinger;
 };
 
 namespace {
@@ -346,14 +373,17 @@
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
+    impl::LayerHistoryV2::Summary summary;
+
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
@@ -361,28 +391,30 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
     // layer1 is still active but infrequent.
     history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
     EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+    for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -396,33 +428,36 @@
 
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
     history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer1 expires.
     layer1.clear();
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, layerCount());
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
@@ -432,37 +467,41 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer2 expires.
     layer2.clear();
-    EXPECT_TRUE(history().summarize(time).empty());
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
     // layer3 becomes active and has high refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer3 expires.
     layer3.clear();
-    EXPECT_TRUE(history().summarize(time).empty());
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
     EXPECT_EQ(0, layerCount());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
@@ -600,6 +639,46 @@
     EXPECT_EQ(1, animatingLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+        recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    }
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
 class LayerHistoryTestV2Parameterized
       : public LayerHistoryTestV2,
         public testing::WithParamInterface<std::chrono::nanoseconds> {};
@@ -642,7 +721,7 @@
             infrequentLayerUpdates++;
         }
 
-        if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) {
+        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
             ASSERT_NE(0, history().summarize(time).size());
             ASSERT_GE(2, history().summarize(time).size());
 
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
new file mode 100644
index 0000000..53dfe3f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrameTimeline.h"
+#include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using namespace std::chrono_literals;
+using namespace testing;
+
+using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+
+class TestableMessageQueue : public impl::MessageQueue {
+public:
+    class MockHandler : public MessageQueue::Handler {
+    public:
+        explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
+        ~MockHandler() override = default;
+        MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+    };
+
+    TestableMessageQueue() = default;
+    ~TestableMessageQueue() override = default;
+
+    void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+
+    void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+        vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
+    }
+};
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+    MockVSyncDispatch() = default;
+    ~MockVSyncDispatch() override = default;
+
+    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&));
+};
+
+class MockTokenManager : public frametimeline::TokenManager {
+public:
+    MockTokenManager() = default;
+    ~MockTokenManager() override = default;
+
+    MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+};
+
+class MessageQueueTest : public testing::Test {
+public:
+    MessageQueueTest() = default;
+    ~MessageQueueTest() override = default;
+
+    void SetUp() override {
+        EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
+
+        EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+        EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
+        EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+    }
+
+    sp<TestableMessageQueue::MockHandler> mHandler =
+            new TestableMessageQueue::MockHandler(mEventQueue);
+    MockVSyncDispatch mVSyncDispatch;
+    MockTokenManager mTokenManager;
+    TestableMessageQueue mEventQueue;
+
+    const CallbackToken mCallbackToken{5};
+    constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
+    constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(MessageQueueTest, invalidate) {
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwice) {
+    InSequence s;
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+    InSequence s;
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+    const auto startTime = 100;
+    const auto endTime = startTime + mDuration.count();
+    const auto presentTime = 500;
+    const auto vsyncId = 42;
+    EXPECT_CALL(mTokenManager,
+                generateTokenForPredictions(
+                        frametimeline::TimelineItem(startTime, endTime, presentTime)))
+            .WillOnce(Return(vsyncId));
+    EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+
+    const auto timingAfterCallback =
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                     .readyDuration = 0,
+                                                     .earliestVsync = presentTime};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+
+    const auto timing =
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+                                                     .readyDuration = 0,
+                                                     .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
deleted file mode 100644
index 0b74682..0000000
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// 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 <log/log.h>
-#include <thread>
-
-#include "Scheduler/PhaseOffsets.h"
-
-using namespace testing;
-
-namespace android {
-namespace scheduler {
-
-class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
-public:
-    TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
-                                    nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
-                                    nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
-          : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
-                                 sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
-                                 appEarlyGlDuration) {}
-};
-
-class PhaseDurationTest : public testing::Test {
-protected:
-    PhaseDurationTest()
-          : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
-                            21'000'000) {}
-
-    ~PhaseDurationTest() = default;
-
-    TestablePhaseOffsetsAsDurations mPhaseDurations;
-};
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
-    mPhaseDurations.setRefreshRateFps(60.0f);
-    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
-    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
-
-    EXPECT_EQ(currentOffsets, offsets);
-    EXPECT_EQ(offsets.late.sf, 6'166'667);
-
-    EXPECT_EQ(offsets.late.app, 2'333'334);
-
-    EXPECT_EQ(offsets.early.sf, 666'667);
-
-    EXPECT_EQ(offsets.early.app, 833'334);
-
-    EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
-
-    EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
-    mPhaseDurations.setRefreshRateFps(90.0f);
-    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
-    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
-
-    EXPECT_EQ(currentOffsets, offsets);
-    EXPECT_EQ(offsets.late.sf, 611'111);
-
-    EXPECT_EQ(offsets.late.app, 2'333'333);
-
-    EXPECT_EQ(offsets.early.sf, -4'888'889);
-
-    EXPECT_EQ(offsets.early.app, 833'333);
-
-    EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
-
-    EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
-    TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
-
-    auto validateOffsets = [](auto& offsets) {
-        EXPECT_EQ(offsets.late.sf, 1'000'000);
-
-        EXPECT_EQ(offsets.late.app, 1'000'000);
-
-        EXPECT_EQ(offsets.early.sf, 1'000'000);
-
-        EXPECT_EQ(offsets.early.app, 1'000'000);
-
-        EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
-        EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
-    };
-
-    phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
-    auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
-    auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
-    EXPECT_EQ(currentOffsets, offsets);
-    validateOffsets(offsets);
-
-    phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
-    currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
-    offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
-    EXPECT_EQ(currentOffsets, offsets);
-    validateOffsets(offsets);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
-    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
-
-    EXPECT_EQ(offsets.late.sf, 57'527'208);
-
-    EXPECT_EQ(offsets.late.app, 37'027'208);
-
-    EXPECT_EQ(offsets.early.sf, 52'027'208);
-
-    EXPECT_EQ(offsets.early.app, 35'527'208);
-
-    EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
-
-    EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
-}
-
-} // namespace
-
-class TestablePhaseOffsets : public impl::PhaseOffsets {
-public:
-    TestablePhaseOffsets()
-          : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
-                               10'000'000) {}
-};
-
-class PhaseOffsetsTest : public testing::Test {
-protected:
-    PhaseOffsetsTest() = default;
-    ~PhaseOffsetsTest() = default;
-
-    TestablePhaseOffsets mPhaseOffsets;
-};
-
-namespace {
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
-    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
-
-    EXPECT_EQ(offsets.late.sf, 1'000'000);
-
-    EXPECT_EQ(offsets.late.app, 1'000'000);
-
-    EXPECT_EQ(offsets.early.sf, 1'000'000);
-
-    EXPECT_EQ(offsets.early.app, 1'000'000);
-
-    EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
-    EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
-}
-
-} // namespace
-} // namespace scheduler
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index c919e93..4762fd4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -43,6 +43,14 @@
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
+    float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) {
+        return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
+    }
+
+    std::vector<float> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
+        return refreshRateConfigs.mKnownFrameRates;
+    }
+
     // Test config IDs
     static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
     static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
@@ -284,7 +292,8 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
-        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, 1.0f}};
+        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
+                 /*focused*/ false}};
     };
 
     EXPECT_EQ(mExpected90Config,
@@ -335,7 +344,6 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_72);
@@ -344,17 +352,14 @@
     // range.
     auto layers = std::vector<LayerRequirement>{};
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
-                                                     false, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
-                                                     false, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -365,163 +370,134 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.name = "";
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -531,43 +507,35 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -582,28 +550,24 @@
     lr2.desiredRefreshRate = 60.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -620,8 +584,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -630,8 +593,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -640,8 +602,7 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -650,8 +611,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -660,8 +620,7 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -670,8 +629,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
@@ -680,8 +638,7 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -690,8 +647,7 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -700,12 +656,10 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -715,43 +669,35 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -762,71 +708,57 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr.desiredRefreshRate = 24.0f;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -839,56 +771,48 @@
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15.0f;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -900,10 +824,8 @@
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                       /*idle*/ false, &ignored);
-        printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
-        EXPECT_EQ(mExpected60Config, refreshRate);
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
     }
 }
 
@@ -931,7 +853,6 @@
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -946,24 +867,21 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -975,7 +893,6 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -987,15 +904,12 @@
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                       /*idle*/ false, &ignored);
-        printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
-        EXPECT_EQ(mExpected90Config, refreshRate);
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1012,8 +926,7 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1022,8 +935,7 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1031,8 +943,7 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -1041,8 +952,7 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -1050,12 +960,10 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
-                                                     /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1071,7 +979,7 @@
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1079,7 +987,7 @@
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1087,7 +995,7 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1095,7 +1003,7 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1105,20 +1013,20 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
-    bool touchConsidered;
+    RefreshRateConfigs::GlobalSignals consideredSignals;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    refreshRateConfigs->getBestRefreshRate({}, false, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(false, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
 
-    refreshRateConfigs->getBestRefreshRate({}, true, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(true, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                 LayerRequirement{.weight = 1.0f}};
@@ -1131,8 +1039,9 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(true, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60.0f;
@@ -1140,8 +1049,9 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(false, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1149,8 +1059,9 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(true, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60.0f;
@@ -1158,12 +1069,12 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
-    EXPECT_EQ(false, touchConsidered);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_60);
@@ -1199,7 +1110,7 @@
         lr.name = ss.str();
 
         const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored);
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
         EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
                 << "Expecting " << test.first << "fps => " << test.second << "Hz";
     }
@@ -1218,22 +1129,15 @@
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& lr = layers[0];
 
-    bool touchConsidered = false;
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr.desiredRefreshRate = 60.0f;
-    lr.name = "60Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
-                                                     &touchConsidered));
-    EXPECT_EQ(false, touchConsidered);
-
+    RefreshRateConfigs::GlobalSignals consideredSignals;
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz ExplicitDefault";
+    lr.focused = true;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
-                                                     &touchConsidered));
-    EXPECT_EQ(false, touchConsidered);
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+                                                     &consideredSignals));
+    EXPECT_EQ(false, consideredSignals.touch);
 }
 
 TEST_F(RefreshRateConfigsTest,
@@ -1249,24 +1153,16 @@
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& lr = layers[0];
 
-    bool touchConsidered = false;
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr.desiredRefreshRate = 90.0f;
-    lr.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
-                                                     &touchConsidered));
-
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 90.0f;
     lr.name = "90Hz ExplicitDefault";
+    lr.focused = true;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
-                                                     &touchConsidered));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
 }
 
 TEST_F(RefreshRateConfigsTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitLayers) {
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_90);
@@ -1275,11 +1171,11 @@
                       {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
               0);
 
-    bool touchConsidered = false;
+    RefreshRateConfigs::GlobalSignals consideredSignals;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate({}, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
-    EXPECT_EQ(false, touchConsidered);
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
+                                                     &consideredSignals));
+    EXPECT_EQ(false, consideredSignals.touch);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& lr = layers[0];
@@ -1287,37 +1183,57 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz ExplicitDefault";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Heuristic;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
+    lr.focused = false;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Max;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Max";
+    lr.focused = false;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
     lr.vote = LayerVoteType::Min;
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Min";
+    lr.focused = false;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
-                                                     &touchConsidered));
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitching) {
@@ -1331,10 +1247,8 @@
     layer.desiredRefreshRate = 90.0f;
     layer.name = "90Hz ExplicitDefault";
 
-    bool touchConsidered;
     ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs
-                      ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getConfigId());
 
     RefreshRateConfigs::Policy policy;
@@ -1342,8 +1256,7 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
     ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs
-                      ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getConfigId());
 }
 
@@ -1357,38 +1270,42 @@
 
     // Return the config ID from calling getBestRefreshRate() for a single layer with the
     // given voteType and fps.
-    auto getFrameRate = [&](LayerVoteType voteType, float fps,
-                            bool touchActive = false) -> HwcConfigIndexType {
+    auto getFrameRate = [&](LayerVoteType voteType, float fps, bool touchActive = false,
+                            bool focused = true) -> HwcConfigIndexType {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = fps;
-        bool touchConsidered;
-        return refreshRateConfigs
-                ->getBestRefreshRate(layers, touchActive, /*idle*/ false, &touchConsidered)
+        layers[0].focused = focused;
+        return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
                 .getConfigId();
     };
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
                       {HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
               0);
-    bool touchConsidered;
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs
-                      ->getBestRefreshRate({}, /*touchActive=*/false, /*idle*/ false,
-                                           &touchConsidered)
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
                       .getConfigId());
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
     EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90.f));
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
     EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+
+    // Layers not focused are not allowed to override primary config
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/false,
+                           /*focused=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/false,
+                           /*focused=*/false));
 
     // Touch boost should be restricted to the primary range.
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f, /*touch=*/true));
     // When we're higher than the primary range max due to a layer frame rate setting, touch boost
     // shouldn't drag us back down to the primary range max.
     EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90,
+    EXPECT_EQ(HWC_CONFIG_ID_60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/true));
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1410,13 +1327,19 @@
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     layers[0].name = "Test layer";
 
-    auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> HwcConfigIndexType {
+    const auto getIdleFrameRate = [&](LayerVoteType voteType,
+                                      bool touchActive) -> HwcConfigIndexType {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = 90.f;
-        bool touchConsidered;
-        return refreshRateConfigs
-                ->getBestRefreshRate(layers, touchActive, /*idle=*/true, &touchConsidered)
-                .getConfigId();
+        RefreshRateConfigs::GlobalSignals consideredSignals;
+        const auto configId =
+                refreshRateConfigs
+                        ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
+                                             &consideredSignals)
+                        .getConfigId();
+        // Refresh rate will be chosen by either touch state or idle state
+        EXPECT_EQ(!touchActive, consideredSignals.idle);
+        return configId;
     };
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1424,38 +1347,158 @@
               0);
 
     // Idle should be lower priority than touch boost.
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitDefault, true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true));
 
     // With no layers, idle should still be lower priority than touch boost.
-    bool touchConsidered;
     EXPECT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs
-                      ->getBestRefreshRate({}, /*touchActive=*/true, /*idle=*/true,
-                                           &touchConsidered)
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
                       .getConfigId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitDefault, false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false));
 
     // Idle should be applied rather than the current config when there are no layers.
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs
-                      ->getBestRefreshRate({}, /*touchActive=*/false, /*idle=*/true,
-                                           &touchConsidered)
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
                       .getConfigId());
 }
 
+TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+        const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps);
+        float expectedFrameRate;
+        if (fps < 26.91f) {
+            expectedFrameRate = 24.0f;
+        } else if (fps < 37.51f) {
+            expectedFrameRate = 30.0f;
+        } else if (fps < 52.51f) {
+            expectedFrameRate = 45.0f;
+        } else if (fps < 66.01f) {
+            expectedFrameRate = 60.0f;
+        } else if (fps < 81.01f) {
+            expectedFrameRate = 72.0f;
+        } else {
+            expectedFrameRate = 90.0f;
+        }
+        EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate)
+                << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    struct ExpectedRate {
+        float rate;
+        const RefreshRate& expected;
+    };
+
+    /* clang-format off */
+    std::vector<ExpectedRate> knownFrameRatesExpectations = {
+        {24.0f, mExpected60Config},
+        {30.0f, mExpected60Config},
+        {45.0f, mExpected90Config},
+        {60.0f, mExpected60Config},
+        {72.0f, mExpected90Config},
+        {90.0f, mExpected90Config},
+    };
+    /* clang-format on */
+
+    // Make sure the test tests all the known frame rate
+    const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
+    const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+                                  knownFrameRatesExpectations.begin(),
+                                  [](float a, const ExpectedRate& b) { return a == b.rate; });
+    EXPECT_TRUE(equal);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::Heuristic;
+    for (const auto& expectedRate : knownFrameRatesExpectations) {
+        layer.desiredRefreshRate = expectedRate.rate;
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(expectedRate.expected, refreshRate);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
+    EXPECT_TRUE(mExpected60Config < mExpected90Config);
+    EXPECT_FALSE(mExpected60Config < mExpected60Config);
+    EXPECT_FALSE(mExpected90Config < mExpected90Config);
+}
+
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    // SetPolicy(60, 90), current 90Hz => TurnOn.
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 90), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(90, 90), current 90Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
+    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
+}
+
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
+    const uid_t uid = 1234;
+    refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
+    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 43b8e01..8cd8372 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -31,9 +27,8 @@
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -65,7 +60,7 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    void setupComposer(int virtualDisplayCount);
+    void setupComposer(uint32_t virtualDisplayCount);
     sp<BufferQueueLayer> createBufferQueueLayer();
     sp<BufferStateLayer> createBufferStateLayer();
     sp<EffectLayer> createEffectLayer();
@@ -123,7 +118,11 @@
 }
 
 void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
-    layer->commitTransaction(layer->getCurrentState());
+    layer->pushPendingState();
+    auto c = layer->getCurrentState();
+    if (layer->applyPendingStates(&c)) {
+        layer->commitTransaction(c);
+    }
 }
 
 void RefreshRateSelectionTest::setupScheduler() {
@@ -132,26 +131,28 @@
 
     EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
     EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    auto primaryDispSync = std::make_unique<mock::DispSync>();
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*primaryDispSync, getPeriod())
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
             .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(primaryDispSync),
-                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
-                            std::move(sfEventThread));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
 }
 
-void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+void RefreshRateSelectionTest::setupComposer(uint32_t virtualDisplayCount) {
     mComposer = new Hwc2::mock::Composer();
     EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
@@ -283,6 +284,3 @@
 
 } // namespace
 } // 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/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..509858a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,39 +14,35 @@
  * 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 <log/log.h>
 
 #include <mutex>
 
-#include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
 
 using testing::_;
 using testing::Return;
 
 namespace android {
+namespace {
 
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999;
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
 
 class SchedulerTest : public testing::Test {
 protected:
     class MockEventThreadConnection : public android::EventThreadConnection {
     public:
         explicit MockEventThreadConnection(EventThread* eventThread)
-              : EventThreadConnection(eventThread, ResyncCallback(),
+              : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
                                       ISurfaceComposer::eConfigChangedSuppress) {}
         ~MockEventThreadConnection() = default;
 
@@ -56,32 +52,32 @@
     };
 
     SchedulerTest();
-    ~SchedulerTest() override;
 
-    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
-    std::unique_ptr<TestableScheduler> mScheduler;
+    Hwc2::mock::Display mDisplay;
+    const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+                                                          .setVsyncPeriod(16'666'667)
+                                                          .setConfigGroup(0)
+                                                          .build()},
+                                                 HwcConfigIndexType(0)};
+
+    mock::SchedulerCallback mSchedulerCallback;
+
+    // The scheduler should initially disable VSYNC.
+    struct ExpectDisableVsync {
+        ExpectDisableVsync(mock::SchedulerCallback& callback) {
+            EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
+        }
+    } mExpectDisableVsync{mSchedulerCallback};
+
+    static constexpr bool kUseContentDetectionV2 = false;
+    TestableScheduler mScheduler{mConfigs, mSchedulerCallback, kUseContentDetectionV2};
 
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
-    Hwc2::mock::Display mDisplay;
 };
 
 SchedulerTest::SchedulerTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
-            HWC2::Display::Config::Builder(mDisplay, 0)
-                    .setVsyncPeriod(int32_t(16666667))
-                    .setConfigGroup(0)
-                    .build()};
-    mRefreshRateConfigs = std::make_unique<
-            scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
-    mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
     auto eventThread = std::make_unique<mock::EventThread>();
     mEventThread = eventThread.get();
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -93,86 +89,111 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+    mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
     EXPECT_TRUE(mConnectionHandle);
 }
 
-SchedulerTest::~SchedulerTest() {
-    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());
-}
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+} // namespace
 
 TEST_F(SchedulerTest, invalidConnectionHandle) {
     Scheduler::ConnectionHandle handle;
 
-    sp<IDisplayEventConnection> connection;
-    ASSERT_NO_FATAL_FAILURE(
-            connection = mScheduler->createDisplayEventConnection(handle,
-                                                                  ISurfaceComposer::
-                                                                          eConfigChangedSuppress));
+    const sp<IDisplayEventConnection> connection =
+            mScheduler.createDisplayEventConnection(handle,
+                                                    ISurfaceComposer::eConfigChangedSuppress);
+
     EXPECT_FALSE(connection);
-    EXPECT_FALSE(mScheduler->getEventConnection(handle));
+    EXPECT_FALSE(mScheduler.getEventConnection(handle));
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+    mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+    mScheduler.onScreenAcquired(handle);
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+    mScheduler.onScreenReleased(handle);
 
     std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+    mScheduler.dump(handle, output);
     EXPECT_TRUE(output.empty());
 
-    EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+    EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
+    mScheduler.setDuration(handle, 10ns, 20ns);
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
-    sp<IDisplayEventConnection> connection;
-    ASSERT_NO_FATAL_FAILURE(
-            connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
-                                                                  ISurfaceComposer::
-                                                                          eConfigChangedSuppress));
+    const sp<IDisplayEventConnection> connection =
+            mScheduler.createDisplayEventConnection(mConnectionHandle,
+                                                    ISurfaceComposer::eConfigChangedSuppress);
+
     ASSERT_EQ(mEventThreadConnection, connection);
-    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
+    EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(
-            mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+    mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+    mScheduler.onScreenAcquired(mConnectionHandle);
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+    mScheduler.onScreenReleased(mConnectionHandle);
 
     std::string output("dump");
     EXPECT_CALL(*mEventThread, dump(output)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+    mScheduler.dump(mConnectionHandle, output);
     EXPECT_FALSE(output.empty());
 
-    EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+    EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
+    mScheduler.setDuration(mConnectionHandle, 10ns, 20ns);
 
     static constexpr size_t kEventConnections = 5;
-    ON_CALL(*mEventThread, getEventThreadConnectionCount())
-            .WillByDefault(Return(kEventConnections));
-    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
+    EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
+    EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
 }
 
-} // namespace
-} // namespace android
+TEST_F(SchedulerTest, noLayerHistory) {
+    // Layer history should not be created if there is a single config.
+    ASSERT_FALSE(mScheduler.hasLayerHistory());
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+    TestableSurfaceFlinger flinger;
+    mock::MockLayer layer(flinger.flinger());
+
+    // Content detection should be no-op.
+    mScheduler.registerLayer(&layer);
+    mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+
+    constexpr bool kPowerStateNormal = true;
+    mScheduler.setDisplayPowerState(kPowerStateNormal);
+
+    constexpr uint32_t kDisplayArea = 999'999;
+    mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+
+    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+    mScheduler.chooseRefreshRateForContent();
+}
+
+TEST_F(SchedulerTest, testDispatchCachedReportedConfig) {
+    // If the optional fields are cleared, the function should return before
+    // onConfigChange is called.
+    mScheduler.clearOptionalFieldsInFeatures();
+    EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedConfig());
+    EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) {
+    HwcConfigIndexType configId = HwcConfigIndexType(111);
+    nsecs_t vsyncPeriod = 111111;
+
+    // If the handle is incorrect, the function should return before
+    // onConfigChange is called.
+    Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+    EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayConfigChanged(invalidHandle,
+                                                                        PHYSICAL_DISPLAY_ID,
+                                                                        configId, vsyncPeriod));
+    EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 0d6c799..e25d501 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -32,10 +32,9 @@
 #pragma clang diagnostic pop // ignored "-Wconversion"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -158,7 +157,11 @@
 
 void SetFrameRateTest::commitTransaction() {
     for (auto layer : mLayers) {
-        layer.get()->commitTransaction(layer.get()->getCurrentState());
+        layer->pushPendingState();
+        auto c = layer->getCurrentState();
+        if (layer->applyPendingStates(&c)) {
+            layer->commitTransaction(c);
+        }
     }
 }
 
@@ -168,23 +171,25 @@
 
     EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
     EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    auto primaryDispSync = std::make_unique<mock::DispSync>();
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*primaryDispSync, getPeriod())
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
             .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(primaryDispSync),
-                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
-                            std::move(sfEventThread));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
 }
 
 void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
new file mode 100644
index 0000000..2362a31
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class CreateDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
+    const String8 name("virtual.test");
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // The call should notify the interceptor that a display was created.
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display should have been added to the current state
+    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+    const auto& display = getCurrentDisplayState(displayToken);
+    EXPECT_TRUE(display.isVirtual());
+    EXPECT_FALSE(display.isSecure);
+    EXPECT_EQ(name.string(), display.displayName);
+
+    // --------------------------------------------------------------------
+    // Cleanup conditions
+
+    // Destroying the display invalidates the display state.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
+    const String8 name("virtual.test");
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // The call should notify the interceptor that a display was created.
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+    int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
+    // Set the calling identity to graphics so captureDisplay with secure is allowed.
+    IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
+                                                   AID_GRAPHICS);
+    sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
+    IPCThreadState::self()->restoreCallingIdentity(oldId);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display should have been added to the current state
+    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+    const auto& display = getCurrentDisplayState(displayToken);
+    EXPECT_TRUE(display.isVirtual());
+    EXPECT_TRUE(display.isSecure);
+    EXPECT_EQ(name.string(), display.displayName);
+
+    // --------------------------------------------------------------------
+    // Cleanup conditions
+
+    // Destroying the display invalidates the display state.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
new file mode 100644
index 0000000..0614434
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class DestroyDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A virtual display exists
+    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+    existing.inject();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // The call should notify the interceptor that a display was created.
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+    // Destroying the display invalidates the display state.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.destroyDisplay(existing.token());
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display should have been removed from the current state
+    EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
+
+    // Ths display should still exist in the drawing state
+    EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
+
+    // The display transaction needed flasg should be set
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+}
+
+TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    sp<BBinder> displayToken = new BBinder();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.destroyDisplay(displayToken);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
new file mode 100644
index 0000000..0171f1b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+    GetDisplayNativePrimaries();
+    void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+    void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+    static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+    SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+    injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+        ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    primaries.red.X = startingVal++;
+    primaries.red.Y = startingVal++;
+    primaries.red.Z = startingVal++;
+    primaries.green.X = startingVal++;
+    primaries.green.Y = startingVal++;
+    primaries.green.Z = startingVal++;
+    primaries.blue.X = startingVal++;
+    primaries.blue.Y = startingVal++;
+    primaries.blue.Z = startingVal++;
+    primaries.white.X = startingVal++;
+    primaries.white.Y = startingVal++;
+    primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+        const ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    EXPECT_EQ(primaries.red.X, startingVal++);
+    EXPECT_EQ(primaries.red.Y, startingVal++);
+    EXPECT_EQ(primaries.red.Z, startingVal++);
+    EXPECT_EQ(primaries.green.X, startingVal++);
+    EXPECT_EQ(primaries.green.Y, startingVal++);
+    EXPECT_EQ(primaries.green.Z, startingVal++);
+    EXPECT_EQ(primaries.blue.X, startingVal++);
+    EXPECT_EQ(primaries.blue.Y, startingVal++);
+    EXPECT_EQ(primaries.blue.Z, startingVal++);
+    EXPECT_EQ(primaries.white.X, startingVal++);
+    EXPECT_EQ(primaries.white.Y, startingVal++);
+    EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+    injector.inject();
+    auto internalDisplayToken = injector.token();
+
+    ui::DisplayPrimaries expectedPrimaries;
+    populateDummyDisplayNativePrimaries(expectedPrimaries);
+    mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+    sp<BBinder> notInternalDisplayToken = new BBinder();
+
+    ui::DisplayPrimaries primaries;
+    populateDummyDisplayNativePrimaries(primaries);
+    EXPECT_EQ(NAME_NOT_FOUND,
+              mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+    // Check primaries argument wasn't modified in case of failure
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
new file mode 100644
index 0000000..cd3f6ab
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -0,0 +1,734 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class HandleTransactionLockedTest : public DisplayTransactionTest {
+public:
+    template <typename Case>
+    void setupCommonPreconditions();
+
+    template <typename Case, bool connected>
+    static void expectHotplugReceived(mock::EventThread*);
+
+    template <typename Case>
+    void setupCommonCallExpectationsForConnectProcessing();
+
+    template <typename Case>
+    void setupCommonCallExpectationsForDisconnectProcessing();
+
+    template <typename Case>
+    void processesHotplugConnectCommon();
+
+    template <typename Case>
+    void ignoresHotplugConnectCommon();
+
+    template <typename Case>
+    void processesHotplugDisconnectCommon();
+
+    template <typename Case>
+    void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
+
+    template <typename Case>
+    void verifyPhysicalDisplayIsConnected();
+
+    void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
+};
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonPreconditions() {
+    // Wide color displays support is configured appropriately
+    Case::WideColorSupport::injectConfigChange(this);
+
+    // SurfaceFlinger will use a test-controlled factory for BufferQueues
+    injectFakeBufferQueueFactory();
+
+    // SurfaceFlinger will use a test-controlled factory for native window
+    // surfaces.
+    injectFakeNativeWindowSurfaceFactory();
+}
+
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+    const auto convert = [](auto physicalDisplayId) {
+        return std::make_optional(DisplayId{physicalDisplayId});
+    };
+
+    EXPECT_CALL(*eventThread,
+                onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+            .Times(1);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+    Case::Display::setupHwcHotplugCallExpectations(this);
+
+    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+    Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
+    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+    Case::WideColorSupport::setupComposerCallExpectations(this);
+    Case::HdrSupport::setupComposerCallExpectations(this);
+    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+    expectHotplugReceived<Case, true>(mEventThread);
+    expectHotplugReceived<Case, true>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+    expectHotplugReceived<Case, false>(mEventThread);
+    expectHotplugReceived<Case, false>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+    // The display device should have been set up in the list of displays.
+    ASSERT_TRUE(hasDisplayDevice(displayToken));
+    const auto& device = getDisplayDevice(displayToken);
+    EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+
+    std::optional<DisplayDeviceState::Physical> expectedPhysical;
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        ASSERT_TRUE(displayId);
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        expectedPhysical = {.id = *displayId,
+                            .type = *connectionType,
+                            .hwcDisplayId = *hwcDisplayId};
+    }
+
+    // The display should have been set up in the current display state
+    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+    const auto& current = getCurrentDisplayState(displayToken);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+    EXPECT_EQ(expectedPhysical, current.physical);
+
+    // The display should have been set up in the drawing display state
+    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+    const auto& draw = getDrawingDisplayState(displayToken);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+    EXPECT_EQ(expectedPhysical, draw.physical);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+    // HWComposer should have an entry for the display
+    EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+    // SF should have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
+
+    verifyDisplayIsConnected<Case>(displayToken);
+}
+
+void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+    EXPECT_FALSE(hasDisplayDevice(displayToken));
+    EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+    EXPECT_FALSE(hasDrawingDisplayState(displayToken));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    setupCommonPreconditions<Case>();
+
+    // A hotplug connect event is enqueued for a display
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    setupCommonCallExpectationsForConnectProcessing<Case>();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    verifyPhysicalDisplayIsConnected<Case>();
+
+    // --------------------------------------------------------------------
+    // Cleanup conditions
+
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    setupCommonPreconditions<Case>();
+
+    // A hotplug connect event is enqueued for a display
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // HWComposer should not have an entry for the display
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    setupCommonPreconditions<Case>();
+
+    // A hotplug disconnect event is enqueued for a display
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+    // The display is already completely set up.
+    Case::Display::injectHwcDisplay(this);
+    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+    existing.inject();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+            .Times(0);
+
+    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // HWComposer should not have an entry for the display
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+    // SF should not have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+
+    // The existing token should have been removed
+    verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest,
+       processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
+    // Inject an external display.
+    ExternalDisplayVariant::injectHwcDisplay(this);
+
+    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+    // Inject a primary display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+
+    processesHotplugConnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+    // Inject both a primary and external display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+    ExternalDisplayVariant::injectHwcDisplay(this);
+
+    // TODO: This is an unnecessary call.
+    EXPECT_CALL(*mComposer,
+                getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+            .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+                            SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+                            Return(Error::NONE)));
+
+    ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
+    processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+    processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    setupCommonPreconditions<Case>();
+
+    // A hotplug connect event is enqueued for a display
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+    // A hotplug disconnect event is also enqueued for the same display
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    setupCommonCallExpectationsForConnectProcessing<Case>();
+    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // HWComposer should not have an entry for the display
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+    // SF should not have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    setupCommonPreconditions<Case>();
+
+    // The display is already completely set up.
+    Case::Display::injectHwcDisplay(this);
+    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+    existing.inject();
+
+    // A hotplug disconnect event is enqueued for a display
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    // A hotplug connect event is also enqueued for the same display
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    setupCommonCallExpectationsForConnectProcessing<Case>();
+    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The existing token should have been removed
+    verifyDisplayIsNotConnected(existing.token());
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+
+    // A new display should be connected in its place
+
+    verifyPhysicalDisplayIsConnected<Case>();
+
+    // --------------------------------------------------------------------
+    // Cleanup conditions
+
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+    using Case = HwcVirtualDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // The HWC supports at least one virtual display
+    injectMockComposer(1);
+
+    setupCommonPreconditions<Case>();
+
+    // A virtual display was added to the current state, and it has a
+    // surface(producer)
+    sp<BBinder> displayToken = new BBinder();
+
+    DisplayDeviceState state;
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+    sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
+    state.surface = surface;
+    mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+    EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
+    EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
+    EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
+                                  Return(NO_ERROR)));
+    EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
+
+    EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
+
+    EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
+    EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
+
+    Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
+    Case::WideColorSupport::setupComposerCallExpectations(this);
+    Case::HdrSupport::setupComposerCallExpectations(this);
+    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display device should have been set up in the list of displays.
+    verifyDisplayIsConnected<Case>(displayToken);
+
+    // --------------------------------------------------------------------
+    // Cleanup conditions
+
+    EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
+            .WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+    // Cleanup
+    mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+    mFlinger.mutableDrawingState().displays.removeItem(displayToken);
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+    using Case = HwcVirtualDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // The HWC supports at least one virtual display
+    injectMockComposer(1);
+
+    setupCommonPreconditions<Case>();
+
+    // A virtual display was added to the current state, but it does not have a
+    // surface.
+    sp<BBinder> displayToken = new BBinder();
+
+    DisplayDeviceState state;
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+    mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // There will not be a display device set up.
+    EXPECT_FALSE(hasDisplayDevice(displayToken));
+
+    // The drawing display state will be set from the current display state.
+    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+    const auto& draw = getDrawingDisplayState(displayToken);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+    using Case = HwcVirtualDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A virtual display is set up but is removed from the current state.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+    Case::Display::injectHwcDisplay(this);
+    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+    existing.inject();
+    mFlinger.mutableCurrentState().displays.removeItem(existing.token());
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The existing token should have been removed
+    verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    constexpr uint32_t oldLayerStack = 0u;
+    constexpr uint32_t newLayerStack = 123u;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a change to the layerStack state
+    display.mutableDrawingDisplayState().layerStack = oldLayerStack;
+    display.mutableCurrentDisplayState().layerStack = newLayerStack;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+    constexpr ui::Rotation newTransform = ui::ROTATION_180;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a change to the orientation state
+    display.mutableDrawingDisplayState().orientation = oldTransform;
+    display.mutableCurrentDisplayState().orientation = newTransform;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    const Rect oldLayerStackRect(0, 0, 0, 0);
+    const Rect newLayerStackRect(0, 0, 123, 456);
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+    display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    const Rect oldFrame(0, 0, 0, 0);
+    const Rect newFrame(0, 0, 123, 456);
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    constexpr int oldWidth = 0;
+    constexpr int oldHeight = 10;
+    constexpr int newWidth = 123;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto nativeWindow = new mock::NativeWindow();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.setNativeWindow(nativeWindow);
+    display.setDisplaySurface(displaySurface);
+    // Setup injection expectations
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+    display.inject();
+
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().width = oldWidth;
+    display.mutableDrawingDisplayState().height = oldHeight;
+    display.mutableCurrentDisplayState().width = newWidth;
+    display.mutableCurrentDisplayState().height = oldHeight;
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    constexpr int oldWidth = 0;
+    constexpr int oldHeight = 10;
+    constexpr int newHeight = 123;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto nativeWindow = new mock::NativeWindow();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.setNativeWindow(nativeWindow);
+    display.setDisplaySurface(displaySurface);
+    // Setup injection expectations
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+    display.inject();
+
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().width = oldWidth;
+    display.mutableDrawingDisplayState().height = oldHeight;
+    display.mutableCurrentDisplayState().width = oldWidth;
+    display.mutableCurrentDisplayState().height = newHeight;
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
new file mode 100644
index 0000000..69e0501
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <android/hardware/power/Boost.h>
+
+namespace android {
+namespace {
+
+using android::hardware::power::Boost;
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+    mFlinger.scheduler()->replaceTouchTimer(100);
+    std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
+    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+    std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+    std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
new file mode 100644
index 0000000..42f4cf3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnHotplugReceivedTest : public DisplayTransactionTest {};
+
+TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) {
+    constexpr int currentSequenceId = 123;
+    constexpr HWDisplayId hwcDisplayId1 = 456;
+    constexpr HWDisplayId hwcDisplayId2 = 654;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Set the current sequence id for accepted events
+    mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+    // Set the main thread id so that the current thread does not appear to be
+    // the main thread.
+    mFlinger.mutableMainThreadId() = std::thread::id();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // We expect invalidate() to be invoked once to trigger display transaction
+    // processing.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    // Simulate two hotplug events (a connect and a disconnect)
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display transaction needed flag should be set.
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+    // All events should be in the pending event queue.
+    const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
+    ASSERT_EQ(2u, pendingEvents.size());
+    EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
+    EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
+    EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
+    EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) {
+    constexpr int currentSequenceId = 123;
+    constexpr int otherSequenceId = 321;
+    constexpr HWDisplayId displayId = 456;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Set the current sequence id for accepted events
+    mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+    // Set the main thread id so that the current thread does not appear to be
+    // the main thread.
+    mFlinger.mutableMainThreadId() = std::thread::id();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // We do not expect any calls to invalidate().
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    // Call with an unexpected sequence id
+    mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display transaction needed flag should not be set
+    EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+    // There should be no pending events
+    EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
+    constexpr int currentSequenceId = 123;
+    constexpr HWDisplayId displayId1 = 456;
+
+    // --------------------------------------------------------------------
+    // Note:
+    // --------------------------------------------------------------------
+    // This test case is a bit tricky. We want to verify that
+    // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
+    // don't really want to provide coverage for everything the later function
+    // does as there are specific tests for it.
+    // --------------------------------------------------------------------
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Set the current sequence id for accepted events
+    mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+    // Set the main thread id so that the current thread does appear to be the
+    // main thread.
+    mFlinger.mutableMainThreadId() = std::this_thread::get_id();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // We expect invalidate() to be invoked once to trigger display transaction
+    // processing.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    // Simulate a disconnect on a display id that is not connected. This should
+    // be enqueued by onHotplugReceived(), and dequeued by
+    // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+    mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The display transaction needed flag should be set.
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+    // There should be no event queued on return, as it should have been
+    // processed.
+    EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
new file mode 100644
index 0000000..7a9403b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+
+TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A primary display is set up
+    Case::Display::injectHwcDisplay(this);
+    auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
+    primaryDisplay.inject();
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // We expect the surface interceptor to possibly be used, but we treat it as
+    // disabled since it is called as a side effect rather than directly by this
+    // function.
+    EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
+
+    // We expect a call to get the active display config.
+    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+    // We expect invalidate() to be invoked once to trigger display transaction
+    // processing.
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.onInitializeDisplays();
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The primary display should have a current state
+    ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
+    const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+    // The layer stack state should be set to zero
+    EXPECT_EQ(0u, primaryDisplayState.layerStack);
+    // The orientation state should be set to zero
+    EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
+
+    // The orientedDisplaySpaceRect state should be set to INVALID
+    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
+
+    // The layerStackSpaceRect state should be set to INVALID
+    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
+
+    // The width and height should both be zero
+    EXPECT_EQ(0u, primaryDisplayState.width);
+    EXPECT_EQ(0u, primaryDisplayState.height);
+
+    // The display should be set to PowerMode::ON
+    ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
+    auto displayDevice = primaryDisplay.mutableDisplayDevice();
+    EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+
+    // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
+    FrameStats stats;
+    mFlinger.getAnimFrameTracker().getStats(&stats);
+    EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
+
+    // The display transaction needed flag should be set.
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+    // The compositor timing should be set to default values
+    const auto& compositorTiming = mFlinger.getCompositorTiming();
+    EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
+    EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
+    EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
new file mode 100644
index 0000000..be01984
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class SetDisplayStateLockedTest : public DisplayTransactionTest {};
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // We have an unknown display token not associated with a known display
+    sp<BBinder> displayToken = new BBinder();
+
+    // The requested display state references the unknown display.
+    DisplayState state;
+    state.what = DisplayState::eLayerStackChanged;
+    state.token = displayToken;
+    state.layerStack = 456;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The display token still doesn't match anything known.
+    EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is already set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // No changes are made to the display
+    DisplayState state;
+    state.what = 0;
+    state.token = display.token();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is already set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a surface that can be set.
+    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+    // The current display state has the surface set
+    display.mutableCurrentDisplayState().surface = surface;
+
+    // The incoming request sets the same surface
+    DisplayState state;
+    state.what = DisplayState::eSurfaceChanged;
+    state.token = display.token();
+    state.surface = surface;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The current display state is unchanged.
+    EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is already set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // There is a surface that can be set.
+    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+    // The current display state does not have a surface
+    display.mutableCurrentDisplayState().surface = nullptr;
+
+    // The incoming request sets a surface
+    DisplayState state;
+    state.what = DisplayState::eSurfaceChanged;
+    state.token = display.token();
+    state.surface = surface;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display layer stack state is set to the new value
+    EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is already set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display has a layer stack set
+    display.mutableCurrentDisplayState().layerStack = 456u;
+
+    // The incoming request sets the same layer stack
+    DisplayState state;
+    state.what = DisplayState::eLayerStackChanged;
+    state.token = display.token();
+    state.layerStack = 456u;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The current display state is unchanged
+    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display has a layer stack set
+    display.mutableCurrentDisplayState().layerStack = 654u;
+
+    // The incoming request sets a different layer stack
+    DisplayState state;
+    state.what = DisplayState::eLayerStackChanged;
+    state.token = display.token();
+    state.layerStack = 456u;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The desired display state has been set to the new value.
+    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
+    using Case = SimplePrimaryDisplayCase;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
+    const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+    const Rect initialLayerStackRect = {5, 6, 7, 8};
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The current display state projection state is all set
+    display.mutableCurrentDisplayState().orientation = initialOrientation;
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+    display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+    // The incoming request sets the same projection state
+    DisplayState state;
+    state.what = DisplayState::eDisplayProjectionChanged;
+    state.token = display.token();
+    state.orientation = initialOrientation;
+    state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+    state.layerStackSpaceRect = initialLayerStackRect;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The current display state is unchanged
+    EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
+
+    EXPECT_EQ(initialOrientedDisplayRect,
+              display.getCurrentDisplayState().orientedDisplaySpaceRect);
+    EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
+    using Case = SimplePrimaryDisplayCase;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+    constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The current display state has an orientation set
+    display.mutableCurrentDisplayState().orientation = initialOrientation;
+
+    // The incoming request sets a different orientation
+    DisplayState state;
+    state.what = DisplayState::eDisplayProjectionChanged;
+    state.token = display.token();
+    state.orientation = desiredOrientation;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display state has the new value.
+    EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
+    using Case = SimplePrimaryDisplayCase;
+    const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+    const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The current display state does not have a orientedDisplaySpaceRect
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+
+    // The incoming request sets a orientedDisplaySpaceRect
+    DisplayState state;
+    state.what = DisplayState::eDisplayProjectionChanged;
+    state.token = display.token();
+    state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display state has the new value.
+    EXPECT_EQ(desiredOrientedDisplayRect,
+              display.getCurrentDisplayState().orientedDisplaySpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
+    using Case = SimplePrimaryDisplayCase;
+    const Rect initialLayerStackRect = {0, 0, 0, 0};
+    const Rect desiredLayerStackRect = {5, 6, 7, 8};
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The current display state does not have a layerStackSpaceRect
+    display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+    // The incoming request sets a layerStackSpaceRect
+    DisplayState state;
+    state.what = DisplayState::eDisplayProjectionChanged;
+    state.token = display.token();
+    state.layerStackSpaceRect = desiredLayerStackRect;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display state has the new value.
+    EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
+    using Case = SimplePrimaryDisplayCase;
+    constexpr uint32_t initialWidth = 1024;
+    constexpr uint32_t initialHeight = 768;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The current display state has a size set
+    display.mutableCurrentDisplayState().width = initialWidth;
+    display.mutableCurrentDisplayState().height = initialHeight;
+
+    // The incoming request sets the same display size
+    DisplayState state;
+    state.what = DisplayState::eDisplaySizeChanged;
+    state.token = display.token();
+    state.width = initialWidth;
+    state.height = initialHeight;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The current display state is unchanged
+    EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
+    EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
+    using Case = SimplePrimaryDisplayCase;
+    constexpr uint32_t initialWidth = 0;
+    constexpr uint32_t desiredWidth = 1024;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display does not yet have a width
+    display.mutableCurrentDisplayState().width = initialWidth;
+
+    // The incoming request sets a display width
+    DisplayState state;
+    state.what = DisplayState::eDisplaySizeChanged;
+    state.token = display.token();
+    state.width = desiredWidth;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display state has the new value.
+    EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
+    using Case = SimplePrimaryDisplayCase;
+    constexpr uint32_t initialHeight = 0;
+    constexpr uint32_t desiredHeight = 768;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display does not yet have a height
+    display.mutableCurrentDisplayState().height = initialHeight;
+
+    // The incoming request sets a display height
+    DisplayState state;
+    state.what = DisplayState::eDisplaySizeChanged;
+    state.token = display.token();
+    state.height = desiredHeight;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The current display state has the new value.
+    EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
new file mode 100644
index 0000000..2117628
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+// Used when we simulate a display that supports doze.
+template <typename Display>
+struct DozeIsSupportedVariant {
+    static constexpr bool DOZE_SUPPORTED = true;
+    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+            IComposerClient::PowerMode::DOZE;
+    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+            IComposerClient::PowerMode::DOZE_SUSPEND;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(
+                                        std::vector<DisplayCapability>({DisplayCapability::DOZE})),
+                                Return(Error::NONE)));
+    }
+};
+
+template <typename Display>
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+    static constexpr bool DOZE_SUPPORTED = false;
+    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+            IComposerClient::PowerMode::ON;
+    static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+            IComposerClient::PowerMode::ON;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+                                Return(Error::NONE)));
+    }
+};
+
+struct EventThreadBaseSupportedVariant {
+    static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
+        // The callback should not be notified to toggle VSYNC.
+        EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
+
+        // The event thread should not be notified.
+        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+    }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+    static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+        // These calls are only expected for the primary display.
+
+        // Instead expect no calls.
+        setupVsyncAndEventThreadNoCallExpectations(test);
+    }
+
+    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+        // These calls are only expected for the primary display.
+
+        // Instead expect no calls.
+        setupVsyncAndEventThreadNoCallExpectations(test);
+    }
+};
+
+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);
+
+        // The event thread should be notified that the screen was acquired.
+        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+    }
+
+    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+        // The callback should be notified to disable VSYNC.
+        EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
+
+        // The event thread should not be notified that the screen was released.
+        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+    }
+};
+
+struct DispSyncIsSupportedVariant {
+    static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1);
+        EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+    }
+};
+
+struct DispSyncNotSupportedVariant {
+    static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <PowerMode initialPowerMode, PowerMode targetPowerMode>
+struct TransitionVariantCommon {
+    static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+    static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+    static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
+        Case::setupRepaintEverythingCallExpectations(test);
+    }
+
+    static void verifyPostconditions(DisplayTransactionTest* test) {
+        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+    }
+};
+
+struct TransitionOffToDozeSuspendVariant
+      : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::setupRepaintEverythingCallExpectations(test);
+    }
+
+    static void verifyPostconditions(DisplayTransactionTest* test) {
+        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+    }
+};
+
+struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+    }
+
+    static void verifyPostconditions(DisplayTransactionTest* test) {
+        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+    }
+};
+
+struct TransitionDozeSuspendToOffVariant
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+    }
+
+    static void verifyPostconditions(DisplayTransactionTest* test) {
+        EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+    }
+};
+
+struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+    }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
+        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+    }
+};
+
+struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+    }
+};
+
+struct TransitionDozeSuspendToOnVariant
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
+        Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+    }
+};
+
+struct TransitionOnToDozeSuspendVariant
+      : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+    }
+};
+
+struct TransitionOnToUnknownVariant
+      : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
+    template <typename Case>
+    static void setupCallExpectations(DisplayTransactionTest* test) {
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::setupNoComposerPowerModeCallExpectations(test);
+    }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+          typename DispSyncVariant, typename TransitionVariant>
+struct DisplayPowerCase {
+    using Display = DisplayVariant;
+    using Doze = DozeVariant;
+    using EventThread = EventThreadVariant;
+    using DispSync = DispSyncVariant;
+    using Transition = TransitionVariant;
+
+    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+        Display::injectHwcDisplayWithNoDefaultCapabilities(test);
+        auto display = Display::makeFakeExistingDisplayInjector(test);
+        display.inject();
+        display.mutableDisplayDevice()->setPowerMode(mode);
+        return display;
+    }
+
+    static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+        test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
+    }
+
+    static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+    }
+
+    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
+                                                        PowerMode mode) {
+        EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
+        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
+                .Times(1);
+    }
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
+        // Any calls to get the active config will return a default value.
+        EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+                                      Return(Error::NONE)));
+
+        // Any calls to get whether the display supports dozing will return the value set by the
+        // policy variant.
+        EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+        EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+    }
+
+    static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+    }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase =
+        DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+                         EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+                         TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+        DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+                         EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+                         TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+    template <typename Case>
+    void transitionDisplayCommon();
+};
+
+template <PowerMode PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    Case::Doze::setupComposerCallExpectations(this);
+    auto display =
+            Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+    Case::setInitialPrimaryHWVsyncEnabled(this,
+                                          PowerModeInitialVSyncEnabled<
+                                                  Case::Transition::INITIAL_POWER_MODE>::value);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+    Case::Transition::template setupCallExpectations<Case>(this);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+                                  Case::Transition::TARGET_POWER_MODE);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A primary display device is set up
+    Case::Display::injectHwcDisplay(this);
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display is already set to PowerMode::ON
+    display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+    using Case = HwcVirtualDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Insert display data so that the HWC thinks it created the virtual display.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+
+    // A virtual display device is set up
+    Case::Display::injectHwcDisplay(this);
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display is set to PowerMode::ON
+    getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+    transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+    transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
new file mode 100644
index 0000000..61f0788
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, and the display should respond with an non-empty list of supported
+// color modes. Wide-color support should be configured.
+template <typename Display>
+struct WideColorP3ColorimetricSupportedVariant {
+    static constexpr bool WIDE_COLOR_SUPPORTED = true;
+
+    static void injectConfigChange(DisplayTransactionTest* test) {
+        test->mFlinger.mutableUseColorManagement() = true;
+        test->mFlinger.mutableHasWideColorDisplay() = true;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+    }
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
+
+        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
+                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
+                .WillOnce(DoAll(SetArgPointee<2>(
+                                        std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
+                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
+                                 RenderIntent::COLORIMETRIC))
+                .WillOnce(Return(Error::NONE));
+    }
+};
+
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = true;
+    static constexpr bool HDR10_SUPPORTED = true;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+                                        Hdr::HDR10_PLUS,
+                                        Hdr::HDR10,
+                                })),
+                                Return(Error::NONE)));
+    }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HDR10, so HDR10 support should be configured.
+template <typename Display>
+struct Hdr10SupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
+    static constexpr bool HDR10_SUPPORTED = true;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
+                                Return(Error::NONE)));
+    }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HLG, so HLG support should be configured.
+template <typename Display>
+struct HdrHlgSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
+    static constexpr bool HDR10_SUPPORTED = false;
+    static constexpr bool HDR_HLG_SUPPORTED = true;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+                .WillOnce(
+                        DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
+    }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
+template <typename Display>
+struct HdrDolbyVisionSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
+    static constexpr bool HDR10_SUPPORTED = false;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
+                                Return(Error::NONE)));
+    }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+                        PerFrameMetadataKey::WHITE_POINT_X,
+                        PerFrameMetadataKey::WHITE_POINT_Y,
+                        PerFrameMetadataKey::MAX_LUMINANCE,
+                        PerFrameMetadataKey::MIN_LUMINANCE,
+                })));
+    }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+                        PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+                })));
+    }
+};
+
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::HDR10_PLUS_SEI,
+                })));
+    }
+};
+
+using WideColorP3ColorimetricDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
+             HdrNotSupportedVariant<PrimaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             Hdr10SupportedVariant<PrimaryDisplayVariant>,
+             Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10DisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             Hdr10SupportedVariant<PrimaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrHlgDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrDolbyVisionDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+             NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             HdrNotSupportedVariant<PrimaryDisplayVariant>,
+             Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             HdrNotSupportedVariant<PrimaryDisplayVariant>,
+             Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+
+class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
+public:
+    template <typename T>
+    void setupNewDisplayDeviceInternalTest();
+};
+
+template <typename Case>
+void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
+    const sp<BBinder> displayToken = new BBinder();
+    const sp<compositionengine::mock::DisplaySurface> displaySurface =
+            new compositionengine::mock::DisplaySurface();
+    const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Wide color displays support is configured appropriately
+    Case::WideColorSupport::injectConfigChange(this);
+
+    // The display is setup with the HWC.
+    Case::Display::injectHwcDisplay(this);
+
+    // SurfaceFlinger will use a test-controlled factory for native window
+    // surfaces.
+    injectFakeNativeWindowSurfaceFactory();
+
+    // A compositionengine::Display has already been created
+    auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    // Various native window calls will be made.
+    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+    Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+    Case::WideColorSupport::setupComposerCallExpectations(this);
+    Case::HdrSupport::setupComposerCallExpectations(this);
+    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    DisplayDeviceState state;
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        ASSERT_TRUE(displayId);
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
+    }
+
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+    auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                         displaySurface, producer);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    ASSERT_TRUE(device != nullptr);
+    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
+    EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+    EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
+    EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
+    EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+    EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
+    EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
+    EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
+    EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
+    // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
+    // remapped, and the test only ever sets up one config. If there were an error
+    // looking up the remapped index, device->getActiveConfig() would be -1 instead.
+    EXPECT_EQ(0, device->getActiveConfig().value());
+    EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+              device->getSupportedPerFrameMetadata());
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
+    setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
+    setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
+    setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
+    setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
+    setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+    setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
+    setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
+    setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
+    setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+    setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+    setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 806f95c..a9d9dc0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -16,71 +16,68 @@
 
 #pragma once
 
+#include <Scheduler/Scheduler.h>
 #include <gmock/gmock.h>
 #include <gui/ISurfaceComposer.h>
 
-#include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
-class TestableScheduler : public Scheduler, private ISchedulerCallback {
+class TestableScheduler : public Scheduler {
 public:
-    TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
-          : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
-        if (mUseContentDetectionV2) {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
-        } else {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
-        }
-    }
+    TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+                      bool useContentDetectionV2)
+          : TestableScheduler(std::make_unique<mock::VsyncController>(),
+                              std::make_unique<mock::VSyncTracker>(), configs, callback,
+                              useContentDetectionV2) {}
 
-    TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
-                      std::unique_ptr<EventControlThread> eventControlThread,
-                      const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
-          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
-                      useContentDetectionV2, true) {
-        if (mUseContentDetectionV2) {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
-        } else {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
-        }
-    }
+    TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+                      std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+                      const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+                      bool useContentDetectionV2)
+          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
+                      callback, createLayerHistory(configs, useContentDetectionV2),
+                      {.supportKernelTimer = false,
+                       .useContentDetection = true,
+                       .useContentDetectionV2 = useContentDetectionV2}) {}
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
         return Scheduler::createConnection(std::move(eventThread));
     }
 
-    size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
-        if (mUseContentDetectionV2) {
-            return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
-                    ->mLayerInfos.size();
-        } else {
-            return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
-                    ->mLayerInfos.size();
-        }
-    }
-
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
     auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
-    auto& mutableEventControlThread() { return mEventControlThread; }
-    auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
 
-    auto mutableLayerHistory() {
+    bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+    auto* mutableLayerHistory() {
+        LOG_ALWAYS_FATAL_IF(mOptions.useContentDetectionV2);
         return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
     }
 
-    auto mutableLayerHistoryV2() {
+    auto* mutableLayerHistoryV2() {
+        LOG_ALWAYS_FATAL_IF(!mOptions.useContentDetectionV2);
         return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
     }
 
+    size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+        if (!mLayerHistory) return 0;
+        return mOptions.useContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
+                                              : mutableLayerHistory()->mLayerInfos.size();
+    }
+
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
             mTouchTimer.reset();
@@ -97,20 +94,30 @@
         return mFeatures.touch == Scheduler::TouchState::Active;
     }
 
+    void dispatchCachedReportedConfig() {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        return Scheduler::dispatchCachedReportedConfig();
+    }
+
+    void clearOptionalFieldsInFeatures() {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        mFeatures.cachedConfigChangedParams.reset();
+    }
+
+    void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                          HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+        return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId,
+                                                           vsyncPeriod);
+    }
+
     ~TestableScheduler() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the Scheduler instance may
         // still be referenced by something despite our best efforts to destroy
         // it after each test is done.
-        mutableEventControlThread().reset();
-        mutablePrimaryDispSync().reset();
+        mVsyncSchedule.controller.reset();
         mConnections.clear();
     }
-
-private:
-    void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
-    void repaintEverythingForHWC() override {}
-    void kernelTimerChanged(bool /*expired*/) override {}
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4652da0..6ce738a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -29,7 +29,7 @@
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
 #include "EffectLayer.h"
-#include "FakePhaseOffsets.h"
+#include "FakeVsyncConfiguration.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/MessageQueue.h"
@@ -40,6 +40,7 @@
 #include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockDisplayIdGenerator.h"
 
 namespace android {
 
@@ -65,15 +66,6 @@
 public:
     ~Factory() = default;
 
-    std::unique_ptr<DispSync> createDispSync(const char*, bool) override {
-        return nullptr;
-    }
-
-    std::unique_ptr<EventControlThread> createEventControlThread(
-            std::function<void(bool)>) override {
-        return nullptr;
-    }
-
     std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
         return nullptr;
     }
@@ -82,19 +74,18 @@
         return std::make_unique<android::impl::MessageQueue>();
     }
 
-    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
-                                               const scheduler::RefreshRateConfigs&,
+    std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                ISchedulerCallback&) override {
         return nullptr;
     }
 
-    std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
-        return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
+        return new android::impl::SurfaceInterceptor();
     }
 
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
@@ -175,12 +166,15 @@
 
 } // namespace surfaceflinger::test
 
-class TestableSurfaceFlinger {
+class TestableSurfaceFlinger final : private ISchedulerCallback {
 public:
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
     SurfaceFlinger* flinger() { return mFlinger.get(); }
     TestableScheduler* scheduler() { return mScheduler; }
+    mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() {
+        return mGpuVirtualDisplayIdGenerator;
+    }
 
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
@@ -198,37 +192,43 @@
         mFlinger->mCompositionEngine->setTimeStats(timeStats);
     }
 
-    void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
-                        std::unique_ptr<EventControlThread> eventControlThread,
+    // The ISchedulerCallback argument can be nullptr for a no-op implementation.
+    void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+                        std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
-                        bool useContentDetectionV2 = false) {
+                        ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
         std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
                 HWC2::Display::Config::Builder(mDisplay, 0)
-                        .setVsyncPeriod(int32_t(16666667))
+                        .setVsyncPeriod(16'666'667)
                         .setConfigGroup(0)
                         .build()};
 
+        if (hasMultipleConfigs) {
+            configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
+                                         .setVsyncPeriod(11'111'111)
+                                         .setConfigGroup(0)
+                                         .build());
+        }
+
         mFlinger->mRefreshRateConfigs = std::make_unique<
                 scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
         mFlinger->mRefreshRateStats = std::make_unique<
                 scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
                                              /*currentConfig=*/HwcConfigIndexType(0),
                                              /*powerMode=*/hal::PowerMode::OFF);
-        mFlinger->mPhaseConfiguration =
-                mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+        mFlinger->mVsyncConfiguration =
+                mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+        mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
-        mScheduler =
-                new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
-                                      *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+        constexpr bool kUseContentDetectionV2 = false;
+        mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                           *mFlinger->mRefreshRateConfigs, *(callback ?: this),
+                                           kUseContentDetectionV2);
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
         resetScheduler(mScheduler);
-
-        mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
-                                          mFlinger->mSfConnectionHandle,
-                                          mFlinger->mPhaseConfiguration->getCurrentOffsets());
     }
 
     void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -277,6 +277,10 @@
         layer->mPotentialCursor = potentialCursor;
     }
 
+    static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
+        layer->mDrawingParent = drawingParent;
+    }
+
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
@@ -289,8 +293,6 @@
         return mFlinger->destroyDisplay(displayToken);
     }
 
-    auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
-
     auto setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -329,21 +331,22 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
-
-    auto captureScreenImplLocked(const RenderArea& renderArea,
-                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
-                                 const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
-                                 bool forSystem, int* outSyncFd, bool regionSampling) {
-        bool ignored;
-        return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
-                                                 useIdentityTransform, forSystem, outSyncFd,
-                                                 regionSampling, ignored);
+    auto onMessageReceived(int32_t what) {
+        return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
     }
 
-    auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
-                                 const LayerVector::Visitor& visitor) {
-        return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+    auto renderScreenImplLocked(const RenderArea& renderArea,
+                                SurfaceFlinger::TraverseLayersFunction traverseLayers,
+                                const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
+                                bool regionSampling) {
+        ScreenCaptureResults captureResults;
+        return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
+                                                outSyncFd, regionSampling, captureResults);
+    }
+
+    auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
+                                    const LayerVector::Visitor& visitor) {
+        return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor);
     }
 
     auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -353,20 +356,26 @@
 
     auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
 
-    auto setTransactionState(const Vector<ComposerState>& states,
+    auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
                              bool hasListenerCallbacks,
-                             std::vector<ListenerCallbacks>& listenerCallbacks) {
-        return mFlinger->setTransactionState(states, displays, flags, applyToken,
-                                             inputWindowCommands, desiredPresentTime, uncacheBuffer,
-                                             hasListenerCallbacks, listenerCallbacks);
+                             std::vector<ListenerCallbacks>& listenerCallbacks,
+                             uint64_t transactionId) {
+        return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
+                                             applyToken, inputWindowCommands, desiredPresentTime,
+                                             uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
+                                             transactionId);
     }
 
     auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
 
+    auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+        return mFlinger->onTransact(code, data, reply, flags);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -425,7 +434,7 @@
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
         mutableEventQueue().reset();
-        mutableInterceptor().reset();
+        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
@@ -462,7 +471,8 @@
         static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
         static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
 
-        FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary)
+        FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+                               bool isPrimary)
               : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
 
         auto& setHwcDisplayId(hal::HWDisplayId displayId) {
@@ -535,14 +545,16 @@
             flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
-                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
+                const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
+                LOG_ALWAYS_FATAL_IF(!physicalId);
+                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
                 (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
                             : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
             }
         }
 
     private:
-        const DisplayId mDisplayId;
+        const HalDisplayId mDisplayId;
         const hal::DisplayType mHwcDisplayType;
         const bool mIsPrimary;
 
@@ -634,8 +646,10 @@
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
+                const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
+                LOG_ALWAYS_FATAL_IF(!physicalId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-                state.physical = {.id = *displayId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
+                state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
             }
 
             state.isSecure = mCreationArgs.isSecure;
@@ -659,10 +673,17 @@
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };
 
+private:
+    void setVsyncEnabled(bool) override {}
+    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
+    void repaintEverythingForHWC() override {}
+    void kernelTimerChanged(bool) override {}
+
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
     TestableScheduler* mScheduler = nullptr;
     Hwc2::mock::Display mDisplay;
+    mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7a1c7c6..a90f424 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -58,6 +58,7 @@
 #define FMT_STRING         false
 #define LAYER_ID_0         0
 #define LAYER_ID_1         1
+#define UID_0              123
 #define LAYER_ID_INVALID   -1
 #define NUM_LAYERS         1
 #define NUM_LAYERS_INVALID "INVALID"
@@ -221,13 +222,14 @@
 }
 
 static std::string genLayerName(int32_t layerId) {
-    return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId);
+    return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
 }
 
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
     switch (type) {
         case TimeStamp::POST:
-            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
             break;
         case TimeStamp::ACQUIRE:
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -349,6 +351,61 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canIncreaseJankyFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(4);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(4);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
 TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
     // this stat is not in the proto so verify by checking the string dump
     constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -424,7 +481,7 @@
                                            std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
                                                    .count());
 
-    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    // Push a fake present fence to trigger flushing the RenderEngine timings.
     mTimeStats->setPowerMode(PowerMode::ON);
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
@@ -505,7 +562,7 @@
     ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
     ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
 
-    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    // Push a fake present fence to trigger flushing the RenderEngine timings.
     mTimeStats->setPowerMode(PowerMode::ON);
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
@@ -789,6 +846,16 @@
                                                    .count());
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
     EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
 
     const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
@@ -797,6 +864,11 @@
     EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
     EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
     EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+    EXPECT_THAT(result, HasSubstr("jankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0"));
+    EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0"));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -833,6 +905,15 @@
     ASSERT_EQ(0, globalProto.stats_size());
 }
 
+TEST_F(TimeStatsTest, noInfInAverageFPS) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 1000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageFPS = 0.000"));
+}
+
 namespace {
 std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
                                              const std::vector<int32_t>& frameCounts) {
@@ -895,6 +976,8 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
     mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
     mTimeStats->setPowerMode(PowerMode::ON);
     mTimeStats->recordFrameDuration(1000000, 3000000);
@@ -904,6 +987,12 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
                                      android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -935,6 +1024,12 @@
                                                              expectedRenderEngineTiming.c_str(),
                                                      expectedRenderEngineTiming.size()),
                                              expectedRenderEngineTiming.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
@@ -966,6 +1061,15 @@
     }
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
                                      android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -1024,6 +1128,14 @@
         EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
         EXPECT_CALL(*mDelegate,
                     statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/tests/unittests/TimerTest.cpp
new file mode 100644
index 0000000..cda6bbf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimerTest.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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;
+
+namespace android::scheduler {
+
+struct TimerTest : testing::Test {
+    static constexpr int mIterations = 20;
+
+    AsyncCallRecorder<void (*)()> mCallbackRecorder;
+    Timer 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);
+        EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
+        EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
+    }
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 2a48a22..68cf330 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -31,10 +31,9 @@
 
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -67,21 +66,23 @@
         EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*eventThread, createEventConnection(_, _))
                 .WillOnce(Return(
-                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                        new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                  ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
         EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
                 .WillOnce(Return(
-                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                        new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                  ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
-        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
 
-        mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
-                                std::make_unique<mock::EventControlThread>(),
+        mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
+                                std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
     }
 
@@ -89,10 +90,10 @@
     TestableSurfaceFlinger mFlinger;
 
     std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
 
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+    mock::VsyncController* mVsyncController = new mock::VsyncController();
+    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -101,7 +102,9 @@
         sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
         InputWindowCommands inputWindowCommands;
         int64_t desiredPresentTime = -1;
+        int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
         client_cache_t uncacheBuffer;
+        int64_t id = -1;
     };
 
     void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
@@ -115,26 +118,28 @@
     }
 
     void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
-                     int64_t desiredPresentTime) {
+                     int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
         mTransactionNumber++;
         transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
         transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
         transaction.desiredPresentTime = desiredPresentTime;
+        transaction.frameTimelineVsyncId = frameTimelineVsyncId;
     }
 
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
         // called in SurfaceFlinger::signalTransaction
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime()));
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1);
+                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationTime = systemTime();
-        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+        mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                     transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks);
+                                     mHasListenerCallbacks, mCallbacks, transaction.id);
 
         // This transaction should not have been placed on the transaction queue.
         // If transaction is synchronous or syncs input windows, SF
@@ -159,16 +164,17 @@
         // first check will see desired present time has not passed,
         // but afterwards it will look like the desired present time has passed
         nsecs_t time = systemTime();
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ time + s2ns(1));
+                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+        mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                     transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks);
+                                     mHasListenerCallbacks, mCallbacks, transaction.id);
 
         nsecs_t returnedTime = systemTime();
         EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
@@ -182,24 +188,25 @@
         // called in SurfaceFlinger::signalTransaction
         nsecs_t time = systemTime();
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
         setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                    /*desiredPresentTime*/ time + s2ns(1));
+                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
 
         // transaction that would not have gone on the pending thread if not
         // blocked
         TransactionInfo transactionB;
         setupSingle(transactionB, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1);
+                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
 
         nsecs_t applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+        mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
                                      transactionA.desiredPresentTime, transactionA.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks);
+                                     mHasListenerCallbacks, mCallbacks, transactionA.id);
 
         // This thread should not have been blocked by the above transaction
         // (5s is the timeout period that applyTransactionState waits for SF to
@@ -207,10 +214,11 @@
         EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
 
         applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+        mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
                                      transactionB.desiredPresentTime, transactionB.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks);
+                                     mHasListenerCallbacks, mCallbacks, transactionB.id);
 
         // this thread should have been blocked by the above transaction
         // if this is an animation, this thread should be blocked for 5s
@@ -247,16 +255,17 @@
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
     // nsecs_t time = systemTime();
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
             .WillOnce(Return(nsecs_t(5 * 1e8)))
             .WillOnce(Return(s2ns(2)));
     TransactionInfo transactionA; // transaction to go on pending queue
     setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                /*desiredPresentTime*/ s2ns(1));
-    mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
-                                 transactionA.applyToken, transactionA.inputWindowCommands,
-                                 transactionA.desiredPresentTime, transactionA.uncacheBuffer,
-                                 mHasListenerCallbacks, mCallbacks);
+                /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+    mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+                                 transactionA.displays, transactionA.flags, transactionA.applyToken,
+                                 transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+                                 transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                 transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
     ASSERT_EQ(1, transactionQueue.size());
@@ -272,9 +281,10 @@
     // different process) to re-query and reset the cached expected present time
     TransactionInfo empty;
     empty.applyToken = sp<IBinder>();
-    mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
-                                 empty.inputWindowCommands, empty.desiredPresentTime,
-                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks);
+    mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
+                                 empty.flags, empty.applyToken, empty.inputWindowCommands,
+                                 empty.desiredPresentTime, empty.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index be49ef3..0af5f30 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,8 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    bool needsMoreSamples() const final { return false; }
+    bool isVSyncInPhase(nsecs_t, int) const final { return false; }
     void dump(std::string&) const final {}
 
 private:
@@ -64,7 +66,7 @@
     bool addVsyncTimestamp(nsecs_t) final { return true; }
 
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         auto const normalized_to_base = time_point - mBase;
         auto const floor = (normalized_to_base) % mPeriod;
         if (floor == 0) {
@@ -74,18 +76,20 @@
     }
 
     void set_interval(nsecs_t interval, nsecs_t last_known) {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         mPeriod = interval;
         mBase = last_known;
     }
 
     nsecs_t currentPeriod() const final {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         return mPeriod;
     }
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    bool needsMoreSamples() const final { return false; }
+    bool isVSyncInPhase(nsecs_t, int) const final { return false; }
     void dump(std::string&) const final {}
 
 private:
@@ -102,30 +106,36 @@
 
 class RepeatingCallbackReceiver {
 public:
-    RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
-          : mWorkload(wl),
+    RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
+          : mWorkload(workload),
+            mReadyDuration(readyDuration),
             mCallback(
-                    dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
+                    dispatch, [&](auto time, auto, auto) { callback_called(time); }, "repeat0") {}
 
     void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
         mCallbackTimes.reserve(iterations);
-        mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+        mCallback.schedule(
+                {.workDuration = mWorkload,
+                 .readyDuration = mReadyDuration,
+                 .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
 
         for (auto i = 0u; i < iterations - 1; i++) {
-            std::unique_lock<decltype(mMutex)> lk(mMutex);
-            mCv.wait(lk, [&] { return mCalled; });
+            std::unique_lock lock(mMutex);
+            mCv.wait(lock, [&] { return mCalled; });
             mCalled = false;
             auto last = mLastTarget;
-            lk.unlock();
+            lock.unlock();
 
             onEachFrame(last);
 
-            mCallback.schedule(mWorkload, last + mWorkload);
+            mCallback.schedule({.workDuration = mWorkload,
+                                .readyDuration = mReadyDuration,
+                                .earliestVsync = last + mWorkload + mReadyDuration});
         }
 
         // wait for the last callback.
-        std::unique_lock<decltype(mMutex)> lk(mMutex);
-        mCv.wait(lk, [&] { return mCalled; });
+        std::unique_lock lock(mMutex);
+        mCv.wait(lock, [&] { return mCalled; });
     }
 
     void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
@@ -134,7 +144,7 @@
 
 private:
     void callback_called(nsecs_t time) {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         mCallbackTimes.push_back(time);
         mCalled = true;
         mLastTarget = time;
@@ -142,6 +152,7 @@
     }
 
     nsecs_t const mWorkload;
+    nsecs_t const mReadyDuration;
     VSyncCallbackRegistration mCallback;
 
     std::mutex mMutex;
@@ -158,9 +169,9 @@
 
     static size_t constexpr num_clients = 3;
     std::array<RepeatingCallbackReceiver, num_clients>
-            cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
-                        RepeatingCallbackReceiver(dispatch, toNs(0h)),
-                        RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+            cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us), toNs(2500us)),
+                        RepeatingCallbackReceiver(dispatch, toNs(0h), toNs(0h)),
+                        RepeatingCallbackReceiver(dispatch, toNs(1ms), toNs(3ms))};
 
     auto const on_each_frame = [](nsecs_t) {};
     std::array<std::thread, num_clients> threads{
@@ -185,7 +196,7 @@
     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
                                      mVsyncMoveThreshold);
 
-    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
 
     auto const on_each_frame = [&](nsecs_t last_known) {
         tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
@@ -203,7 +214,7 @@
     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
                                      mVsyncMoveThreshold);
 
-    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
 
     auto jump_frame_counter = 0u;
     auto constexpr jump_frame_at = 10u;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 1899bed..72b5396 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,8 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -63,19 +65,19 @@
 class ControllableClock : public TimeKeeper {
 public:
     ControllableClock() {
-        ON_CALL(*this, alarmIn(_, _))
-                .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+        ON_CALL(*this, alarmAt(_, _))
+                .WillByDefault(Invoke(this, &ControllableClock::alarmAtDefaultBehavior));
         ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
     }
 
     MOCK_CONST_METHOD0(now, nsecs_t());
-    MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+    MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
     MOCK_METHOD0(alarmCancel, void());
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
-    void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+    void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
         mCallback = callback;
-        mNextCallbackTime = time + mCurrentTime;
+        mNextCallbackTime = time;
     }
 
     nsecs_t fakeTime() const { return mCurrentTime; }
@@ -89,15 +91,18 @@
 
     void advanceBy(nsecs_t advancement) {
         mCurrentTime += advancement;
-        if (mCurrentTime >= mNextCallbackTime && mCallback) {
+        if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) {
             mCallback();
         }
     };
 
+    void setLag(nsecs_t lag) { mLag = lag; }
+
 private:
     std::function<void()> mCallback;
     nsecs_t mNextCallbackTime = 0;
     nsecs_t mCurrentTime = 0;
+    nsecs_t mLag = 0;
 };
 
 class CountingCallback {
@@ -105,18 +110,24 @@
     CountingCallback(VSyncDispatch& dispatch)
           : mDispatch(dispatch),
             mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
-                                                       std::placeholders::_1,
-                                                       std::placeholders::_2),
+                                                       std::placeholders::_1, std::placeholders::_2,
+                                                       std::placeholders::_3),
                                              "test")) {}
     ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
 
     operator VSyncDispatch::CallbackToken() const { return mToken; }
 
-    void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
+    void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) {
+        mCalls.push_back(time);
+        mWakeupTime.push_back(wakeup_time);
+        mReadyTime.push_back(readyTime);
+    }
 
     VSyncDispatch& mDispatch;
     VSyncDispatch::CallbackToken mToken;
     std::vector<nsecs_t> mCalls;
+    std::vector<nsecs_t> mWakeupTime;
+    std::vector<nsecs_t> mReadyTime;
 };
 
 class PausingCallback {
@@ -134,18 +145,18 @@
     operator VSyncDispatch::CallbackToken() const { return mToken; }
 
     void pause(nsecs_t, nsecs_t) {
-        std::unique_lock<std::mutex> lk(mMutex);
+        std::unique_lock lock(mMutex);
         mPause = true;
         mCv.notify_all();
 
-        mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+        mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; });
 
         mResourcePresent = (mResource.lock() != nullptr);
     }
 
     bool waitForPause() {
-        std::unique_lock<std::mutex> lk(mMutex);
-        auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+        std::unique_lock lock(mMutex);
+        auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; });
         return waiting;
     }
 
@@ -154,7 +165,7 @@
     bool resourcePresent() { return mResourcePresent; }
 
     void unpause() {
-        std::unique_lock<std::mutex> lk(mMutex);
+        std::unique_lock lock(mMutex);
         mPause = false;
         mCv.notify_all();
     }
@@ -184,8 +195,8 @@
         class TimeKeeperWrapper : public TimeKeeper {
         public:
             TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
-            void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
-                mControllableClock.alarmIn(callback, time);
+            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(); }
@@ -214,22 +225,30 @@
 };
 
 TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
     {
         VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
                                           mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
-        EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+        EXPECT_EQ(mDispatch.schedule(cb,
+                                     {.workDuration = 100,
+                                      .readyDuration = 0,
+                                      .earliestVsync = 1000}),
+                  ScheduleResult::Scheduled);
     }
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
     auto intended = mPeriod - 230;
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 100,
+                                  .readyDuration = 0,
+                                  .earliestVsync = intended}),
+              ScheduleResult::Scheduled);
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -238,10 +257,10 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
-    EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+    EXPECT_CALL(mMockClock, alarmAt(_, 1050));
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, 100, mPeriod);
+    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -254,37 +273,53 @@
     auto const workDuration = 10 * mPeriod;
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
             .WillOnce(Return(mPeriod * 11));
-    EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+    EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = workDuration,
+                                  .readyDuration = 0,
+                                  .earliestVsync = mPeriod}),
+              ScheduleResult::Scheduled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 100,
+                                  .readyDuration = 0,
+                                  .earliestVsync = mPeriod}),
+              ScheduleResult::Scheduled);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 100,
+                                  .readyDuration = 0,
+                                  .earliestVsync = mPeriod}),
+              ScheduleResult::Scheduled);
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
     PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
-    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 100,
+                                  .readyDuration = 0,
+                                  .earliestVsync = mPeriod}),
+              ScheduleResult::Scheduled);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -294,14 +329,18 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
     auto resource = std::make_shared<int>(110);
 
     PausingCallback cb(mDispatch, 50ms);
     cb.stashResource(resource);
-    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 100,
+                                  .readyDuration = 0,
+                                  .earliestVsync = mPeriod}),
+              ScheduleResult::Scheduled);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -324,15 +363,15 @@
             .WillOnce(Return(1075));
 
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 955)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 813)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 975)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 100, mPeriod);
-    mDispatch.schedule(cb1, 250, mPeriod);
+    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
 
     advanceToNextCallback();
     advanceToNextCallback();
@@ -352,53 +391,54 @@
             .WillOnce(Return(10000));
 
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 750)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 100, mPeriod * 10);
-    mDispatch.schedule(cb1, 250, mPeriod);
+    mDispatch.schedule(cb0,
+                       {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
+    mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
     mDispatch.cancel(cb1);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 400, 1000);
-    mDispatch.schedule(cb1, 200, 1000);
-    mDispatch.schedule(cb1, 300, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 400, 1000);
-    mDispatch.schedule(cb1, 200, 1000);
-    mDispatch.schedule(cb1, 500, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1590)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
 
     auto offset = 400;
     auto closeOffset = offset + mDispatchGroupThreshold - 1;
@@ -407,9 +447,10 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 400, 1000);
-    mDispatch.schedule(cb1, 200, 1000);
-    mDispatch.schedule(cb1, closeOffset, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1,
+                       {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
     ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -417,8 +458,9 @@
     ASSERT_THAT(cb1.mCalls.size(), Eq(1));
     EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
 
-    mDispatch.schedule(cb0, 400, 2000);
-    mDispatch.schedule(cb1, notCloseOffset, 2000);
+    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch.schedule(cb1,
+                       {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
     advanceToNextCallback();
     ASSERT_THAT(cb1.mCalls.size(), Eq(2));
     EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -429,16 +471,17 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 900));
-    EXPECT_CALL(mMockClock, alarmIn(_, 800));
-    EXPECT_CALL(mMockClock, alarmIn(_, 100));
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 800)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 100, 1000);
-    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
     EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
 }
@@ -451,32 +494,38 @@
             .WillOnce(Return(2950));
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, 100, 920);
+    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
 
     mMockClock.advanceBy(850);
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
 
-    mDispatch.schedule(cb, 100, 1900);
+    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
     mMockClock.advanceBy(900);
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
     mMockClock.advanceBy(125);
     EXPECT_THAT(cb.mCalls.size(), Eq(2));
 
-    mDispatch.schedule(cb, 100, 2900);
+    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
     mMockClock.advanceBy(975);
     EXPECT_THAT(cb.mCalls.size(), Eq(3));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
 
     VSyncDispatch::CallbackToken tmp;
-    tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
-                                     "o.o");
+    tmp = mDispatch.registerCallback(
+            [&](auto, auto, auto) {
+                mDispatch.schedule(tmp,
+                                   {.workDuration = 100,
+                                    .readyDuration = 0,
+                                    .earliestVsync = 2000});
+            },
+            "o.o");
 
-    mDispatch.schedule(tmp, 100, 1000);
+    mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
@@ -484,17 +533,27 @@
     VSyncDispatch::CallbackToken tmp;
     std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
-            [&](auto timestamp, auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+            [&](auto timestamp, auto, auto) {
+                EXPECT_EQ(mDispatch.schedule(tmp,
+                                             {.workDuration = 400,
+                                              .readyDuration = 0,
+                                              .earliestVsync = timestamp - mVsyncMoveThreshold}),
                           ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+                EXPECT_EQ(mDispatch.schedule(tmp,
+                                             {.workDuration = 400,
+                                              .readyDuration = 0,
+                                              .earliestVsync = timestamp}),
+                          ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp,
+                                             {.workDuration = 400,
+                                              .readyDuration = 0,
+                                              .earliestVsync = timestamp + mVsyncMoveThreshold}),
                           ScheduleResult::Scheduled);
                 lastTarget = timestamp;
             },
             "oo");
 
-    mDispatch.schedule(tmp, 999, 1000);
+    mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
     EXPECT_THAT(lastTarget, Eq(1000));
 
@@ -504,40 +563,40 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 950)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1950)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, 0, 1000);
+    mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
 
     mMockClock.advanceBy(750);
-    mDispatch.schedule(cb, 50, 1000);
+    mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
-    mDispatch.schedule(cb, 50, 2000);
+    mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
 
     mMockClock.advanceBy(800);
-    mDispatch.schedule(cb, 100, 2000);
+    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 850)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1800)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, 500, 1000);
-    mDispatch.schedule(cb1, 100, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
-    mDispatch.schedule(cb0, 200, 2000);
-    mDispatch.schedule(cb1, 150, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
     advanceToNextCallback();
@@ -545,46 +604,58 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
-    mDispatch.schedule(cb0, 500, 1000);
-    mDispatch.schedule(cb1, 500, 20000);
+    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
-    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     mDispatch.cancel(cb0);
-    mDispatch.schedule(cb0, 100, 1000);
+    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
-    EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+    EXPECT_THAT(mDispatch.schedule(token,
+                                   {.workDuration = 100,
+                                    .readyDuration = 0,
+                                    .earliestVsync = 1000}),
+                Eq(ScheduleResult::Error));
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
 }
 
 // b/1450138150
 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 500));
+    EXPECT_CALL(mMockClock, alarmAt(_, 500));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
     mMockClock.advanceBy(400);
 
-    EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
 }
@@ -595,69 +666,240 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1002));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
     mMockClock.advanceBy(400);
-    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
     Sequence seq;
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
-    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0,
+                                 {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 600));
+    EXPECT_CALL(mMockClock, alarmAt(_, 600));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
 
-    EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
 
     advanceToNextCallback();
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
     EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
 
     VSyncCallbackRegistration cb(
-            mDispatch, [](auto, auto) {}, "");
+            mDispatch, [](auto, auto, auto) {}, "");
     VSyncCallbackRegistration cb1(std::move(cb));
-    cb.schedule(100, 1000);
+    cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
     cb.cancel();
 
-    cb1.schedule(500, 1000);
+    cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     cb1.cancel();
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
-    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
     EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
 
     VSyncCallbackRegistration cb(
-            mDispatch, [](auto, auto) {}, "");
+            mDispatch, [](auto, auto, auto) {}, "");
     VSyncCallbackRegistration cb1(
-            mDispatch, [](auto, auto) {}, "");
+            mDispatch, [](auto, auto, auto) {}, "");
     cb1 = std::move(cb);
-    cb.schedule(100, 1000);
+    cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
     cb.cancel();
 
-    cb1.schedule(500, 1000);
+    cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     cb1.cancel();
 }
 
+// b/154303580
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+// b/154303580.
+// If the same callback tries to reschedule itself after it's too late, timer opts to apply the
+// update later, as opposed to blocking the calling thread.
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
+    CountingCallback cb(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// b/154303580.
+TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+              ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+    mMockClock.advanceToNextCallback();
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) {
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    Sequence seq;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+
+    EXPECT_EQ(mDispatch.schedule(cb1,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb2,
+                                 {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
+              ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(700);
+
+    ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
+    EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
+    ASSERT_THAT(cb1.mReadyTime.size(), Eq(1));
+    EXPECT_THAT(cb1.mReadyTime[0], Eq(1000));
+    ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
+    EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
+    ASSERT_THAT(cb2.mReadyTime.size(), Eq(1));
+    EXPECT_THAT(cb2.mReadyTime[0], Eq(1000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmAt(_, 900));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb,
+                                 {.workDuration = 70,
+                                  .readyDuration = 30,
+                                  .earliestVsync = intended}),
+              ScheduleResult::Scheduled);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+    ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+    EXPECT_THAT(cb.mWakeupTime[0], 900);
+    ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+    EXPECT_THAT(cb.mReadyTime[0], 970);
+}
+
 class VSyncDispatchTimerQueueEntryTest : public testing::Test {
 protected:
     nsecs_t const mPeriod = 1000;
@@ -668,7 +910,7 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
     std::string name("basicname");
     VSyncDispatchTimerQueueEntry entry(
-            name, [](auto, auto) {}, mVsyncMoveThreshold);
+            name, [](auto, auto, auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.name(), Eq(name));
     EXPECT_FALSE(entry.lastExecutedVsyncTarget());
     EXPECT_FALSE(entry.wakeupTime());
@@ -676,10 +918,12 @@
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -696,10 +940,12 @@
             .Times(1)
             .WillOnce(Return(10000));
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+                               mStubTracker, now),
+                Eq(ScheduleResult::Scheduled));
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -709,21 +955,29 @@
     auto callCount = 0;
     auto vsyncCalledTime = 0;
     auto wakeupCalledTime = 0;
+    auto readyCalledTime = 0;
     VSyncDispatchTimerQueueEntry entry(
             "test",
-            [&](auto vsyncTime, auto wakeupTime) {
+            [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
                 callCount++;
                 vsyncCalledTime = vsyncTime;
                 wakeupCalledTime = wakeupTime;
+                readyCalledTime = readyTime;
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
 
-    entry.callback(entry.executing(), *wakeup);
+    auto const ready = entry.readyTime();
+    ASSERT_TRUE(ready);
+    EXPECT_THAT(*ready, Eq(1000));
+
+    entry.callback(entry.executing(), *wakeup, *ready);
 
     EXPECT_THAT(callCount, Eq(1));
     EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
@@ -741,13 +995,15 @@
             .WillOnce(Return(1020));
 
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     entry.update(mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
@@ -760,8 +1016,10 @@
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     entry.update(mStubTracker, 0);
 
     auto const wakeup = entry.wakeupTime();
@@ -771,24 +1029,35 @@
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+    EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+    EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+    EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest,
        willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     Sequence seq;
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
@@ -801,20 +1070,83 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
     VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
+    static constexpr auto effectualOffset = 200;
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+    entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
+    entry.addPendingWorkloadUpdate(
+            {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
+    EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
+    entry.update(mStubTracker, 0);
+    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+    EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) {
+    auto callCount = 0;
+    auto vsyncCalledTime = 0;
+    auto wakeupCalledTime = 0;
+    auto readyCalledTime = 0;
+    VSyncDispatchTimerQueueEntry entry(
+            "test",
+            [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
+                callCount++;
+                vsyncCalledTime = vsyncTime;
+                wakeupCalledTime = wakeupTime;
+                readyCalledTime = readyTime;
+            },
+            mVsyncMoveThreshold);
+
+    EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+                               mStubTracker, 0),
+                Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
+
+    auto const ready = entry.readyTime();
+    ASSERT_TRUE(ready);
+    EXPECT_THAT(*ready, Eq(970));
+
+    entry.callback(entry.executing(), *wakeup, *ready);
+
+    EXPECT_THAT(callCount, Eq(1));
+    EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+    EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
+    EXPECT_FALSE(entry.wakeupTime());
+    auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+    ASSERT_TRUE(lastCalledTarget);
+    EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index bf2a889..3d60479 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -59,41 +59,41 @@
 };
 
 TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
-    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    auto model = tracker.getVSyncPredictionModel();
 
-    EXPECT_THAT(slope, Eq(mPeriod));
-    EXPECT_THAT(intercept, Eq(0));
+    EXPECT_THAT(model.slope, Eq(mPeriod));
+    EXPECT_THAT(model.intercept, Eq(0));
 
     auto const changedPeriod = 2000;
     tracker.setPeriod(changedPeriod);
-    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, Eq(changedPeriod));
-    EXPECT_THAT(intercept, Eq(0));
+    model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, Eq(changedPeriod));
+    EXPECT_THAT(model.intercept, Eq(0));
 }
 
 TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
     for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
-        EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
-        tracker.addVsyncTimestamp(mNow);
+        EXPECT_TRUE(tracker.needsMoreSamples());
+        tracker.addVsyncTimestamp(mNow += mPeriod);
     }
-    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+    EXPECT_FALSE(tracker.needsMoreSamples());
 }
 
 TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
     for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
         tracker.addVsyncTimestamp(mNow += mPeriod);
     }
-    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+    EXPECT_FALSE(tracker.needsMoreSamples());
 
     auto const changedPeriod = mPeriod * 2;
     tracker.setPeriod(changedPeriod);
-    EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+    EXPECT_TRUE(tracker.needsMoreSamples());
 
     for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
-        EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
-        tracker.addVsyncTimestamp(mNow);
+        EXPECT_TRUE(tracker.needsMoreSamples());
+        tracker.addVsyncTimestamp(mNow += changedPeriod);
     }
-    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+    EXPECT_FALSE(tracker.needsMoreSamples());
 }
 
 TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
@@ -124,6 +124,38 @@
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
 }
 
+// b/159882858
+TEST_F(VSyncPredictorTest, updatesTimebaseForSyntheticAfterIdleTime) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+
+    auto const halfPeriod = mPeriod >> 2;
+    nsecs_t relativelyLongGapWithDrift = mPeriod * 100 + halfPeriod;
+
+    EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += relativelyLongGapWithDrift));
+
+    tracker.resetModel();
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, uponBadVsyncWillSwitchToSyntheticWhileRecalibrating) {
+    auto const slightlyMorePeriod = mPeriod + 10;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += slightlyMorePeriod));
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyMorePeriod));
+
+    auto const halfPeriod = mPeriod >> 2;
+    EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += halfPeriod));
+
+    tracker.resetModel();
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
 TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) {
     // these are precomputed simulated 16.6s vsyncs with uniform distribution +/- 1.6ms error
     std::vector<nsecs_t> const simulatedVsyncs{
@@ -232,17 +264,17 @@
     }
 
     auto const mMaxRoundingError = 100;
-    auto [slope, intercept] = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
-    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+    auto model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+    EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
 
     tracker.setPeriod(slowPeriod);
     for (auto const& timestamp : simulatedVsyncsSlow) {
         tracker.addVsyncTimestamp(timestamp);
     }
-    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
-    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+    model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+    EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
 }
 
 TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
@@ -264,9 +296,9 @@
     for (auto const& timestamp : simulatedVsyncsFast) {
         tracker.addVsyncTimestamp(timestamp);
     }
-    auto [slope, intercept] = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, Eq(fastPeriod));
-    EXPECT_THAT(intercept, Eq(0));
+    auto model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, Eq(fastPeriod));
+    EXPECT_THAT(model.intercept, Eq(0));
 
     tracker.setPeriod(slowPeriod);
     for (auto const& timestamp : simulatedVsyncsSlow) {
@@ -276,30 +308,16 @@
     // we had a model for 100ns mPeriod before, use that until the new samples are
     // sufficiently built up
     tracker.setPeriod(idealPeriod);
-    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, Eq(fastPeriod));
-    EXPECT_THAT(intercept, Eq(0));
+    model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, Eq(fastPeriod));
+    EXPECT_THAT(model.intercept, Eq(0));
 
     for (auto const& timestamp : simulatedVsyncsFast2) {
         tracker.addVsyncTimestamp(timestamp);
     }
-    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
-    EXPECT_THAT(slope, Eq(fastPeriod2));
-    EXPECT_THAT(intercept, Eq(0));
-}
-
-TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
-    auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
-
-    for (auto const& timestamp : simulatedVsyncs) {
-        tracker.addVsyncTimestamp(timestamp);
-    }
-    auto const mNow = *simulatedVsyncs.rbegin();
-    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
-
-    // TODO: would be better to decay this as a result of the variance of the samples
-    static auto constexpr aLongTimeOut = 1000000000;
-    EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
+    model = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(model.slope, Eq(fastPeriod2));
+    EXPECT_THAT(model.intercept, Eq(0));
 }
 
 TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
@@ -389,11 +407,9 @@
         tracker.addVsyncTimestamp(i * realPeriod);
     }
 
-    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
-                IsCloseTo(realPeriod, mMaxRoundingError));
+    EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError));
     tracker.resetModel();
-    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
-                IsCloseTo(idealPeriod, mMaxRoundingError));
+    EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError));
 }
 
 TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
@@ -411,7 +427,7 @@
         // When VsyncPredictor returns the period it means that it doesn't know how to predict and
         // it needs to get more samples
         if (slope == mPeriod && intercept == 0) {
-            EXPECT_TRUE(tracker.needsMoreSamples(now));
+            EXPECT_TRUE(tracker.needsMoreSamples());
         }
     }
 }
@@ -432,6 +448,33 @@
     EXPECT_THAT(intercept, Eq(0));
 }
 
+TEST_F(VSyncPredictorTest, isVSyncInPhase) {
+    auto last = mNow;
+    auto const bias = 10;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod - bias;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+        mNow += bias;
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+
+    const auto maxDivider = 5;
+    const auto maxPeriods = 15;
+    for (int divider = 1; divider < maxDivider; divider++) {
+        for (int i = 0; i < maxPeriods; i++) {
+            const bool expectedInPhase = (i % divider) == 0;
+            EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+                    << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
+                    << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+        }
+    }
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ccbd17f..a7568e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,28 +41,11 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
-class VSyncTrackerWrapper : public VSyncTracker {
-public:
-    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
-
-    bool addVsyncTimestamp(nsecs_t timestamp) final {
-        return mTracker->addVsyncTimestamp(timestamp);
-    }
-    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
-        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
-    }
-    nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
-    void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
-    void resetModel() final { mTracker->resetModel(); }
-    void dump(std::string& result) const final { mTracker->dump(result); }
-
-private:
-    std::shared_ptr<VSyncTracker> const mTracker;
-};
-
 class MockClock : public Clock {
 public:
     MOCK_CONST_METHOD0(now, nsecs_t());
@@ -81,89 +64,46 @@
 class MockVSyncDispatch : public VSyncDispatch {
 public:
     MOCK_METHOD2(registerCallback,
-                 CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
+                 CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
     MOCK_METHOD1(unregisterCallback, void(CallbackToken));
-    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+    MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming));
     MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
-class VSyncDispatchWrapper : public VSyncDispatch {
-public:
-    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
-    CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
-                                   std::string callbackName) final {
-        return mDispatch->registerCallback(callbackFn, callbackName);
-    }
-
-    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
-
-    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
-                            nsecs_t earliestVsync) final {
-        return mDispatch->schedule(token, workDuration, earliestVsync);
-    }
-
-    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
-
-    void dump(std::string& result) const final { return mDispatch->dump(result); }
-
-private:
-    std::shared_ptr<VSyncDispatch> const mDispatch;
-};
-
-std::shared_ptr<FenceTime> generateInvalidFence() {
+std::shared_ptr<android::FenceTime> generateInvalidFence() {
     sp<Fence> fence = new Fence();
-    return std::make_shared<FenceTime>(fence);
+    return std::make_shared<android::FenceTime>(fence);
 }
 
-std::shared_ptr<FenceTime> generatePendingFence() {
+std::shared_ptr<android::FenceTime> generatePendingFence() {
     sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
-    return std::make_shared<FenceTime>(fence);
+    return std::make_shared<android::FenceTime>(fence);
 }
 
-void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
-    FenceTime::Snapshot snap(time);
+void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs_t time) {
+    android::FenceTime::Snapshot snap(time);
     fence->applyTrustedSnapshot(snap);
 }
 
-std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
     sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
-    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence);
     signalFenceWithTime(ft, time);
     return ft;
 }
 
-class StubCallback : public DispSync::Callback {
-public:
-    void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
-        std::lock_guard<std::mutex> lk(mMutex);
-        mLastCallTime = when;
-    }
-    std::optional<nsecs_t> lastCallTime() const {
-        std::lock_guard<std::mutex> lk(mMutex);
-        return mLastCallTime;
-    }
-
-private:
-    std::mutex mutable mMutex;
-    std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
-};
-
 class VSyncReactorTest : public testing::Test {
 protected:
     VSyncReactorTest()
-          : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
-            mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+          : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
             mMockClock(std::make_shared<NiceMock<MockClock>>()),
-            mReactor(std::make_unique<ClockWrapper>(mMockClock),
-                     std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
-                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
+            mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit,
                      false /* supportKernelIdleTimer */) {
         ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
         ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
     }
 
-    std::shared_ptr<MockVSyncDispatch> mMockDispatch;
     std::shared_ptr<MockVSyncTracker> mMockTracker;
     std::shared_ptr<MockClock> mMockClock;
     static constexpr size_t kPendingLimit = 3;
@@ -178,7 +118,7 @@
     VSyncDispatch::CallbackToken const mFakeToken{2398};
 
     nsecs_t lastCallbackTime = 0;
-    StubCallback outerCb;
+    // StubCallback outerCb;
     std::function<void(nsecs_t, nsecs_t)> innerCb;
 
     VSyncReactor mReactor;
@@ -213,7 +153,7 @@
 }
 
 TEST_F(VSyncReactorTest, limitsPendingFences) {
-    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<std::shared_ptr<android::FenceTime>, kPendingLimit * 2> fences;
     std::array<nsecs_t, fences.size()> fakeTimes;
     std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
     std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
@@ -254,86 +194,48 @@
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
-    nsecs_t const fakeTimestamp = 4839;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
-            .Times(1)
-            .WillOnce(Return(fakeTimestamp));
-
-    EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
-    nsecs_t const fakeTimestamp = 4839;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
-            .Times(1)
-            .WillOnce(Return(fakeTimestamp));
-
-    EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
-    nsecs_t const fakeTimestamp = 4839;
-    nsecs_t const fakePeriod = 1010;
-    nsecs_t const mFakeNow = 2214;
-    int const numPeriodsOut = 3;
-    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
-    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
-            .WillOnce(Return(fakeTimestamp));
-    EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, getPeriod) {
-    nsecs_t const fakePeriod = 1010;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
-}
-
 TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
     Mock::VerifyAndClearExpectations(mMockTracker.get());
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
 
-    EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
 TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
     nsecs_t sampleTime = 0;
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    mReactor.setPeriod(period);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    mReactor.startPeriodTransition(period);
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -342,16 +244,18 @@
     nsecs_t const secondPeriod = 5000;
     nsecs_t const thirdPeriod = 2000;
 
-    mReactor.setPeriod(secondPeriod);
+    mReactor.startPeriodTransition(secondPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    mReactor.setPeriod(thirdPeriod);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+    mReactor.startPeriodTransition(thirdPeriod);
+    EXPECT_TRUE(
+            mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(
+            mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -366,9 +270,10 @@
     nsecs_t skewyPeriod = period >> 1;
     bool periodFlushed = false;
     nsecs_t sampleTime = 0;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(
+            mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -386,22 +291,22 @@
 
 TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
 TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     Mock::VerifyAndClearExpectations(mMockTracker.get());
 
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
-    EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -410,7 +315,7 @@
     bool periodFlushed = false;
 
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
-    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -418,23 +323,23 @@
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
 
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     auto time = 0;
     auto constexpr numTimestampSubmissions = 10;
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += period;
-        EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 
     time += newPeriod;
-    EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += newPeriod;
-        EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 }
@@ -443,222 +348,108 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     time += period;
-    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 
     time += newPeriod;
-    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
 
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
-static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
-    return period - phase;
-}
-
-TEST_F(VSyncReactorTest, addEventListener) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
-            .Times(2)
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    ASSERT_TRUE(innerCb);
-    innerCb(mFakeVSyncTime, mFakeWakeupTime);
-    innerCb(mFakeVSyncTime, mFakeWakeupTime);
-}
-
-TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
-            .InSequence(seq)
-            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
-            .InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    ASSERT_TRUE(innerCb);
-    innerCb(mFakeVSyncTime, mFakeWakeupTime);
-    EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
-}
-
-TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-// b/149221293
-TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
-    class SelfRemovingCallback : public DispSync::Callback {
-    public:
-        SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
-        void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
-            mVsr.removeEventListener(this, &when);
-        }
-
-    private:
-        VSyncReactor& mVsr;
-    } selfRemover(mReactor);
-
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
-    innerCb(0, 0);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
-    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
-    static constexpr nsecs_t anotherPeriod = 23333;
-    Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
-            .InSequence(seq);
-    EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
-            .InSequence(seq);
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) {
+    auto time = 0;
     bool periodFlushed = false;
-    mReactor.setPeriod(anotherPeriod);
-    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
-    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
+    nsecs_t const newPeriod = 4000;
+    mReactor.startPeriodTransition(newPeriod);
 
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+    static auto constexpr numSamplesWithNewPeriod = 4;
     Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+    EXPECT_CALL(*mMockTracker, needsMoreSamples())
+            .Times(numSamplesWithNewPeriod - 2)
             .InSequence(seq)
-            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+            .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mMockTracker, needsMoreSamples())
+            .Times(1)
             .InSequence(seq)
-            .WillOnce(Return(ScheduleResult::Scheduled));
+            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
 
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
-            .InSequence(seq)
-            .WillOnce(Return(ScheduleResult::Scheduled));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
 
-    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
-            .InSequence(seq)
-            .WillOnce(Return(ScheduleResult::Scheduled));
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
-    ASSERT_TRUE(innerCb);
-    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
 }
 
-TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
-    nsecs_t const negativePhase = -4000;
+TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
+    auto time = 0;
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+    mReactor.startPeriodTransition(newPeriod);
+
     Sequence seq;
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+    EXPECT_CALL(*mMockTracker, needsMoreSamples())
+            .Times(1)
             .InSequence(seq)
-            .WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
-            .InSequence(seq);
-    mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
 }
 
-TEST_F(VSyncReactorTest, beginResyncResetsModel) {
-    EXPECT_CALL(*mMockTracker, resetModel());
-    mReactor.beginResync();
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
+    auto time = 0;
+    bool periodFlushed = false;
+    nsecs_t const newPeriod1 = 4000;
+    nsecs_t const newPeriod2 = 7000;
+
+    mReactor.startPeriodTransition(newPeriod1);
+
+    Sequence seq;
+    EXPECT_CALL(*mMockTracker, needsMoreSamples())
+            .Times(4)
+            .InSequence(seq)
+            .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mMockTracker, needsMoreSamples())
+            .Times(1)
+            .InSequence(seq)
+            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
+
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+
+    mReactor.startPeriodTransition(newPeriod2);
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
 }
 
 TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
     bool periodFlushed = true;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
@@ -666,62 +457,37 @@
 
 TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
     // Create a reactor which supports the kernel idle timer
-    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
-                                    std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
-                                    std::make_unique<VSyncTrackerWrapper>(mMockTracker),
+    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
                                     kPendingLimit, true /* supportKernelIdleTimer */);
 
     bool periodFlushed = true;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
     idleReactor.setIgnorePresentFences(true);
 
-    nsecs_t const newPeriod = 5000;
-    idleReactor.setPeriod(newPeriod);
-
-    EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+    // First, set the same period, which should only be confirmed when we receive two
+    // matching callbacks
+    idleReactor.startPeriodTransition(10000);
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(idleReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    // Correct period but incorrect timestamp delta
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    // Correct period and correct timestamp delta
+    EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    // Then, set a new period, which should be confirmed as soon as we receive a callback
+    // reporting the new period
+    nsecs_t const newPeriod = 5000;
+    idleReactor.startPeriodTransition(newPeriod);
+    // Incorrect timestamp delta and period
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    // Incorrect timestamp delta but correct period
+    EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
-using VSyncReactorDeathTest = VSyncReactorTest;
-TEST_F(VSyncReactorDeathTest, invalidRemoval) {
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
-    EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, invalidChange) {
-    EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
-
-    // the current DispSync-interface usage pattern has evolved around an implementation quirk,
-    // which is a callback is assumed to always exist, and it is valid api usage to change the
-    // offset of an object that is in the removed state.
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
-    mReactor.changePhaseOffset(&outerCb, mPhase);
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
-    ON_CALL(*mMockDispatch, schedule(_, _, _))
-            .WillByDefault(Return(ScheduleResult::CannotSchedule));
-    EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
-    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
-            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
-    EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
-
-    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    ASSERT_TRUE(innerCb);
-    Mock::VerifyAndClearExpectations(mMockDispatch.get());
-
-    ON_CALL(*mMockDispatch, schedule(_, _, _))
-            .WillByDefault(Return(ScheduleResult::CannotSchedule));
-    EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
-}
-
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
new file mode 100644
index 0000000..72ee6db
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class TestableWorkDuration : public impl::WorkDuration {
+public:
+    TestableWorkDuration(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+                         nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                         nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+          : impl::WorkDuration({60.0f, 90.0f}, currentFps, sfDuration, appDuration, sfEarlyDuration,
+                               appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
+};
+
+class WorkDurationTest : public testing::Test {
+protected:
+    WorkDurationTest()
+          : mWorkDuration(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+                          21'000'000) {}
+
+    ~WorkDurationTest() = default;
+
+    TestableWorkDuration mWorkDuration;
+};
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
+    mWorkDuration.setRefreshRateFps(60.0f);
+    auto currentOffsets = mWorkDuration.getCurrentConfigs();
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(60.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+    EXPECT_EQ(offsets.late.appOffset, 2'333'334);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 666'667);
+    EXPECT_EQ(offsets.early.appOffset, 833'334);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
+    mWorkDuration.setRefreshRateFps(90.0f);
+    auto currentOffsets = mWorkDuration.getCurrentConfigs();
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(90.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sfOffset, 611'111);
+    EXPECT_EQ(offsets.late.appOffset, 2'333'333);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, -4'888'889);
+    EXPECT_EQ(offsets.early.appOffset, 833'333);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
+    TestableWorkDuration phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+    auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
+        EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+        EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+        EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+        EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod);
+
+        EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+        EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+        EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+        EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod);
+
+        EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+        EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+        EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+        EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod);
+    };
+
+    const auto testForRefreshRate = [&](float refreshRate) {
+        phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate);
+        auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs();
+        auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate);
+        EXPECT_EQ(currentOffsets, offsets);
+        validateOffsets(offsets,
+                        std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / refreshRate)));
+    };
+
+    testForRefreshRate(90.0f);
+    testForRefreshRate(60.0f);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
+    EXPECT_EQ(offsets.late.appOffset, 37'027'208);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 52'027'208);
+    EXPECT_EQ(offsets.early.appOffset, 35'527'208);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+    TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                         std::optional<nsecs_t> earlySfOffsetNs,
+                         std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                         std::optional<nsecs_t> earlyAppOffsetNs,
+                         std::optional<nsecs_t> earlyGpuAppOffsetNs,
+                         nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+                         std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+                         std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+                         std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+                         std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+                         nsecs_t thresholdForNextVsync)
+          : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+                               earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+                               earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+                               highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
+                               highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
+                               highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest() = default;
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000,
+                                       4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000,
+                                       3'000'000, 4'000'000, 10'000'000};
+};
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+    EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+    EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60.0f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+    EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+    EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90.0f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 2'000'000);
+    EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
+    TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {},        2'000'000,
+                                      1'000'000, {},        {}, {}, {}, 10'000'000};
+    auto offsets = phaseOffsets.getConfigsForRefreshRate(60.0f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
+    TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {},        2'000'000,
+                                      1'000'000, {},        {}, {}, {}, 10'000'000};
+    auto offsets = phaseOffsets.getConfigsForRefreshRate(90.0f);
+
+    EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+    EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+    EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.early.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns);
+    EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+    EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+    EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000);
+
+    EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns);
+    EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
new file mode 100644
index 0000000..106da81
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VsyncModulator.h"
+
+namespace android::scheduler {
+
+class VsyncModulatorTest : public testing::Test {
+    enum {
+        SF_OFFSET_LATE,
+        APP_OFFSET_LATE,
+        SF_DURATION_LATE,
+        APP_DURATION_LATE,
+        SF_OFFSET_EARLY,
+        APP_OFFSET_EARLY,
+        SF_DURATION_EARLY,
+        APP_DURATION_EARLY,
+        SF_OFFSET_EARLY_GPU,
+        APP_OFFSET_EARLY_GPU,
+        SF_DURATION_EARLY_GPU,
+        APP_DURATION_EARLY_GPU,
+    };
+
+    static VsyncModulator::TimePoint Now() {
+        static VsyncModulator::TimePoint now;
+        return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME;
+    }
+
+protected:
+    static constexpr auto MIN_EARLY_TRANSACTION_FRAMES =
+            VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
+
+    using Schedule = scheduler::TransactionSchedule;
+    using nanos = std::chrono::nanoseconds;
+    const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
+                                             nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
+    const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+                                                nanos(SF_DURATION_EARLY),
+                                                nanos(APP_DURATION_EARLY)};
+    const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
+                                            nanos(SF_DURATION_EARLY_GPU),
+                                            nanos(APP_DURATION_EARLY_GPU)};
+
+    const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate};
+    VsyncModulator mVsyncModulator{mOffsets, Now};
+
+    void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
+};
+
+#define CHECK_COMMIT(result, configs)                         \
+    EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
+    EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
+
+#define CHECK_REFRESH(N, result, configs)                           \
+    for (int i = 0; i < N; i++) {                                   \
+        EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
+        EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());       \
+    }
+
+TEST_F(VsyncModulatorTest, Late) {
+    EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+
+    CHECK_COMMIT(std::nullopt, kLate);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyEnd) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStart) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+    CHECK_COMMIT(kEarly, kEarly);
+
+    for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+        EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+        CHECK_REFRESH(1, std::nullopt, kEarly);
+    }
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(1, kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(1, kEarly, kEarly);
+
+    for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+        EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+        CHECK_REFRESH(1, std::nullopt, kEarly);
+    }
+
+    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 0780af1..7de1872 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -18,19 +18,16 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#define LOG_TAG "MockComposer"
 #include "mock/DisplayHardware/MockComposer.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 Composer::Composer() = default;
 Composer::~Composer() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index c2c5072..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -24,8 +24,7 @@
 
 class GraphicBuffer;
 
-namespace Hwc2 {
-namespace mock {
+namespace Hwc2::mock {
 
 using android::hardware::graphics::common::V1_0::ColorTransform;
 using android::hardware::graphics::common::V1_0::Transform;
@@ -52,11 +51,9 @@
     MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
     MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
-    MOCK_METHOD0(isRemote, bool());
     MOCK_METHOD0(resetCommands, void());
     MOCK_METHOD0(executeCommands, Error());
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
-    MOCK_CONST_METHOD0(isUsingVrComposer, bool());
     MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
     MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
     MOCK_METHOD1(acceptDisplayChanges, Error(Display));
@@ -110,7 +107,6 @@
     MOCK_METHOD3(setLayerVisibleRegion,
                  Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
     MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t));
-    MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
     MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
     MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
     MOCK_METHOD4(getDisplayedContentSamplingAttributes,
@@ -143,6 +139,5 @@
     MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
 };
 
-} // namespace mock
-} // namespace Hwc2
+} // namespace Hwc2::mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index 2ec37c1..c9788af 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -16,14 +16,10 @@
 
 #include "mock/DisplayHardware/MockDisplay.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 Display::Display() = default;
 Display::~Display() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
\ No newline at end of file
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index fe99e77..a96d9db 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -22,9 +22,7 @@
 
 using android::HWC2::Layer;
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 namespace hal = android::hardware::graphics::composer::hal;
 
@@ -98,6 +96,4 @@
     MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
 };
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
index 8be7077..1ba38a8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
@@ -16,14 +16,10 @@
 
 #include "MockPowerAdvisor.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 PowerAdvisor::PowerAdvisor() = default;
 PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // 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 e22d0cf..7450b5d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -20,9 +20,7 @@
 
 #include "DisplayHardware/PowerAdvisor.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 class PowerAdvisor : public android::Hwc2::PowerAdvisor {
 public:
@@ -34,6 +32,4 @@
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
deleted file mode 100644
index 1c8c44d..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/MockDispSync.h"
-#include <thread>
-
-using namespace std::chrono_literals;
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-DispSync::DispSync() = default;
-DispSync::~DispSync() = default;
-
-status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback,
-                                    nsecs_t /*lastCallbackTime*/) {
-    if (mCallback.callback != nullptr) {
-        return BAD_VALUE;
-    }
-
-    mCallback = {callback, phase};
-    return NO_ERROR;
-}
-status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) {
-    if (mCallback.callback != callback) {
-        return BAD_VALUE;
-    }
-
-    mCallback = {nullptr, 0};
-    return NO_ERROR;
-}
-
-status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
-    if (mCallback.callback != callback) {
-        return BAD_VALUE;
-    }
-
-    mCallback.phase = phase;
-    return NO_ERROR;
-}
-
-void DispSync::triggerCallback() {
-    if (mCallback.callback == nullptr) return;
-
-    const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch();
-    const auto expectedVSyncTime = now + 16ms;
-    mCallback.callback->onDispSyncEvent(now.count(), expectedVSyncTime.count());
-}
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
deleted file mode 100644
index b39487c..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/DispSync.h"
-
-namespace android {
-namespace mock {
-
-class DispSync : public android::DispSync {
-public:
-    DispSync();
-    ~DispSync() override;
-
-    MOCK_METHOD0(reset, void());
-    MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD0(beginResync, void());
-    MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
-    MOCK_METHOD0(endResync, void());
-    MOCK_METHOD1(setPeriod, void(nsecs_t));
-    MOCK_METHOD0(getPeriod, nsecs_t());
-    MOCK_METHOD0(getIntendedPeriod, nsecs_t());
-    MOCK_METHOD1(setRefreshSkipCount, void(int));
-    MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t));
-    MOCK_METHOD1(setIgnorePresentFences, void(bool));
-    MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t));
-
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-
-    status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
-                              nsecs_t lastCallbackTime) override;
-    status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override;
-    status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
-
-    nsecs_t getCallbackPhase() { return mCallback.phase; }
-
-    void triggerCallback();
-
-private:
-    struct CallbackType {
-        Callback* callback = nullptr;
-        nsecs_t phase;
-    };
-    CallbackType mCallback;
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
new file mode 100644
index 0000000..cfc37ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayIdGenerator.h"
+
+namespace android::mock {
+
+template <typename T>
+class DisplayIdGenerator : public android::DisplayIdGenerator<T> {
+public:
+    // Explicit default instantiation is recommended.
+    DisplayIdGenerator() = default;
+    virtual ~DisplayIdGenerator() = default;
+
+    MOCK_METHOD0(nextId, std::optional<T>());
+    MOCK_METHOD1(markUnused, void(T));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
deleted file mode 100644
index 6ef352a..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/EventControlThread.h"
-
-namespace android {
-namespace mock {
-
-class EventControlThread : public android::EventControlThread {
-public:
-    EventControlThread();
-    ~EventControlThread() override;
-
-    MOCK_METHOD1(setVsyncEnabled, void(bool));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
index 408cd35..302dc01 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockEventThread.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 EventThread::EventThread() = default;
 EventThread::~EventThread() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 054aaf8..b4594c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -20,8 +20,7 @@
 
 #include "Scheduler/EventThread.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class EventThread : public android::EventThread {
 public:
@@ -35,7 +34,9 @@
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
     MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
     MOCK_CONST_METHOD1(dump, void(std::string&));
-    MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
+    MOCK_METHOD2(setDuration,
+                 void(std::chrono::nanoseconds workDuration,
+                      std::chrono::nanoseconds readyDuration));
     MOCK_METHOD1(registerDisplayEventConnection,
                  status_t(const sp<android::EventThreadConnection> &));
     MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
@@ -45,5 +46,4 @@
     MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index 358dfdb..417dcb0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockFrameTracer.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 FrameTracer::FrameTracer() = default;
 FrameTracer::~FrameTracer() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
index f768b81..305cb1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
@@ -20,8 +20,7 @@
 
 #include "FrameTracer/FrameTracer.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class FrameTracer : public android::FrameTracer {
 public:
@@ -39,5 +38,4 @@
     MOCK_METHOD0(miniDump, std::string());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index a82b583..efaa9fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,6 +18,7 @@
 
 #include <gmock/gmock.h>
 
+#include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
 
@@ -34,6 +35,10 @@
     MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
     MOCK_METHOD0(invalidate, void());
     MOCK_METHOD0(refresh, void());
+    MOCK_METHOD3(initVsync,
+                 void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                      std::chrono::nanoseconds));
+    MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
new file mode 100644
index 0000000..72bc89c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/Scheduler.h"
+
+namespace android::mock {
+
+struct SchedulerCallback final : ISchedulerCallback {
+    MOCK_METHOD1(setVsyncEnabled, void(bool));
+    MOCK_METHOD2(changeRefreshRate,
+                 void(const scheduler::RefreshRateConfigs::RefreshRate&,
+                      scheduler::RefreshRateConfigEvent));
+    MOCK_METHOD0(repaintEverythingForHWC, void());
+    MOCK_METHOD1(kernelTimerChanged, void(bool));
+};
+
+struct NoOpSchedulerCallback final : ISchedulerCallback {
+    void setVsyncEnabled(bool) override {}
+    void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+                           scheduler::RefreshRateConfigEvent) override {}
+    void repaintEverythingForHWC() override {}
+    void kernelTimerChanged(bool) override {}
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 7e925b9..0a0e7b5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -20,15 +20,13 @@
 
 #include "mock/MockSurfaceInterceptor.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 SurfaceInterceptor::SurfaceInterceptor() = default;
 SurfaceInterceptor::~SurfaceInterceptor() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 5beee1c..b085027 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -20,8 +20,7 @@
 
 #include "SurfaceInterceptor.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class SurfaceInterceptor : public android::SurfaceInterceptor {
 public:
@@ -33,10 +32,12 @@
                       const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
     MOCK_METHOD0(disable, void());
     MOCK_METHOD0(isEnabled, bool());
-    MOCK_METHOD4(saveTransaction,
+    MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
+    MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
+    MOCK_METHOD7(saveTransaction,
                  void(const Vector<ComposerState>&,
                       const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
-                      const Vector<DisplayState>&, uint32_t));
+                      const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
     MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
     MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
     MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
@@ -46,5 +47,4 @@
     MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
index d686939..f8e76b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockTimeStats.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 TimeStats::TimeStats() = default;
 TimeStats::~TimeStats() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 4186e2b..99ec353 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -20,8 +20,7 @@
 
 #include "TimeStats/TimeStats.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class TimeStats : public android::TimeStats {
 public:
@@ -42,7 +41,7 @@
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
     MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
     MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
@@ -51,6 +50,8 @@
     MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD1(incrementJankyFrames, void(int32_t));
+    MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
     MOCK_METHOD1(setPowerMode,
@@ -59,5 +60,4 @@
     MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 74%
rename from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index f9bacc8..bcccae5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/MockEventControlThread.h"
+#include "mock/MockVSyncTracker.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
new file mode 100644
index 0000000..de98025
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::mock {
+
+class VSyncTracker : public android::scheduler::VSyncTracker {
+public:
+    VSyncTracker();
+    ~VSyncTracker() override;
+
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
similarity index 73%
copy from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
index f9bacc8..25ae1bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/MockEventControlThread.h"
+#include "mock/MockVsyncController.h"
+#include <thread>
 
-namespace android {
-namespace mock {
+using namespace std::chrono_literals;
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+VsyncController::VsyncController() = default;
+VsyncController::~VsyncController() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
new file mode 100644
index 0000000..94d9966
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncController.h"
+
+namespace android::mock {
+
+class VsyncController : public android::scheduler::VsyncController {
+public:
+    VsyncController();
+    ~VsyncController() override;
+
+    MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
+    MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
+    MOCK_METHOD1(setIgnorePresentFences, void(bool));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 5480b00..a13f93b 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,8 +15,10 @@
  */
 #pragma once
 
+#include <gui/SyncScreenCaptureListener.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
+#include <functional>
 #include "TransactionUtils.h"
 
 namespace android {
@@ -27,51 +29,58 @@
 // individual pixel values for testing purposes.
 class ScreenCapture : public RefBase {
 public:
+    static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+                                   ScreenCaptureResults& captureResults) {
+        const auto sf = ComposerService::getComposerService();
+        SurfaceComposerClient::Transaction().apply(true);
+
+        captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        status_t status = sf->captureDisplay(captureArgs, captureListener);
+
+        if (status != NO_ERROR) {
+            return status;
+        }
+        captureResults = captureListener->waitForResults();
+        return captureResults.result;
+    }
+
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
         captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+        DisplayCaptureArgs args;
+        args.displayToken = displayToken;
+        captureDisplay(sc, args);
+    }
+
+    static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
+                               DisplayCaptureArgs& captureArgs) {
+        ScreenCaptureResults captureResults;
+        ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
+        *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+    }
+
+    static status_t captureLayers(LayerCaptureArgs& captureArgs,
+                                  ScreenCaptureResults& captureResults) {
         const auto sf = ComposerService::getComposerService();
         SurfaceComposerClient::Transaction().apply(true);
 
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
+        captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        status_t status = sf->captureLayers(captureArgs, captureListener);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        captureResults = captureListener->waitForResults();
+        return captureResults.result;
     }
 
-    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                              Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                                   Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayersExcluding(
-            std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR,
-                  sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
-                                    ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
-                                    1.0f, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
+        ScreenCaptureResults captureResults;
+        ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults));
+        *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
     }
 
     void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 8e1f943..5c5b18e 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <chrono>
 
 #include <android/native_window.h>
@@ -181,3 +184,6 @@
 };
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index b4342bd..fa742c5 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -16,7 +16,10 @@
     name: "libvibratorservice",
 
     srcs: [
+        "VibratorCallbackScheduler.cpp",
+        "VibratorHalController.cpp",
         "VibratorHalWrapper.cpp",
+        "VibratorManagerHalWrapper.cpp",
     ],
 
     aidl: {
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
new file mode 100644
index 0000000..b033adb
--- /dev/null
+++ b/services/vibratorservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libvibratorservice_test"
+    }
+  ]
+}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
new file mode 100644
index 0000000..f2870b0
--- /dev/null
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+bool DelayedCallback::isExpired() const {
+    return mExpiration <= std::chrono::steady_clock::now();
+}
+
+DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
+    return mExpiration;
+}
+
+void DelayedCallback::run() const {
+    mCallback();
+}
+
+bool DelayedCallback::operator<(const DelayedCallback& other) const {
+    return mExpiration < other.mExpiration;
+}
+
+bool DelayedCallback::operator>(const DelayedCallback& other) const {
+    return mExpiration > other.mExpiration;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+CallbackScheduler::~CallbackScheduler() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mFinished = true;
+    }
+    mCondition.notify_all();
+    if (mCallbackThread && mCallbackThread->joinable()) {
+        mCallbackThread->join();
+    }
+}
+
+void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (mCallbackThread == nullptr) {
+            mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
+        }
+        mQueue.emplace(DelayedCallback(callback, delay));
+    }
+    mCondition.notify_all();
+}
+
+void CallbackScheduler::loop() {
+    while (true) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        if (mFinished) {
+            // Destructor was called, so let the callback thread die.
+            break;
+        }
+        while (!mQueue.empty() && mQueue.top().isExpired()) {
+            DelayedCallback callback = mQueue.top();
+            mQueue.pop();
+            lock.unlock();
+            callback.run();
+            lock.lock();
+        }
+        if (mQueue.empty()) {
+            // Wait until a new callback is scheduled.
+            mCondition.wait(mMutex);
+        } else {
+            // Wait until next callback expires, or a new one is scheduled.
+            mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+        }
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
new file mode 100644
index 0000000..e8606ca
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalController"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+    static bool gHalExists = true;
+    if (!gHalExists) {
+        // We already tried to connect to all of the vibrator HAL versions and none was available.
+        return nullptr;
+    }
+
+    sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
+    if (aidlHal) {
+        ALOGV("Successfully connected to Vibrator HAL AIDL service.");
+        return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+    }
+
+    sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
+    if (halV1_0 == nullptr) {
+        ALOGV("Vibrator HAL service not available.");
+        gHalExists = false;
+        return nullptr;
+    }
+
+    sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
+    if (halV1_3) {
+        ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
+        return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
+    }
+    sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
+    if (halV1_2) {
+        ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
+        return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
+    }
+    sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
+    if (halV1_1) {
+        ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
+        return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
+    }
+    ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
+    return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static constexpr int MAX_RETRIES = 1;
+
+template <typename T>
+HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
+    if (result.isFailed()) {
+        ALOGE("%s failed: %s", functionName, result.errorMessage());
+        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+        mConnectedHal->tryReconnect();
+    }
+    return result;
+}
+
+template <typename T>
+HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
+    std::shared_ptr<HalWrapper> hal = nullptr;
+    {
+        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+        if (mConnectedHal == nullptr) {
+            // Init was never called, so connect to HAL for the first time during this call.
+            mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+
+            if (mConnectedHal == nullptr) {
+                ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+                return HalResult<T>::unsupported();
+            }
+        }
+        hal = mConnectedHal;
+    }
+
+    HalResult<T> ret = processHalResult(halFn(hal), functionName);
+    for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
+        ret = processHalResult(halFn(hal), functionName);
+    }
+
+    return ret;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool HalController::init() {
+    std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+    if (mConnectedHal == nullptr) {
+        mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+    }
+    return mConnectedHal != nullptr;
+}
+
+HalResult<void> HalController::ping() {
+    hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+    return apply(pingFn, "ping");
+}
+
+void HalController::tryReconnect() {
+    std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+    if (mConnectedHal == nullptr) {
+        mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+    } else {
+        mConnectedHal->tryReconnect();
+    }
+}
+
+HalResult<void> HalController::on(milliseconds timeout,
+                                  const std::function<void()>& completionCallback) {
+    hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->on(timeout, completionCallback);
+    };
+    return apply(onFn, "on");
+}
+
+HalResult<void> HalController::off() {
+    hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
+    return apply(offFn, "off");
+}
+
+HalResult<void> HalController::setAmplitude(int32_t amplitude) {
+    hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->setAmplitude(amplitude);
+    };
+    return apply(setAmplitudeFn, "setAmplitude");
+}
+
+HalResult<void> HalController::setExternalControl(bool enabled) {
+    hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->setExternalControl(enabled);
+    };
+    return apply(setExternalControlFn, "setExternalControl");
+}
+
+HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+    hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->alwaysOnEnable(id, effect, strength);
+    };
+    return apply(alwaysOnEnableFn, "alwaysOnEnable");
+}
+
+HalResult<void> HalController::alwaysOnDisable(int32_t id) {
+    hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->alwaysOnDisable(id);
+    };
+    return apply(alwaysOnDisableFn, "alwaysOnDisable");
+}
+
+HalResult<Capabilities> HalController::getCapabilities() {
+    hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
+        return hal->getCapabilities();
+    };
+    return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
+    hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
+        return hal->getSupportedEffects();
+    };
+    return apply(getSupportedEffectsFn, "getSupportedEffects");
+}
+
+HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
+    hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
+            [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
+    return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
+}
+
+HalResult<milliseconds> HalController::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+    hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->performEffect(effect, strength, completionCallback);
+    };
+    return apply(performEffectFn, "performEffect");
+}
+
+HalResult<void> HalController::performComposedEffect(
+        const std::vector<CompositeEffect>& primitiveEffects,
+        const std::function<void()>& completionCallback) {
+    hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+        return hal->performComposedEffect(primitiveEffects, completionCallback);
+    };
+    return apply(performComposedEffectFn, "performComposedEffect");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index db27bd9..9672644 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -23,9 +23,11 @@
 
 #include <utils/Log.h>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 
@@ -46,10 +48,12 @@
 template <class T>
 HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
     if (cache.has_value()) {
-        return HalResult<T>::ok(cache.value());
+        // Return copy of cached value.
+        return HalResult<T>::ok(*cache);
     }
     HalResult<T> ret = loadFn();
     if (ret.isOk()) {
+        // Cache copy of returned value.
         cache.emplace(ret.value());
     }
     return ret;
@@ -62,43 +66,11 @@
     return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
 }
 
-template <class I, class T>
-using perform_fn = hardware::Return<void> (I::*)(T, V1_0::EffectStrength,
-                                                 V1_0::IVibrator::perform_cb);
-
-template <class I, class T>
-HalResult<milliseconds> perform(perform_fn<I, T> performFn, sp<I> handle, T effect,
-                                EffectStrength strength) {
-    V1_0::Status status;
-    int32_t lengthMs;
-    V1_0::IVibrator::perform_cb effectCallback = [&status, &lengthMs](V1_0::Status retStatus,
-                                                                      uint32_t retLengthMs) {
-        status = retStatus;
-        lengthMs = retLengthMs;
-    };
-
-    V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
-    auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
-
-    return HalResult<milliseconds>::fromReturn(result, status, milliseconds(lengthMs));
-}
-
 // -------------------------------------------------------------------------------------------------
 
-template <typename T>
-HalResult<T> HalResult<T>::ok(T value) {
-    return HalResult(value);
-}
-
-template <typename T>
-HalResult<T> HalResult<T>::failed() {
-    return HalResult(/* unsupported= */ false);
-}
-
-template <typename T>
-HalResult<T> HalResult<T>::unsupported() {
-    return HalResult(/* unsupported= */ true);
-}
+const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = ";
+const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX =
+        "android::hardware::vibrator::V1_0::Status = ";
 
 template <typename T>
 HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
@@ -108,7 +80,7 @@
     if (status.isOk()) {
         return HalResult<T>::ok(data);
     }
-    return HalResult<T>::failed();
+    return HalResult<T>::failed(std::string(status.toString8().c_str()));
 }
 
 template <typename T>
@@ -119,34 +91,30 @@
         case V1_0::Status::UNSUPPORTED_OPERATION:
             return HalResult<T>::unsupported();
         default:
-            return HalResult<T>::failed();
+            return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
     }
 }
 
 template <typename T>
 template <typename R>
 HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
-    return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed();
+    return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
 }
 
 template <typename T>
 template <typename R>
 HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
-    return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed();
+    return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+                      : HalResult<T>::failed(ret.description());
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> HalResult<void>::ok() {
-    return HalResult();
-}
-
-HalResult<void> HalResult<void>::failed() {
-    return HalResult(/* failed= */ true);
-}
-
-HalResult<void> HalResult<void>::unsupported() {
-    return HalResult(/* failed= */ false, /* unsupported= */ true);
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+    if (status == android::OK) {
+        return HalResult<void>::ok();
+    }
+    return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status));
 }
 
 HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
@@ -156,7 +124,7 @@
     if (status.isOk()) {
         return HalResult<void>::ok();
     }
-    return HalResult<void>::failed();
+    return HalResult<void>::failed(std::string(status.toString8().c_str()));
 }
 
 HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
@@ -166,20 +134,20 @@
         case V1_0::Status::UNSUPPORTED_OPERATION:
             return HalResult<void>::unsupported();
         default:
-            return HalResult<void>::failed();
+            return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
     }
 }
 
 template <typename R>
 HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
-    return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed();
+    return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
 }
 
 // -------------------------------------------------------------------------------------------------
 
 class HalCallbackWrapper : public Aidl::BnVibratorCallback {
 public:
-    HalCallbackWrapper(const std::function<void()>& completionCallback)
+    HalCallbackWrapper(std::function<void()> completionCallback)
           : mCompletionCallback(completionCallback) {}
 
     binder::Status onComplete() override {
@@ -194,157 +162,252 @@
 // -------------------------------------------------------------------------------------------------
 
 HalResult<void> AidlHalWrapper::ping() {
-    return IInterface::asBinder(mHandle)->pingBinder() ? HalResult<void>::ok()
-                                                       : HalResult<void>::failed();
+    return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+}
+
+void AidlHalWrapper::tryReconnect() {
+    sp<Aidl::IVibrator> newHandle = checkVintfService<Aidl::IVibrator>();
+    if (newHandle) {
+        std::lock_guard<std::mutex> lock(mHandleMutex);
+        mHandle = std::move(newHandle);
+    }
 }
 
 HalResult<void> AidlHalWrapper::on(milliseconds timeout,
                                    const std::function<void()>& completionCallback) {
-    auto cb = new HalCallbackWrapper(completionCallback);
-    return HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
+    HalResult<Capabilities> capabilities = getCapabilities();
+    bool supportsCallback = capabilities.isOk() &&
+            static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
+    auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+    auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
+    if (!supportsCallback && ret.isOk()) {
+        mCallbackScheduler->schedule(completionCallback, timeout);
+    }
+
+    return ret;
 }
 
 HalResult<void> AidlHalWrapper::off() {
-    return HalResult<void>::fromStatus(mHandle->off());
+    return HalResult<void>::fromStatus(getHal()->off());
 }
 
 HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
     float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
-    return HalResult<void>::fromStatus(mHandle->setAmplitude(convertedAmplitude));
+    return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
 }
 
 HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
-    return HalResult<void>::fromStatus(mHandle->setExternalControl(enabled));
+    return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
 }
 
 HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
-    return HalResult<void>::fromStatus(mHandle->alwaysOnEnable(id, effect, strength));
+    return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
 }
 
 HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
-    return HalResult<void>::fromStatus(mHandle->alwaysOnDisable(id));
+    return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
     std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    static auto loadFn = [this]() {
-        int32_t capabilities = 0;
-        auto result = mHandle->getCapabilities(&capabilities);
-        return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
-    };
-    return loadCached<Capabilities>(loadFn, mCapabilities);
+    return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
+                                    mCapabilities);
 }
 
 HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
     std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
-    static auto loadFn = [this]() {
-        std::vector<Effect> supportedEffects;
-        auto result = mHandle->getSupportedEffects(&supportedEffects);
-        return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
-    };
-    return loadCached<std::vector<Effect>>(loadFn, mSupportedEffects);
+    return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
+                                                     this),
+                                           mSupportedEffects);
+}
+
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
+    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
+    return loadCached<std::vector<
+            CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
+                                 mSupportedPrimitives);
 }
 
 HalResult<milliseconds> AidlHalWrapper::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+    HalResult<Capabilities> capabilities = getCapabilities();
+    bool supportsCallback = capabilities.isOk() &&
+            static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
+    auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
     int32_t lengthMs;
-    auto cb = new HalCallbackWrapper(completionCallback);
-    auto result = mHandle->perform(effect, strength, cb, &lengthMs);
-    return HalResult<milliseconds>::fromStatus(result, milliseconds(lengthMs));
+    auto result = getHal()->perform(effect, strength, cb, &lengthMs);
+    milliseconds length = milliseconds(lengthMs);
+
+    auto ret = HalResult<milliseconds>::fromStatus(result, length);
+    if (!supportsCallback && ret.isOk()) {
+        mCallbackScheduler->schedule(completionCallback, length);
+    }
+
+    return ret;
 }
 
 HalResult<void> AidlHalWrapper::performComposedEffect(
         const std::vector<CompositeEffect>& primitiveEffects,
         const std::function<void()>& completionCallback) {
+    // This method should always support callbacks, so no need to double check.
     auto cb = new HalCallbackWrapper(completionCallback);
-    return HalResult<void>::fromStatus(mHandle->compose(primitiveEffects, cb));
+    return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
+    int32_t capabilities = 0;
+    auto result = getHal()->getCapabilities(&capabilities);
+    return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
+    std::vector<Effect> supportedEffects;
+    auto result = getHal()->getSupportedEffects(&supportedEffects);
+    return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+}
+
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
+    std::vector<CompositePrimitive> supportedPrimitives;
+    auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
+    return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
+}
+
+sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+    std::lock_guard<std::mutex> lock(mHandleMutex);
+    return mHandle;
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> HidlHalWrapperV1_0::ping() {
-    auto result = mHandleV1_0->ping();
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+    auto result = getHal()->ping();
     return HalResult<void>::fromReturn(result);
 }
 
-HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout, const std::function<void()>&) {
-    auto result = mHandleV1_0->on(timeout.count());
-    auto status = result.withDefault(V1_0::Status::UNKNOWN_ERROR);
-    return HalResult<void>::fromStatus(status);
+template <typename I>
+void HidlHalWrapper<I>::tryReconnect() {
+    sp<I> newHandle = I::tryGetService();
+    if (newHandle) {
+        std::lock_guard<std::mutex> lock(mHandleMutex);
+        mHandle = std::move(newHandle);
+    }
 }
 
-HalResult<void> HidlHalWrapperV1_0::off() {
-    auto result = mHandleV1_0->off();
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
+                                      const std::function<void()>& completionCallback) {
+    auto result = getHal()->on(timeout.count());
+    auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+    if (ret.isOk()) {
+        mCallbackScheduler->schedule(completionCallback, timeout);
+    }
+    return ret;
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::off() {
+    auto result = getHal()->off();
     return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
-HalResult<void> HidlHalWrapperV1_0::setAmplitude(int32_t amplitude) {
-    auto result = mHandleV1_0->setAmplitude(static_cast<uint8_t>(amplitude));
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
+    auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
     return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
-HalResult<void> HidlHalWrapperV1_0::setExternalControl(bool) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
     ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
     return HalResult<void>::unsupported();
 }
 
-HalResult<void> HidlHalWrapperV1_0::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
     ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
     return HalResult<void>::unsupported();
 }
 
-HalResult<void> HidlHalWrapperV1_0::alwaysOnDisable(int32_t) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
     ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
     return HalResult<void>::unsupported();
 }
 
-HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() {
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
     std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this),
+    return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
                                     mCapabilities);
 }
 
-HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
+template <typename I>
+HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
     ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
     return HalResult<std::vector<Effect>>::unsupported();
 }
 
-HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(Effect effect, EffectStrength strength,
-                                                          const std::function<void()>&) {
-    if (isStaticCastValid<V1_0::Effect>(effect)) {
-        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
-        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
-    }
-
-    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
-          Aidl::toString(effect).c_str());
-    return HalResult<milliseconds>::unsupported();
+template <typename I>
+HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
+    ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
+    return HalResult<std::vector<CompositePrimitive>>::unsupported();
 }
 
-HalResult<void> HidlHalWrapperV1_0::performComposedEffect(const std::vector<CompositeEffect>&,
-                                                          const std::function<void()>&) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
+                                                         const std::function<void()>&) {
     ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
     return HalResult<void>::unsupported();
 }
 
-HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
-    hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
+    hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
     Capabilities capabilities =
             result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
     return HalResult<Capabilities>::fromReturn(result, capabilities);
 }
 
+template <typename I>
+template <typename T>
+HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
+        perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
+        const std::function<void()>& completionCallback) {
+    V1_0::Status status;
+    int32_t lengthMs;
+    auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+        status = retStatus;
+        lengthMs = retLengthMs;
+    };
+
+    V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
+    auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
+    milliseconds length = milliseconds(lengthMs);
+
+    auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+    if (ret.isOk()) {
+        mCallbackScheduler->schedule(completionCallback, length);
+    }
+
+    return ret;
+}
+
+template <typename I>
+sp<I> HidlHalWrapper<I>::getHal() {
+    std::lock_guard<std::mutex> lock(mHandleMutex);
+    return mHandle;
+}
+
 // -------------------------------------------------------------------------------------------------
 
-HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(Effect effect, EffectStrength strength,
-                                                          const std::function<void()>&) {
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     if (isStaticCastValid<V1_0::Effect>(effect)) {
-        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
-        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
-    }
-    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
-        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
+        return performInternal(&V1_0::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
     }
 
     ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -354,19 +417,37 @@
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(Effect effect, EffectStrength strength,
-                                                          const std::function<void()>&) {
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     if (isStaticCastValid<V1_0::Effect>(effect)) {
-        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
-        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
+        return performInternal(&V1_1::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
     }
     if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
-        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
+        return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
+                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+    }
+
+    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+          Aidl::toString(effect).c_str());
+    return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+    if (isStaticCastValid<V1_0::Effect>(effect)) {
+        return performInternal(&V1_2::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
+    }
+    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+        return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
+                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
     }
     if (isStaticCastValid<V1_2::Effect>(effect)) {
-        V1_2::Effect e = static_cast<V1_2::Effect>(effect);
-        return perform(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength);
+        return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
+                               static_cast<V1_2::Effect>(effect), strength, completionCallback);
     }
 
     ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -377,27 +458,27 @@
 // -------------------------------------------------------------------------------------------------
 
 HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
-    auto result = mHandleV1_3->setExternalControl(static_cast<uint32_t>(enabled));
+    auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
     return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
-HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(Effect effect, EffectStrength strength,
-                                                          const std::function<void()>&) {
+HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     if (isStaticCastValid<V1_0::Effect>(effect)) {
-        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
-        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
+        return performInternal(&V1_3::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
     }
     if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
-        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
-        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
+        return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
+                               static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
     }
     if (isStaticCastValid<V1_2::Effect>(effect)) {
-        V1_2::Effect e = static_cast<V1_2::Effect>(effect);
-        return perform(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength);
+        return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+                               static_cast<V1_2::Effect>(effect), strength, completionCallback);
     }
     if (isStaticCastValid<V1_3::Effect>(effect)) {
-        V1_3::Effect e = static_cast<V1_3::Effect>(effect);
-        return perform(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength);
+        return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
+                               static_cast<V1_3::Effect>(effect), strength, completionCallback);
     }
 
     ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -406,16 +487,27 @@
 }
 
 HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
-    HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal();
-    if (!parentResult.isOk()) {
-        // Loading for previous HAL versions already failed, so propagate failure.
-        return parentResult;
+    Capabilities capabilities = Capabilities::NONE;
+
+    sp<V1_3::IVibrator> hal = getHal();
+    auto amplitudeResult = hal->supportsAmplitudeControl();
+    if (!amplitudeResult.isOk()) {
+        return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities);
     }
 
-    Capabilities capabilities = parentResult.value();
-    auto result = mHandleV1_3->supportsExternalControl();
-    capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
-    return HalResult<Capabilities>::fromReturn(result, capabilities);
+    auto externalControlResult = hal->supportsExternalControl();
+    if (amplitudeResult.withDefault(false)) {
+        capabilities |= Capabilities::AMPLITUDE_CONTROL;
+    }
+    if (externalControlResult.withDefault(false)) {
+        capabilities |= Capabilities::EXTERNAL_CONTROL;
+
+        if (amplitudeResult.withDefault(false)) {
+            capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+        }
+    }
+
+    return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
 }
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
new file mode 100644
index 0000000..71955af
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapper"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+constexpr int32_t SINGLE_VIBRATOR_ID = 0;
+
+HalResult<void> LegacyManagerHalWrapper::ping() {
+    return mController->ping();
+}
+
+void LegacyManagerHalWrapper::tryReconnect() {
+    mController->tryReconnect();
+}
+
+HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() {
+    if (mController->init()) {
+        return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID));
+    }
+    // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+    return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>());
+}
+
+HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) {
+    if (id == SINGLE_VIBRATOR_ID && mController->init()) {
+        return HalResult<std::shared_ptr<HalController>>::ok(mController);
+    }
+    // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+    return HalResult<std::shared_ptr<HalController>>::failed("No vibrator with id = " +
+                                                             std::to_string(id));
+}
+
+HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+    return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+    return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
+    return HalResult<void>::unsupported();
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
new file mode 100644
index 0000000..c1a03a1
--- /dev/null
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_benchmark {
+    name: "libvibratorservice_benchmarks",
+    srcs: [
+        "VibratorHalControllerBenchmarks.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "libvibratorservice",
+        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..0acff06
--- /dev/null
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <benchmark/benchmark.h>
+#include <vibratorservice/VibratorHalController.h>
+
+using ::android::enum_range;
+using ::benchmark::Counter;
+using ::benchmark::Fixture;
+using ::benchmark::kMicrosecond;
+using ::benchmark::State;
+using ::benchmark::internal::Benchmark;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+class VibratorBench : public Fixture {
+public:
+    void SetUp(State& /*state*/) override { mController.init(); }
+
+    void TearDown(State& /*state*/) override { mController.off(); }
+
+    static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
+
+    static void DefaultArgs(Benchmark* /*b*/) {
+        // none
+    }
+
+protected:
+    vibrator::HalController mController;
+
+    auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
+
+    bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result,
+                         vibrator::Capabilities&& query, State& state) {
+        if (result.isFailed()) {
+            state.SkipWithError(result.errorMessage());
+            return false;
+        }
+        if (!result.isOk()) {
+            return false;
+        }
+        return (result.value() & query) == query;
+    }
+
+    template <class R>
+    bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
+        if (result.isFailed()) {
+            state.SkipWithError(result.errorMessage());
+            return false;
+        }
+        return true;
+    }
+};
+
+#define BENCHMARK_WRAPPER(fixt, test, code)                \
+    BENCHMARK_DEFINE_F(fixt, test)                         \
+    /* NOLINTNEXTLINE */                                   \
+    (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
+            ->Apply(fixt::DefaultConfig)                   \
+            ->Apply(fixt::DefaultArgs)
+
+BENCHMARK_WRAPPER(VibratorBench, init, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        state.ResumeTiming();
+        controller.init();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, initCached, {
+    for (auto _ : state) {
+        mController.init();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, ping, {
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.ping();
+        state.PauseTiming();
+        checkHalResult(ret, state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
+    for (auto _ : state) {
+        mController.tryReconnect();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, on, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.on(duration, callback);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(mController.off(), state);
+        }
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, off, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        if (!checkHalResult(mController.on(duration, callback), state)) {
+            continue;
+        }
+        state.ResumeTiming();
+        checkHalResult(mController.off(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+        return;
+    }
+
+    auto duration = 60s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        if (!checkHalResult(controller.on(duration, callback), state)) {
+            continue;
+        }
+        state.ResumeTiming();
+        auto ret = controller.setAmplitude(amplitude);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(controller.off(), state);
+        }
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+        return;
+    }
+
+    auto duration = 6000s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    checkHalResult(mController.on(duration, callback), state);
+
+    for (auto _ : state) {
+        checkHalResult(mController.setAmplitude(amplitude), state);
+    }
+
+    checkHalResult(mController.off(), state);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        auto ret = controller.setExternalControl(true);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(controller.setExternalControl(false), state);
+        }
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.setExternalControl(true);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(mController.setExternalControl(false), state);
+        }
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
+        return;
+    }
+
+    auto amplitude = UINT8_MAX;
+
+    checkHalResult(mController.setExternalControl(true), state);
+
+    for (auto _ : state) {
+        checkHalResult(mController.setAmplitude(amplitude), state);
+    }
+
+    checkHalResult(mController.setExternalControl(false), state);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        checkHalResult(controller.getCapabilities(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+    // First call to cache values.
+    checkHalResult(mController.getCapabilities(), state);
+
+    for (auto _ : state) {
+        checkHalResult(mController.getCapabilities(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        checkHalResult(controller.getSupportedEffects(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
+    // First call to cache values.
+    checkHalResult(mController.getSupportedEffects(), state);
+
+    for (auto _ : state) {
+        checkHalResult(mController.getSupportedEffects(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        checkHalResult(controller.getSupportedPrimitives(), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
+    // First call to cache values.
+    checkHalResult(mController.getSupportedPrimitives(), state);
+
+    for (auto _ : state) {
+        checkHalResult(mController.getSupportedPrimitives(), state);
+    }
+});
+
+class VibratorEffectsBench : public VibratorBench {
+public:
+    static void DefaultArgs(Benchmark* b) {
+        vibrator::HalController controller;
+        auto effectsResult = controller.getSupportedEffects();
+        if (!effectsResult.isOk()) {
+            return;
+        }
+
+        std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+        b->ArgNames({"Effect", "Strength"});
+
+        if (supported.empty()) {
+            b->Args({static_cast<long>(-1), static_cast<long>(-1)});
+            return;
+        }
+
+        for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+            if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+                continue;
+            }
+            for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+                b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+            }
+        }
+    }
+
+protected:
+    bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
+    auto getEffect(const State& state) const {
+        return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+    }
+
+    auto getStrength(const State& state) const {
+        return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+        return;
+    }
+    if (!hasArgs(state)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.alwaysOnEnable(id, effect, strength);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(mController.alwaysOnDisable(id), state);
+        }
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+        return;
+    }
+    if (!hasArgs(state)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) {
+            continue;
+        }
+        state.ResumeTiming();
+        checkHalResult(mController.alwaysOnDisable(id), state);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+    if (!hasArgs(state)) {
+        return;
+    }
+
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.performEffect(effect, strength, callback);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(mController.off(), state);
+        }
+    }
+});
+
+class VibratorPrimitivesBench : public VibratorBench {
+public:
+    static void DefaultArgs(Benchmark* b) {
+        vibrator::HalController controller;
+        auto primitivesResult = controller.getSupportedPrimitives();
+        if (!primitivesResult.isOk()) {
+            return;
+        }
+
+        std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+        b->ArgNames({"Primitive"});
+
+        if (supported.empty()) {
+            b->Args({static_cast<long>(-1)});
+            return;
+        }
+
+        for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+            if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+                continue;
+            }
+            if (primitive == hardware::vibrator::CompositePrimitive::NOOP) {
+                continue;
+            }
+            b->Args({static_cast<long>(primitive)});
+        }
+    }
+
+protected:
+    bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
+    auto getPrimitive(const State& state) const {
+        return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
+    auto result = mController.getCapabilities();
+
+    if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+        return;
+    }
+    if (!hasArgs(state)) {
+        return;
+    }
+
+    hardware::vibrator::CompositeEffect effect;
+    effect.primitive = getPrimitive(state);
+    effect.scale = 1.0f;
+    effect.delayMs = static_cast<int32_t>(0);
+
+    std::vector<hardware::vibrator::CompositeEffect> effects;
+    effects.push_back(effect);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        auto ret = mController.performComposedEffect(effects, callback);
+        state.PauseTiming();
+        if (checkHalResult(ret, state)) {
+            checkHalResult(mController.off(), state);
+        }
+    }
+});
+
+BENCHMARK_MAIN();
\ No newline at end of file
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
new file mode 100644
index 0000000..2c194b5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <condition_variable>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for a callback to be executed after a delay.
+class DelayedCallback {
+public:
+    using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
+
+    DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
+          : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
+    ~DelayedCallback() = default;
+
+    void run() const;
+    bool isExpired() const;
+    Timestamp getExpiration() const;
+
+    // Compare by expiration time, where A < B when A expires first.
+    bool operator<(const DelayedCallback& other) const;
+    bool operator>(const DelayedCallback& other) const;
+
+private:
+    std::function<void()> mCallback;
+    Timestamp mExpiration;
+};
+
+// Schedules callbacks to be executed after a delay.
+class CallbackScheduler {
+public:
+    CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
+    virtual ~CallbackScheduler();
+
+    virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);
+
+private:
+    std::condition_variable_any mCondition;
+    std::mutex mMutex;
+
+    // Lazily instantiated only at the first time this scheduler is used.
+    std::unique_ptr<std::thread> mCallbackThread;
+
+    // Used to quit the callback thread when this instance is being destroyed.
+    bool mFinished GUARDED_BY(mMutex);
+
+    // Priority queue with reverse comparator, so tasks that expire first will be on top.
+    std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
+                        std::greater<DelayedCallback>>
+            mQueue GUARDED_BY(mMutex);
+
+    void loop();
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
new file mode 100644
index 0000000..d1028a4
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
+#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Handles the connection to he underlying HAL implementation available.
+class HalConnector {
+public:
+    HalConnector() = default;
+    virtual ~HalConnector() = default;
+
+    virtual std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler> scheduler);
+};
+
+// Controller for Vibrator HAL handle.
+// This relies on HalConnector to connect to the underlying Vibrator HAL service and reconnects to
+// it after each failed api call. This also ensures connecting to the service is thread-safe.
+class HalController : public HalWrapper {
+public:
+    HalController()
+          : HalController(std::make_unique<HalConnector>(), std::make_shared<CallbackScheduler>()) {
+    }
+    HalController(std::unique_ptr<HalConnector> halConnector,
+                  std::shared_ptr<CallbackScheduler> callbackScheduler)
+          : HalWrapper(std::move(callbackScheduler)),
+            mHalConnector(std::move(halConnector)),
+            mConnectedHal(nullptr) {}
+    virtual ~HalController() = default;
+
+    /* Connects to the newest HAL version available, possibly waiting for the registered service to
+     * become available. This will automatically be called at the first API usage if it was not
+     * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+     * the first API call later on. Returns true if any HAL version is available, false otherwise.
+     */
+    virtual bool init();
+
+    /* reloads HAL service instance without waiting. This relies on the HAL version found by init()
+     * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+     */
+    virtual void tryReconnect() override;
+
+    virtual HalResult<void> ping() override;
+    HalResult<void> on(std::chrono::milliseconds timeout,
+                       const std::function<void()>& completionCallback) final override;
+    HalResult<void> off() final override;
+
+    HalResult<void> setAmplitude(int32_t amplitude) final override;
+    HalResult<void> setExternalControl(bool enabled) final override;
+
+    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+                                   hardware::vibrator::EffectStrength strength) final override;
+    HalResult<void> alwaysOnDisable(int32_t id) final override;
+
+    HalResult<Capabilities> getCapabilities() final override;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+            final override;
+
+    HalResult<std::chrono::milliseconds> performEffect(
+            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+            const std::function<void()>& completionCallback) final override;
+
+    HalResult<void> performComposedEffect(
+            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+            const std::function<void()>& completionCallback) final override;
+
+private:
+    std::unique_ptr<HalConnector> mHalConnector;
+    std::mutex mConnectedHalMutex;
+    // Shared pointer to allow local copies to be used by different threads.
+    std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+
+    template <typename T>
+    HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+
+    template <typename T>
+    using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+
+    template <typename T>
+    HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 1a1f64b..bcb735d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -20,6 +20,9 @@
 #include <android-base/thread_annotations.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
 
 namespace android {
 
@@ -31,9 +34,11 @@
 template <typename T>
 class HalResult {
 public:
-    static HalResult<T> ok(T value);
-    static HalResult<T> failed();
-    static HalResult<T> unsupported();
+    static HalResult<T> ok(T value) { return HalResult(value); }
+    static HalResult<T> failed(std::string msg) {
+        return HalResult(std::move(msg), /* unsupported= */ false);
+    }
+    static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
 
     static HalResult<T> fromStatus(binder::Status status, T data);
     static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
@@ -46,27 +51,32 @@
                                    hardware::vibrator::V1_0::Status status, T data);
 
     // This will throw std::bad_optional_access if this result is not ok.
-    T value() const { return mValue.value(); }
+    const T& value() const { return mValue.value(); }
     bool isOk() const { return !mUnsupported && mValue.has_value(); }
     bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
     bool isUnsupported() const { return mUnsupported; }
+    const char* errorMessage() const { return mErrorMessage.c_str(); }
 
 private:
     std::optional<T> mValue;
+    std::string mErrorMessage;
     bool mUnsupported;
 
-    explicit HalResult(T value) : mValue(std::make_optional(value)), mUnsupported(false) {}
-    explicit HalResult(bool unsupported) : mValue(), mUnsupported(unsupported) {}
+    explicit HalResult(T value)
+          : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+    explicit HalResult(std::string errorMessage, bool unsupported)
+          : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
 };
 
 // Empty result of a call to the Vibrator HAL wrapper.
 template <>
 class HalResult<void> {
 public:
-    static HalResult<void> ok();
-    static HalResult<void> failed();
-    static HalResult<void> unsupported();
+    static HalResult<void> ok() { return HalResult(); }
+    static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+    static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
 
+    static HalResult<void> fromStatus(status_t status);
     static HalResult<void> fromStatus(binder::Status status);
     static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
 
@@ -76,13 +86,17 @@
     bool isOk() const { return !mUnsupported && !mFailed; }
     bool isFailed() const { return !mUnsupported && mFailed; }
     bool isUnsupported() const { return mUnsupported; }
+    const char* errorMessage() const { return mErrorMessage.c_str(); }
 
 private:
+    std::string mErrorMessage;
     bool mFailed;
     bool mUnsupported;
 
-    explicit HalResult(bool failed = false, bool unsupported = false)
-          : mFailed(failed), mUnsupported(unsupported) {}
+    explicit HalResult(bool unsupported = false)
+          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+    explicit HalResult(std::string errorMessage)
+          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -122,10 +136,16 @@
 // Wrapper for Vibrator HAL handlers.
 class HalWrapper {
 public:
+    explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
+          : mCallbackScheduler(std::move(scheduler)) {}
     virtual ~HalWrapper() = default;
 
-    virtual HalResult<void> ping() = 0;
+    /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+     * service restarts, to rapidly retry after a failure.
+     */
+    virtual void tryReconnect() = 0;
 
+    virtual HalResult<void> ping() = 0;
     virtual HalResult<void> on(std::chrono::milliseconds timeout,
                                const std::function<void()>& completionCallback) = 0;
     virtual HalResult<void> off() = 0;
@@ -139,6 +159,8 @@
 
     virtual HalResult<Capabilities> getCapabilities() = 0;
     virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
+    virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+    getSupportedPrimitives() = 0;
 
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
@@ -147,133 +169,179 @@
     virtual HalResult<void> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) = 0;
+
+protected:
+    // Shared pointer to allow CallbackScheduler to outlive this wrapper.
+    const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
 };
 
 // Wrapper for the AIDL Vibrator HAL.
 class AidlHalWrapper : public HalWrapper {
 public:
-    explicit AidlHalWrapper(sp<hardware::vibrator::IVibrator> handle)
-          : mHandle(std::move(handle)) {}
+    AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler,
+                   sp<hardware::vibrator::IVibrator> handle)
+          : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+    virtual ~AidlHalWrapper() = default;
 
-    virtual HalResult<void> ping() override;
+    HalResult<void> ping() override final;
+    void tryReconnect() override final;
 
-    virtual HalResult<void> on(std::chrono::milliseconds timeout,
-                               const std::function<void()>& completionCallback) override;
-    virtual HalResult<void> off() override;
+    HalResult<void> on(std::chrono::milliseconds timeout,
+                       const std::function<void()>& completionCallback) override final;
+    HalResult<void> off() override final;
 
-    virtual HalResult<void> setAmplitude(int32_t amplitude) override;
-    virtual HalResult<void> setExternalControl(bool enabled) override;
+    HalResult<void> setAmplitude(int32_t amplitude) override final;
+    HalResult<void> setExternalControl(bool enabled) override final;
 
-    virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
-                                           hardware::vibrator::EffectStrength strength) override;
-    virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+                                   hardware::vibrator::EffectStrength strength) override final;
+    HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    virtual HalResult<Capabilities> getCapabilities() override;
-    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+    HalResult<Capabilities> getCapabilities() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+            override final;
 
-    virtual HalResult<std::chrono::milliseconds> performEffect(
+    HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) override;
+            const std::function<void()>& completionCallback) override final;
 
-    virtual HalResult<void> performComposedEffect(
+    HalResult<void> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) override;
+            const std::function<void()>& completionCallback) override final;
 
 private:
-    const sp<hardware::vibrator::IVibrator> mHandle;
+    std::mutex mHandleMutex;
     std::mutex mCapabilitiesMutex;
     std::mutex mSupportedEffectsMutex;
+    std::mutex mSupportedPrimitivesMutex;
+    sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
     std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
     std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
             GUARDED_BY(mSupportedEffectsMutex);
+    std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
+            GUARDED_BY(mSupportedPrimitivesMutex);
+
+    // Loads directly from IVibrator handle, skipping caches.
+    HalResult<Capabilities> getCapabilitiesInternal();
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+    sp<hardware::vibrator::IVibrator> getHal();
 };
 
-// Wrapper for the HDIL Vibrator HAL v1.0.
-class HidlHalWrapperV1_0 : public HalWrapper {
+// Wrapper for the HDIL Vibrator HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
 public:
-    explicit HidlHalWrapperV1_0(sp<hardware::vibrator::V1_0::IVibrator> handle)
-          : mHandleV1_0(std::move(handle)) {}
+    HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+          : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+    virtual ~HidlHalWrapper() = default;
 
-    virtual HalResult<void> ping() override;
+    HalResult<void> ping() override final;
+    void tryReconnect() override final;
 
-    virtual HalResult<void> on(std::chrono::milliseconds timeout,
-                               const std::function<void()>& completionCallback) override;
-    virtual HalResult<void> off() override;
+    HalResult<void> on(std::chrono::milliseconds timeout,
+                       const std::function<void()>& completionCallback) override final;
+    HalResult<void> off() override final;
 
-    virtual HalResult<void> setAmplitude(int32_t amplitude) override;
+    HalResult<void> setAmplitude(int32_t amplitude) override final;
     virtual HalResult<void> setExternalControl(bool enabled) override;
 
-    virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
-                                           hardware::vibrator::EffectStrength strength) override;
-    virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+                                   hardware::vibrator::EffectStrength strength) override final;
+    HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    virtual HalResult<Capabilities> getCapabilities() override;
-    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+    HalResult<Capabilities> getCapabilities() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+            override final;
 
-    virtual HalResult<std::chrono::milliseconds> performEffect(
-            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) override;
-
-    virtual HalResult<void> performComposedEffect(
+    HalResult<void> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) override;
+            const std::function<void()>& completionCallback) override final;
 
 protected:
-    const sp<hardware::vibrator::V1_0::IVibrator> mHandleV1_0;
+    std::mutex mHandleMutex;
     std::mutex mCapabilitiesMutex;
+    sp<I> mHandle GUARDED_BY(mHandleMutex);
     std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
 
     // Loads directly from IVibrator handle, skipping the mCapabilities cache.
     virtual HalResult<Capabilities> getCapabilitiesInternal();
+
+    template <class T>
+    using perform_fn =
+            hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
+                                          hardware::vibrator::V1_0::IVibrator::perform_cb);
+
+    template <class T>
+    HalResult<std::chrono::milliseconds> performInternal(
+            perform_fn<T> performFn, sp<I> handle, T effect,
+            hardware::vibrator::EffectStrength strength,
+            const std::function<void()>& completionCallback);
+
+    sp<I> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
+public:
+    HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+                       sp<hardware::vibrator::V1_0::IVibrator> handle)
+          : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
+                                                                std::move(handle)) {}
+    virtual ~HidlHalWrapperV1_0() = default;
+
+    HalResult<std::chrono::milliseconds> performEffect(
+            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+            const std::function<void()>& completionCallback) override final;
 };
 
 // Wrapper for the HDIL Vibrator HAL v1.1.
-class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
 public:
-    explicit HidlHalWrapperV1_1(sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
-          : HidlHalWrapperV1_0(handleV1_0),
-            mHandleV1_1(hardware::vibrator::V1_1::IVibrator::castFrom(handleV1_0)) {}
+    HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
+                       sp<hardware::vibrator::V1_1::IVibrator> handle)
+          : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
+                                                                std::move(handle)) {}
+    virtual ~HidlHalWrapperV1_1() = default;
 
-    virtual HalResult<std::chrono::milliseconds> performEffect(
+    HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) override;
-
-protected:
-    const sp<hardware::vibrator::V1_1::IVibrator> mHandleV1_1;
+            const std::function<void()>& completionCallback) override final;
 };
 
 // Wrapper for the HDIL Vibrator HAL v1.2.
-class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
 public:
-    explicit HidlHalWrapperV1_2(sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
-          : HidlHalWrapperV1_1(handleV1_0),
-            mHandleV1_2(hardware::vibrator::V1_2::IVibrator::castFrom(handleV1_0)) {}
+    HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
+                       sp<hardware::vibrator::V1_2::IVibrator> handle)
+          : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
+                                                                std::move(handle)) {}
+    virtual ~HidlHalWrapperV1_2() = default;
 
-    virtual HalResult<std::chrono::milliseconds> performEffect(
+    HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) override;
-
-protected:
-    const sp<hardware::vibrator::V1_2::IVibrator> mHandleV1_2;
+            const std::function<void()>& completionCallback) override final;
 };
 
 // Wrapper for the HDIL Vibrator HAL v1.3.
-class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
 public:
-    explicit HidlHalWrapperV1_3(sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
-          : HidlHalWrapperV1_2(handleV1_0),
-            mHandleV1_3(hardware::vibrator::V1_3::IVibrator::castFrom(handleV1_0)) {}
+    HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
+                       sp<hardware::vibrator::V1_3::IVibrator> handle)
+          : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
+                                                                std::move(handle)) {}
+    virtual ~HidlHalWrapperV1_3() = default;
 
-    virtual HalResult<void> setExternalControl(bool enabled) override;
+    HalResult<void> setExternalControl(bool enabled) override final;
 
-    virtual HalResult<std::chrono::milliseconds> performEffect(
+    HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) override;
+            const std::function<void()>& completionCallback) override final;
 
 protected:
-    const sp<hardware::vibrator::V1_3::IVibrator> mHandleV1_3;
-
-    virtual HalResult<Capabilities> getCapabilitiesInternal() override;
+    HalResult<Capabilities> getCapabilitiesInternal() override final;
 };
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
new file mode 100644
index 0000000..99947a5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+
+#include <vibratorservice/VibratorHalController.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for VibratorManager HAL handlers.
+class ManagerHalWrapper {
+public:
+    ManagerHalWrapper() = default;
+    virtual ~ManagerHalWrapper() = default;
+
+    virtual HalResult<void> ping() = 0;
+
+    /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+     * service restarts, to rapidly retry after a failure.
+     */
+    virtual void tryReconnect() = 0;
+
+    virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
+    virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
+
+    virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
+    virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
+    virtual HalResult<void> cancelSynced() = 0;
+};
+
+// Wrapper for the VibratorManager over single Vibrator HAL.
+class LegacyManagerHalWrapper : public ManagerHalWrapper {
+public:
+    LegacyManagerHalWrapper() : LegacyManagerHalWrapper(std::make_shared<HalController>()) {}
+    explicit LegacyManagerHalWrapper(std::shared_ptr<HalController> controller)
+          : mController(std::move(controller)) {}
+    virtual ~LegacyManagerHalWrapper() = default;
+
+    HalResult<void> ping() override final;
+    void tryReconnect() override final;
+
+    HalResult<std::vector<int32_t>> getVibratorIds() override final;
+    HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+    HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+    HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+    HalResult<void> cancelSynced() override final;
+
+private:
+    const std::shared_ptr<HalController> mController;
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 7c038e9..5fc6d45 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -16,11 +16,14 @@
     name: "libvibratorservice_test",
     test_suites: ["device-tests"],
     srcs: [
+        "VibratorCallbackSchedulerTest.cpp",
+        "VibratorHalControllerTest.cpp",
         "VibratorHalWrapperAidlTest.cpp",
         "VibratorHalWrapperHidlV1_0Test.cpp",
         "VibratorHalWrapperHidlV1_1Test.cpp",
         "VibratorHalWrapperHidlV1_2Test.cpp",
         "VibratorHalWrapperHidlV1_3Test.cpp",
+        "VibratorManagerHalWrapperLegacyTest.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
new file mode 100644
index 0000000..aaeb8f9
--- /dev/null
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperAidlTest"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <condition_variable>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorCallbackSchedulerTest : public Test {
+public:
+    void SetUp() override {
+        mScheduler = std::make_unique<vibrator::CallbackScheduler>();
+        std::lock_guard<std::mutex> lock(mMutex);
+        mExpiredCallbacks.clear();
+    }
+
+protected:
+    std::mutex mMutex;
+    std::condition_variable_any mCondition;
+    std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+    std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
+
+    std::function<void()> createCallback(int32_t id) {
+        return [=]() {
+            {
+                std::lock_guard<std::mutex> lock(mMutex);
+                mExpiredCallbacks.push_back(id);
+            }
+            mCondition.notify_all();
+        };
+    }
+
+    std::vector<int32_t> getExpiredCallbacks() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return std::vector<int32_t>(mExpiredCallbacks);
+    }
+
+    bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
+        time_point<steady_clock> expiration = steady_clock::now() + timeout;
+        while (steady_clock::now() < expiration) {
+            std::lock_guard<std::mutex> lock(mMutex);
+            if (callbackCount <= mExpiredCallbacks.size()) {
+                return true;
+            }
+            mCondition.wait_until(mMutex, expiration);
+        }
+        return false;
+    }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+    mScheduler->schedule(createCallback(1), 15ms);
+
+    // Not triggered before delay.
+    ASSERT_FALSE(waitForCallbacks(1, 10ms));
+    ASSERT_TRUE(getExpiredCallbacks().empty());
+
+    ASSERT_TRUE(waitForCallbacks(1, 10ms));
+    ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
+    mScheduler->schedule(createCallback(1), 10ms);
+    mScheduler->schedule(createCallback(2), 5ms);
+    mScheduler->schedule(createCallback(3), 1ms);
+
+    ASSERT_TRUE(waitForCallbacks(3, 15ms));
+    ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 5; i++) {
+        threads.push_back(std::thread(
+                [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    ASSERT_TRUE(waitForCallbacks(5, 25ms));
+    ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
+    mScheduler->schedule(createCallback(1), 5ms);
+    mScheduler.reset(nullptr);
+
+    // Should time out waiting for callback to run.
+    ASSERT_FALSE(waitForCallbacks(1, 10ms));
+    ASSERT_TRUE(getExpiredCallbacks().empty());
+}
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
new file mode 100644
index 0000000..cda5e9a
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalControllerTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+#include <cutils/atomic.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+static constexpr int MAX_ATTEMPTS = 2;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public vibrator::HalWrapper {
+public:
+    MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
+          : HalWrapper(scheduler) {}
+    virtual ~MockHalWrapper() = default;
+
+    MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+    MOCK_METHOD(void, tryReconnect, (), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, on,
+                (milliseconds timeout, const std::function<void()>& completionCallback),
+                (override));
+    MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+                (int32_t id, Effect effect, EffectStrength strength), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+    MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
+    MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
+    MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
+                (override));
+    MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+                (Effect effect, EffectStrength strength,
+                 const std::function<void()>& completionCallback),
+                (override));
+    MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
+                (const std::vector<CompositeEffect>& primitiveEffects,
+                 const std::function<void()>& completionCallback),
+                (override));
+
+    vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
+};
+
+class TestHalConnector : public vibrator::HalConnector {
+public:
+    TestHalConnector(int32_t* connectCounter, std::shared_ptr<MockHalWrapper> mockHal)
+          : mConnectCounter(connectCounter), mMockHal(std::move(mockHal)) {}
+    ~TestHalConnector() = default;
+
+    std::shared_ptr<vibrator::HalWrapper> connect(
+            std::shared_ptr<vibrator::CallbackScheduler>) override final {
+        android_atomic_inc(mConnectCounter);
+        return mMockHal;
+    }
+
+private:
+    int32_t* mConnectCounter;
+    std::shared_ptr<MockHalWrapper> mMockHal;
+};
+
+class FailingHalConnector : public vibrator::HalConnector {
+public:
+    FailingHalConnector(int32_t* connectCounter) : mConnectCounter(connectCounter) {}
+    ~FailingHalConnector() = default;
+
+    std::shared_ptr<vibrator::HalWrapper> connect(
+            std::shared_ptr<vibrator::CallbackScheduler>) override final {
+        android_atomic_inc(mConnectCounter);
+        return nullptr;
+    }
+
+private:
+    int32_t* mConnectCounter;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalControllerTest : public Test {
+public:
+    void SetUp() override {
+        mConnectCounter = 0;
+        auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+        mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+        auto halConnector = std::make_unique<TestHalConnector>(&mConnectCounter, mMockHal);
+        mController = std::make_unique<vibrator::HalController>(std::move(halConnector),
+                                                                std::move(callbackScheduler));
+        ASSERT_NE(mController, nullptr);
+    }
+
+protected:
+    int32_t mConnectCounter;
+    std::shared_ptr<MockHalWrapper> mMockHal;
+    std::unique_ptr<vibrator::HalController> mController;
+
+    void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
+                            vibrator::HalResult<void> voidResult,
+                            vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
+                            vibrator::HalResult<std::vector<Effect>> effectsResult,
+                            vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+                            vibrator::HalResult<milliseconds> durationResult) {
+        EXPECT_CALL(*mMockHal.get(), ping())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), off())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(),
+                    alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), getCapabilities())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(capabilitiesResult));
+        EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(effectsResult));
+        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(primitivesResult));
+        EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(durationResult));
+        EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
+
+        if (cardinality > 1) {
+            // One reconnection call after each failure.
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+        }
+    }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalControllerTest, TestInit) {
+    ASSERT_TRUE(mController->init());
+    ASSERT_EQ(1, mConnectCounter);
+
+    // Noop when wrapper was already initialized.
+    ASSERT_TRUE(mController->init());
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
+    std::vector<Effect> effects;
+    effects.push_back(Effect::CLICK);
+    effects.push_back(Effect::TICK);
+    std::vector<CompositePrimitive> primitives;
+    primitives.push_back(CompositePrimitive::CLICK);
+    primitives.push_back(CompositePrimitive::THUD);
+    std::vector<CompositeEffect> compositeEffects;
+    compositeEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+    compositeEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+    setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
+                       vibrator::HalResult<vibrator::Capabilities>::ok(
+                               vibrator::Capabilities::ON_CALLBACK),
+                       vibrator::HalResult<std::vector<Effect>>::ok(effects),
+                       vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+                       vibrator::HalResult<milliseconds>::ok(100ms));
+
+    ASSERT_TRUE(mController->ping().isOk());
+    ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
+    ASSERT_TRUE(mController->off().isOk());
+    ASSERT_TRUE(mController->setAmplitude(255).isOk());
+    ASSERT_TRUE(mController->setExternalControl(true).isOk());
+    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+    ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
+
+    auto getCapabilitiesResult = mController->getCapabilities();
+    ASSERT_TRUE(getCapabilitiesResult.isOk());
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
+
+    auto getSupportedEffectsResult = mController->getSupportedEffects();
+    ASSERT_TRUE(getSupportedEffectsResult.isOk());
+    ASSERT_EQ(effects, getSupportedEffectsResult.value());
+
+    auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
+    ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
+    ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
+
+    auto performEffectResult =
+            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
+    ASSERT_TRUE(performEffectResult.isOk());
+    ASSERT_EQ(100ms, performEffectResult.value());
+
+    ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+    setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
+                       vibrator::HalResult<void>::unsupported(),
+                       vibrator::HalResult<vibrator::Capabilities>::unsupported(),
+                       vibrator::HalResult<std::vector<Effect>>::unsupported(),
+                       vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+                       vibrator::HalResult<milliseconds>::unsupported());
+
+    ASSERT_EQ(0, mConnectCounter);
+
+    ASSERT_TRUE(mController->ping().isUnsupported());
+    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+    ASSERT_TRUE(mController->off().isUnsupported());
+    ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+    ASSERT_TRUE(
+            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+                        .isUnsupported());
+    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+                        .isUnsupported());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+    setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
+                       vibrator::HalResult<void>::failed("message"),
+                       vibrator::HalResult<vibrator::Capabilities>::failed("message"),
+                       vibrator::HalResult<std::vector<Effect>>::failed("message"),
+                       vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+                       vibrator::HalResult<milliseconds>::failed("message"));
+
+    ASSERT_EQ(0, mConnectCounter);
+
+    ASSERT_TRUE(mController->ping().isFailed());
+    ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
+    ASSERT_TRUE(mController->off().isFailed());
+    ASSERT_TRUE(mController->setAmplitude(255).isFailed());
+    ASSERT_TRUE(mController->setExternalControl(true).isFailed());
+    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
+    ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
+    ASSERT_TRUE(mController->getCapabilities().isFailed());
+    ASSERT_TRUE(mController->getSupportedEffects().isFailed());
+    ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+    ASSERT_TRUE(
+            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
+    ASSERT_TRUE(
+            mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), ping())
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+        EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), ping())
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+    }
+
+    ASSERT_EQ(0, mConnectCounter);
+    ASSERT_TRUE(mController->ping().isOk());
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+    ASSERT_EQ(0, mConnectCounter);
+
+    EXPECT_CALL(*mMockHal.get(), ping())
+            .Times(Exactly(10))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    // Connector was called only by the first thread to use the api.
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) {
+    auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter);
+    mController =
+            std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
+    ASSERT_EQ(0, mConnectCounter);
+
+    ASSERT_FALSE(mController->init());
+    ASSERT_TRUE(mController->ping().isUnsupported());
+    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+    ASSERT_TRUE(mController->off().isUnsupported());
+    ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+    ASSERT_TRUE(
+            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+                        .isUnsupported());
+    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+                        .isUnsupported());
+
+    // One connection attempt per api call.
+    ASSERT_EQ(13, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+                .Times(Exactly(1))
+                .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
+                    mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
+                    return vibrator::HalResult<void>::ok();
+                });
+        EXPECT_CALL(*mMockHal.get(), ping())
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+        EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), ping())
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+        EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mController->on(10ms, callback).isOk());
+    ASSERT_TRUE(mController->ping().isFailed());
+    mMockHal.reset();
+    ASSERT_EQ(0, *callbackCounter.get());
+
+    // Callback triggered even after HalWrapper was reconnected.
+    std::this_thread::sleep_for(15ms);
+    ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 6db449a..96b76ba 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 #include <thread>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 #include "test_utils.h"
@@ -88,11 +89,13 @@
     void SetUp() override {
         mMockBinder = new StrictMock<MockBinder>();
         mMockHal = new StrictMock<MockIVibrator>();
-        mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockHal);
+        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+        mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
         ASSERT_NE(mWrapper, nullptr);
     }
 
 protected:
+    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
     std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
     sp<StrictMock<MockBinder>> mMockBinder = nullptr;
@@ -113,20 +116,25 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPing) {
-    {
-        InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), onAsBinder())
-                .Times(Exactly(1))
-                .WillRepeatedly(Return(mMockBinder.get()));
-        EXPECT_CALL(*mMockBinder.get(), pingBinder()).Times(Exactly(1));
-    }
+    EXPECT_CALL(*mMockHal.get(), onAsBinder())
+            .Times(Exactly(2))
+            .WillRepeatedly(Return(mMockBinder.get()));
+    EXPECT_CALL(*mMockBinder.get(), pingBinder())
+            .Times(Exactly(2))
+            .WillOnce(Return(android::OK))
+            .WillRepeatedly(Return(android::DEAD_OBJECT));
 
+    ASSERT_TRUE(mWrapper->ping().isOk());
     ASSERT_TRUE(mWrapper->ping().isFailed());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestOn) {
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
     {
         InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
@@ -146,11 +154,46 @@
     ASSERT_EQ(1, *callbackCounter.get());
 
     ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported());
-    // Callback not triggered
+    // Callback not triggered for unsupported
     ASSERT_EQ(1, *callbackCounter.get());
 
     ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
-    // Callback not triggered
+    // Callback not triggered on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status()));
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+        EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+    // Callback not triggered for unsupported and on failure
     ASSERT_EQ(1, *callbackCounter.get());
 }
 
@@ -274,6 +317,10 @@
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
@@ -314,11 +361,63 @@
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getSupportedEffects();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(supportedEffects, result.value());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestPerformEffect) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
+    std::vector<CompositePrimitive> supportedPrimitives;
+    supportedPrimitives.push_back(CompositePrimitive::CLICK);
+    supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
+
+    auto result = mWrapper->getSupportedPrimitives();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(supportedPrimitives, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
+    std::vector<CompositePrimitive> supportedPrimitives;
+    supportedPrimitives.push_back(CompositePrimitive::CLICK);
+    supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getSupportedPrimitives();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(supportedPrimitives, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getSupportedPrimitives();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(supportedPrimitives, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
     {
         InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
                 .Times(Exactly(1))
                 .WillRepeatedly(
@@ -342,12 +441,52 @@
 
     result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
+    // Callback not triggered for unsupported
     ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
+    // Callback not triggered on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+        EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+
+    result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    // Callback not triggered for unsupported and on failure
     ASSERT_EQ(1, *callbackCounter.get());
 }
 
@@ -383,11 +522,11 @@
 
     result = mWrapper->performComposedEffect(singleEffect, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
+    // Callback not triggered for unsupported
     ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
+    // Callback not triggered on failure
     ASSERT_EQ(1, *callbackCounter.get());
 }
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 7f1016f..06aa36f 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 #include <thread>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 #include "test_utils.h"
@@ -59,11 +60,13 @@
 public:
     void SetUp() override {
         mMockHal = new StrictMock<MockIVibratorV1_0>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockHal);
+        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
         ASSERT_NE(mWrapper, nullptr);
     }
 
 protected:
+    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
     std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
 };
@@ -89,17 +92,20 @@
                 .Times(Exactly(1))
                 .WillRepeatedly(
                         [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
                 .Times(Exactly(1))
                 .WillRepeatedly([](uint32_t) {
                     return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
                 });
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(100))))
+        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
                 .Times(Exactly(1))
                 .WillRepeatedly([](uint32_t) {
                     return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
                 });
-        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1000))))
+        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
                 .Times(Exactly(1))
                 .WillRepeatedly([](uint32_t) {
                     return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
@@ -110,20 +116,14 @@
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
     ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(1, *callbackCounter.get());
 
     ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+    ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
 
-    ASSERT_TRUE(mWrapper->on(100ms, callback).isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
-
-    ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
@@ -226,12 +226,20 @@
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
     ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
 }
 
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
+    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+}
+
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
     {
         InSequence seq;
@@ -240,9 +248,12 @@
                 .Times(Exactly(1))
                 .WillRepeatedly(
                         [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
-                            cb(V1_0::Status::OK, 100);
+                            cb(V1_0::Status::OK, 10);
                             return hardware::Return<void>();
                         });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(),
                     perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
                 .Times(Exactly(1))
@@ -269,24 +280,20 @@
 
     auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
@@ -295,6 +302,7 @@
     // Using TICK that is only available in v1.1
     auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isUnsupported());
+    // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
 }
 
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
index d0531e6..d887efc 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -23,6 +23,7 @@
 
 #include <utils/Log.h>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 #include "test_utils.h"
@@ -58,11 +59,13 @@
 public:
     void SetUp() override {
         mMockHal = new StrictMock<MockIVibratorV1_1>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockHal);
+        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
         ASSERT_NE(mWrapper, nullptr);
     }
 
 protected:
+    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
     std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
 };
@@ -70,23 +73,28 @@
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
+                            cb(V1_0::Status::OK, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
@@ -100,6 +108,9 @@
                     cb(V1_0::Status::OK, 10);
                     return hardware::Return<void>();
                 });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(),
                     perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
                 .Times(Exactly(1))
@@ -128,23 +139,19 @@
     auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(10ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
@@ -153,5 +160,6 @@
     // Using THUD that is only available in v1.2
     auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isUnsupported());
+    // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
 }
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
index 5d2c269..26d9350 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -23,6 +23,7 @@
 
 #include <utils/Log.h>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 #include "test_utils.h"
@@ -61,11 +62,13 @@
 public:
     void SetUp() override {
         mMockHal = new StrictMock<MockIVibratorV1_2>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockHal);
+        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
         ASSERT_NE(mWrapper, nullptr);
     }
 
 protected:
+    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
     std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
 };
@@ -73,43 +76,53 @@
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+                            cb(V1_0::Status::OK, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+                                   MockIVibratorV1_2::perform_cb cb) {
+                    cb(V1_0::Status::OK, 10);
+                    return hardware::Return<void>();
+                });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
@@ -120,9 +133,12 @@
                 .Times(Exactly(1))
                 .WillRepeatedly(
                         [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
-                            cb(V1_0::Status::OK, 100);
+                            cb(V1_0::Status::OK, 10);
                             return hardware::Return<void>();
                         });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(),
                     perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
                 .Times(Exactly(1))
@@ -149,24 +165,20 @@
 
     auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
@@ -175,5 +187,6 @@
     // Using TEXTURE_TICK that is only available in v1.3
     auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isUnsupported());
+    // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
 }
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index a799257..08652f4 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 #include <thread>
 
+#include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
 #include "test_utils.h"
@@ -68,11 +69,13 @@
 public:
     void SetUp() override {
         mMockHal = new StrictMock<MockIVibratorV1_3>();
-        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockHal);
+        mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+        mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
         ASSERT_NE(mWrapper, nullptr);
     }
 
 protected:
+    std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
     std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
     sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
 };
@@ -115,7 +118,8 @@
 
     auto result = mWrapper->getCapabilities();
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
+                      vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
               result.value());
 }
 
@@ -210,6 +214,10 @@
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
@@ -258,63 +266,78 @@
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+                            cb(V1_0::Status::OK, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+                                   MockIVibratorV1_3::perform_cb cb) {
+                    cb(V1_0::Status::OK, 10);
+                    return hardware::Return<void>();
+                });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
-    EXPECT_CALL(*mMockHal.get(),
-                perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(
-                    [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
-                        cb(V1_0::Status::OK, 100);
-                        return hardware::Return<void>();
-                    });
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+                            cb(V1_0::Status::OK, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
     auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
 
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(100ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
@@ -328,6 +351,9 @@
                             cb(V1_0::Status::OK, 10);
                             return hardware::Return<void>();
                         });
+        EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+                .Times(Exactly(1))
+                .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(),
                     perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
                                 _))
@@ -357,21 +383,17 @@
     auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(10ms, result.value());
-    // TODO(b/153418251): check callback will be triggered once implemented
-    ASSERT_EQ(0, *callbackCounter.get());
+    ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
     ASSERT_TRUE(result.isUnsupported());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
 
     result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
     ASSERT_TRUE(result.isFailed());
-    // Callback not triggered
-    ASSERT_EQ(0, *callbackCounter.get());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
 }
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
new file mode 100644
index 0000000..d5520a1
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapperLegacyTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalController : public vibrator::HalController {
+public:
+    MockHalController() = default;
+    virtual ~MockHalController() = default;
+
+    MOCK_METHOD(bool, init, (), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+    MOCK_METHOD(void, tryReconnect, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorManagerHalWrapperLegacyTest : public Test {
+public:
+    void SetUp() override {
+        mMockController = std::make_shared<StrictMock<MockHalController>>();
+        mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController);
+        ASSERT_NE(mWrapper, nullptr);
+    }
+
+protected:
+    std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr;
+    std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
+    EXPECT_CALL(*mMockController.get(), ping())
+            .Times(Exactly(2))
+            .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+    ASSERT_TRUE(mWrapper->ping().isFailed());
+    ASSERT_TRUE(mWrapper->ping().isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
+    EXPECT_CALL(*mMockController.get(), tryReconnect()).Times(Exactly(1));
+
+    mWrapper->tryReconnect();
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
+    std::vector<int32_t> expectedIds;
+    expectedIds.push_back(0);
+
+    EXPECT_CALL(*mMockController.get(), init())
+            .Times(Exactly(2))
+            .WillOnce(Return(false))
+            .WillRepeatedly(Return(true));
+
+    auto result = mWrapper->getVibratorIds();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(std::vector<int32_t>(), result.value());
+
+    result = mWrapper->getVibratorIds();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(expectedIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithValidIdReturnsController) {
+    EXPECT_CALL(*mMockController.get(), init())
+            .Times(Exactly(2))
+            .WillOnce(Return(false))
+            .WillRepeatedly(Return(true));
+
+    ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
+
+    auto result = mWrapper->getVibrator(0);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(mMockController.get(), result.value().get());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithInvalidIdFails) {
+    ASSERT_TRUE(mWrapper->getVibrator(-1).isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) {
+    std::vector<int32_t> vibratorIds;
+    vibratorIds.push_back(0);
+
+    ASSERT_TRUE(mWrapper->prepareSynced(vibratorIds).isUnsupported());
+    ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
+    ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index fc9b364..8d0b22e 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -28,6 +28,20 @@
 using ::android::hardware::vibrator::CompositeEffect;
 using ::android::hardware::vibrator::CompositePrimitive;
 
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public vibrator::CallbackScheduler {
+public:
+    MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+                (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+    arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
 class TestFactory {
 public:
     static CompositeEffect createCompositeEffect(CompositePrimitive primitive,
@@ -48,6 +62,8 @@
     ~TestFactory() = delete;
 };
 
+// -------------------------------------------------------------------------------------------------
+
 } // namespace vibrator
 
 } // namespace android
diff --git a/vulkan/include/hardware/hwvulkan.h b/vulkan/include/hardware/hwvulkan.h
index 9e9a14d..98bc8e3 100644
--- a/vulkan/include/hardware/hwvulkan.h
+++ b/vulkan/include/hardware/hwvulkan.h
@@ -54,8 +54,9 @@
 /* A hwvulkan_device_t corresponds to an ICD on other systems. Currently there
  * can only be one on a system (HWVULKAN_DEVICE_0). It is opened once per
  * process when the Vulkan API is first used; the hw_device_t::close() function
- * is never called. Any non-trivial resource allocation should be done when
- * the VkInstance is created rather than when the hwvulkan_device_t is opened.
+ * is called upon driver unloading. Any non-trivial resource allocation should
+ * be done when the VkInstance is created rather than when the hwvulkan_device_t
+ * is opened.
  */
 typedef struct hwvulkan_device_t {
     struct hw_device_t common;
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 9ffe83b..ba98696 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,17 +27,19 @@
 #define VK_ANDROID_native_buffer 1
 
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
  *
  * This version of the extension transitions from gralloc0 to gralloc1 usage
  * flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
  * to fill out deprecated fields in VkNativeBufferANDROID, and will call the
  * deprecated vkGetSwapchainGrallocUsageANDROID if the new
  * vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
- * backwards-compatibility support is temporary, and will likely be removed in
+ * backwards-compatibility support is temporary, and will likely be removed
  * (along with all gralloc0 support) in a future release.
  */
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
  *
  * This version of the extension doesn't introduce new types or structs, but is
  * to accommodate the new struct VkBindImageMemorySwapchainInfoKHR added in
@@ -47,97 +49,155 @@
  * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
  * pNext chain of VkBindImageMemoryInfo and passed down to the driver.
  */
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     8
-#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME   "VK_ANDROID_native_buffer"
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
 
-#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id)    ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
-#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID   VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
-#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
-#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
+    ((type)(1000000000 +                        \
+            (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
+#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID \
+    VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID \
+    VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
+    VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
 
+/* clang-format off */
 typedef enum VkSwapchainImageUsageFlagBitsANDROID {
     VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
     VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkSwapchainImageUsageFlagBitsANDROID;
 typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
 
+/*
+ * struct VkNativeBufferUsage2ANDROID
+ *
+ * consumer: gralloc1 consumer usage flag
+ * producer: gralloc1 producer usage flag
+ */
 typedef struct {
-    uint64_t consumer;
-    uint64_t producer;
+    uint64_t                          consumer;
+    uint64_t                          producer;
 } VkNativeBufferUsage2ANDROID;
 
+/*
+ * struct VkNativeBufferANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * handle: buffer handle returned from gralloc alloc()
+ * stride: stride returned from gralloc alloc()
+ * format: gralloc format requested when the buffer was allocated
+ * usage: gralloc usage requested when the buffer was allocated
+ * usage2: gralloc usage requested when the buffer was allocated
+ */
 typedef struct {
-    VkStructureType             sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
-    const void*                 pNext;
-
-    // Buffer handle and stride returned from gralloc alloc()
-    buffer_handle_t             handle;
-    int                         stride;
-
-    // Gralloc format and usage requested when the buffer was allocated.
-    int                         format;
-    int                         usage; // DEPRECATED in SPEC_VERSION 6
-    // -- Added in SPEC_VERSION 6 --
-    VkNativeBufferUsage2ANDROID usage2;
+    VkStructureType                   sType;
+    const void*                       pNext;
+    buffer_handle_t                   handle;
+    int                               stride;
+    int                               format;
+    int                               usage; /* DEPRECATED in SPEC_VERSION 6 */
+    VkNativeBufferUsage2ANDROID       usage2; /* ADDED in SPEC_VERSION 6 */
 } VkNativeBufferANDROID;
 
+/*
+ * struct VkSwapchainImageCreateInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * usage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
 typedef struct {
-    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
-    const void*                            pNext;
-
-    VkSwapchainImageUsageFlagsANDROID      usage;
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkSwapchainImageUsageFlagsANDROID usage;
 } VkSwapchainImageCreateInfoANDROID;
 
+/*
+ * struct VkPhysicalDevicePresentationPropertiesANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * sharedImage: specifies if the image can be shared with the display system
+ */
 typedef struct {
-    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
-    const void*                            pNext;
-
-    VkBool32                               sharedImage;
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkBool32                          sharedImage;
 } VkPhysicalDevicePresentationPropertiesANDROID;
 
-// -- DEPRECATED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
-// -- ADDED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
-typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
-typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+/* DEPRECATED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
+    VkDevice                          device,
+    VkFormat                          format,
+    VkImageUsageFlags                 imageUsage,
+    int*                              grallocUsage);
+
+/* ADDED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
+    VkDevice                          device,
+    VkFormat                          format,
+    VkImageUsageFlags                 imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t*                         grallocConsumerUsage,
+    uint64_t*                         grallocProducerUsage);
+
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
+    VkDevice                          device,
+    VkImage                           image,
+    int                               nativeFenceFd,
+    VkSemaphore                       semaphore,
+    VkFence                           fence);
+
+typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(
+    VkQueue                           queue,
+    uint32_t                          waitSemaphoreCount,
+    const VkSemaphore*                pWaitSemaphores,
+    VkImage                           image,
+    int*                              pNativeFenceFd);
 
 #ifndef VK_NO_PROTOTYPES
 
-// -- DEPRECATED in SPEC_VERSION 6 --
+/* DEPRECATED in SPEC_VERSION 6 */
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
-    VkDevice            device,
-    VkFormat            format,
-    VkImageUsageFlags   imageUsage,
-    int*                grallocUsage
+    VkDevice                          device,
+    VkFormat                          format,
+    VkImageUsageFlags                 imageUsage,
+    int*                              grallocUsage
 );
-// -- ADDED in SPEC_VERSION 6 --
+
+/* ADDED in SPEC_VERSION 6 */
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
-    VkDevice            device,
-    VkFormat            format,
-    VkImageUsageFlags   imageUsage,
+    VkDevice                          device,
+    VkFormat                          format,
+    VkImageUsageFlags                 imageUsage,
     VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
-    uint64_t*           grallocConsumerUsage,
-    uint64_t*           grallocProducerUsage
+    uint64_t*                         grallocConsumerUsage,
+    uint64_t*                         grallocProducerUsage
 );
+
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
-    VkDevice            device,
-    VkImage             image,
-    int                 nativeFenceFd,
-    VkSemaphore         semaphore,
-    VkFence             fence
+    VkDevice                          device,
+    VkImage                           image,
+    int                               nativeFenceFd,
+    VkSemaphore                       semaphore,
+    VkFence                           fence
 );
+
 VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageANDROID(
-    VkQueue             queue,
-    uint32_t            waitSemaphoreCount,
-    const VkSemaphore*  pWaitSemaphores,
-    VkImage             image,
-    int*                pNativeFenceFd
+    VkQueue                           queue,
+    uint32_t                          waitSemaphoreCount,
+    const VkSemaphore*                pWaitSemaphores,
+    VkImage                           image,
+    int*                              pNativeFenceFd
 );
+
 #endif
+/* clang-format on */
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif // __VK_ANDROID_NATIVE_BUFFER_H__
+#endif /* __VK_ANDROID_NATIVE_BUFFER_H__ */
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index f69de1f..1d29bab 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -21,7 +21,7 @@
 }
 
 llndk_library {
-    name: "libvulkan",
+    name: "libvulkan.llndk",
     symbol_file: "libvulkan.map.txt",
     export_llndk_headers: [
         "vulkan_headers_llndk",
@@ -30,6 +30,7 @@
 
 cc_library_shared {
     name: "libvulkan",
+    llndk_stubs: "libvulkan.llndk",
     clang: true,
     sanitize: {
         misc_undefined: ["integer"],
@@ -89,7 +90,6 @@
         "libhardware",
         "libsync",
         "libbase",
-        "libdl_android",
         "libhidlbase",
         "liblog",
         "libui",
@@ -100,6 +100,7 @@
         "libnativebridge_lazy",
         "libnativeloader_lazy",
         "libnativewindow",
+        "libvndksupport",
         "android.hardware.graphics.common@1.0",
         "libSurfaceFlingerProp",
     ],
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 5b9affd..2d4690a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1174,23 +1174,18 @@
 // ----------------------------------------------------------------------------
 
 bool EnsureInitialized() {
-    static std::once_flag once_flag;
-    static bool initialized;
+    static bool initialized = false;
+    static pid_t init_attempted_for_pid = 0;
+    static std::mutex init_lock;
 
-    std::call_once(once_flag, []() {
-        if (driver::OpenHAL()) {
-            initialized = true;
-        }
-    });
+    std::lock_guard<std::mutex> lock(init_lock);
+    if (init_attempted_for_pid == getpid())
+        return initialized;
 
-    {
-        static pid_t pid = getpid() + 1;
-        static std::mutex layer_lock;
-        std::lock_guard<std::mutex> lock(layer_lock);
-        if (pid != getpid()) {
-            pid = getpid();
-            DiscoverLayers();
-        }
+    init_attempted_for_pid = getpid();
+    if (driver::OpenHAL()) {
+        DiscoverLayers();
+        initialized = true;
     }
 
     return initialized;
@@ -1256,7 +1251,7 @@
     ATRACE_CALL();
 
     if (!EnsureInitialized())
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
 
     uint32_t count = GetLayerCount();
 
@@ -1280,7 +1275,7 @@
     ATRACE_CALL();
 
     if (!EnsureInitialized())
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
 
     if (pLayerName) {
         const Layer* layer = FindLayer(pLayerName);
@@ -1456,6 +1451,11 @@
 VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
     ATRACE_CALL();
 
+    // Load the driver here if not done yet. This api will be used in Zygote
+    // for Vulkan driver pre-loading because of the minimum overhead.
+    if (!EnsureInitialized())
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
     *pApiVersion = VK_API_VERSION_1_1;
     return VK_SUCCESS;
 }
diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h
index 416cba0..2a215d7 100644
--- a/vulkan/libvulkan/api.h
+++ b/vulkan/libvulkan/api.h
@@ -24,17 +24,34 @@
 namespace vulkan {
 namespace api {
 
-// clang-format off
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+                                   const VkAllocationCallbacks* pAllocator,
+                                   VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+                                const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+                                 const VkDeviceCreateInfo* pCreateInfo,
+                                 const VkAllocationCallbacks* pAllocator,
+                                 VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+                              const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
+                                 VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+                                     uint32_t* pPropertyCount,
+                                     VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
+                               uint32_t* pPropertyCount,
+                               VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+                                   const char* pLayerName,
+                                   uint32_t* pPropertyCount,
+                                   VkExtensionProperties* pProperties);
 VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
-// clang-format on
 
 inline InstanceData& GetData(VkInstance instance) {
     return driver::GetData(instance).opaque_api_data;
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index d9a9427..26052fb 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -226,6 +226,7 @@
     INIT_PROC(true, dev, CreateQueryPool);
     INIT_PROC(true, dev, DestroyQueryPool);
     INIT_PROC(true, dev, GetQueryPoolResults);
+    INIT_PROC(false, dev, ResetQueryPool);
     INIT_PROC(true, dev, CreateBuffer);
     INIT_PROC(true, dev, DestroyBuffer);
     INIT_PROC(true, dev, CreateBufferView);
@@ -337,8 +338,20 @@
     INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
     INIT_PROC(false, dev, GetDeviceQueue2);
     INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
+    INIT_PROC(false, dev, CreateRenderPass2);
+    INIT_PROC(false, dev, CmdBeginRenderPass2);
+    INIT_PROC(false, dev, CmdNextSubpass2);
+    INIT_PROC(false, dev, CmdEndRenderPass2);
+    INIT_PROC(false, dev, GetSemaphoreCounterValue);
+    INIT_PROC(false, dev, WaitSemaphores);
+    INIT_PROC(false, dev, SignalSemaphore);
     INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID);
     INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID);
+    INIT_PROC(false, dev, CmdDrawIndirectCount);
+    INIT_PROC(false, dev, CmdDrawIndexedIndirectCount);
+    INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
+    INIT_PROC(false, dev, GetBufferDeviceAddress);
+    INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
     // clang-format on
 
     return success;
@@ -391,6 +404,7 @@
 VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
 VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
 VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -520,8 +534,20 @@
 VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
 VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
 VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
 VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+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 EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
     return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -551,6 +577,7 @@
         "vkEnumerateInstanceVersion",
         "vkEnumeratePhysicalDeviceGroups",
         "vkEnumeratePhysicalDeviceGroupsKHR",
+        "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR",
         "vkEnumeratePhysicalDevices",
         "vkGetDisplayModeProperties2KHR",
         "vkGetDisplayPlaneCapabilities2KHR",
@@ -571,7 +598,7 @@
         "vkGetPhysicalDeviceFormatProperties",
         "vkGetPhysicalDeviceFormatProperties2",
         "vkGetPhysicalDeviceFormatProperties2KHR",
-        "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
+        "vkGetPhysicalDeviceFragmentShadingRatesKHR",
         "vkGetPhysicalDeviceImageFormatProperties",
         "vkGetPhysicalDeviceImageFormatProperties2",
         "vkGetPhysicalDeviceImageFormatProperties2KHR",
@@ -583,6 +610,7 @@
         "vkGetPhysicalDeviceProperties",
         "vkGetPhysicalDeviceProperties2",
         "vkGetPhysicalDeviceProperties2KHR",
+        "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR",
         "vkGetPhysicalDeviceQueueFamilyProperties",
         "vkGetPhysicalDeviceQueueFamilyProperties2",
         "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
@@ -595,6 +623,7 @@
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
+        "vkGetPhysicalDeviceToolPropertiesEXT",
         "vkSubmitDebugUtilsMessageEXT",
     };
     // clang-format on
@@ -646,6 +675,7 @@
         { "vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2) },
         { "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) },
         { "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) },
+        { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
         { "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
         { "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
         { "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
@@ -665,12 +695,16 @@
         { "vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(CmdDraw) },
         { "vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexed) },
         { "vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirect) },
+        { "vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirectCount) },
         { "vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirect) },
+        { "vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirectCount) },
         { "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) },
         { "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) },
+        { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) },
         { "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) },
         { "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
         { "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
@@ -709,6 +743,7 @@
         { "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) },
         { "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) },
         { "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) },
+        { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) },
         { "vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(CreateSampler) },
         { "vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(CreateSamplerYcbcrConversion) },
         { "vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(CreateSemaphore) },
@@ -749,13 +784,16 @@
         { "vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(FreeDescriptorSets) },
         { "vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(FreeMemory) },
         { "vkGetAndroidHardwareBufferPropertiesANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetAndroidHardwareBufferPropertiesANDROID) },
+        { "vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferDeviceAddress) },
         { "vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements) },
         { "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) },
+        { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) },
         { "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) },
         { "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) },
         { "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) },
         { "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
         { "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
+        { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
         { "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
         { "vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue) },
         { "vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue2) },
@@ -771,6 +809,7 @@
         { "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
         { "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
         { "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+        { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
         { "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
         { "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
         { "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
@@ -784,12 +823,15 @@
         { "vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(ResetDescriptorPool) },
         { "vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(ResetEvent) },
         { "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) },
+        { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) },
         { "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
+        { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
         { "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
         { "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
         { "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
         { "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
         { "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
+        { "vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(WaitSemaphores) },
     };
     // clang-format on
     constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
@@ -965,6 +1007,10 @@
     return GetData(device).dispatch.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
 }
 
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+    GetData(device).dispatch.ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
 VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
     return GetData(device).dispatch.CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
 }
@@ -1481,6 +1527,34 @@
     GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
 }
 
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+    return GetData(device).dispatch.CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+    GetData(commandBuffer).dispatch.CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+    GetData(commandBuffer).dispatch.CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+    GetData(commandBuffer).dispatch.CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+    return GetData(device).dispatch.GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+    return GetData(device).dispatch.WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+    return GetData(device).dispatch.SignalSemaphore(device, pSignalInfo);
+}
+
 VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
     return GetData(device).dispatch.GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
 }
@@ -1489,6 +1563,26 @@
     return GetData(device).dispatch.GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
 }
 
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+    GetData(commandBuffer).dispatch.CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+    GetData(commandBuffer).dispatch.CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    return GetData(device).dispatch.GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    return GetData(device).dispatch.GetBufferDeviceAddress(device, pInfo);
+}
+
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+    return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
 
 }  // anonymous namespace
 
@@ -1755,6 +1849,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+    vulkan::api::ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
     return vulkan::api::CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
 }
@@ -2400,6 +2499,41 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+    return vulkan::api::CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+    vulkan::api::CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+    vulkan::api::CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+    vulkan::api::CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+    return vulkan::api::GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+    return vulkan::api::WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+    return vulkan::api::SignalSemaphore(device, pSignalInfo);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkGetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
     return vulkan::api::GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
 }
@@ -2409,4 +2543,29 @@
     return vulkan::api::GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
 }
 
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+    vulkan::api::CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+    vulkan::api::CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    return vulkan::api::GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkDeviceAddress vkGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    return vulkan::api::GetBufferDeviceAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+    return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
 // clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 2195845..ad5cc34 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -99,6 +99,7 @@
     PFN_vkCreateQueryPool CreateQueryPool;
     PFN_vkDestroyQueryPool DestroyQueryPool;
     PFN_vkGetQueryPoolResults GetQueryPoolResults;
+    PFN_vkResetQueryPool ResetQueryPool;
     PFN_vkCreateBuffer CreateBuffer;
     PFN_vkDestroyBuffer DestroyBuffer;
     PFN_vkCreateBufferView CreateBufferView;
@@ -210,8 +211,20 @@
     PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
     PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
+    PFN_vkCreateRenderPass2 CreateRenderPass2;
+    PFN_vkCmdBeginRenderPass2 CmdBeginRenderPass2;
+    PFN_vkCmdNextSubpass2 CmdNextSubpass2;
+    PFN_vkCmdEndRenderPass2 CmdEndRenderPass2;
+    PFN_vkGetSemaphoreCounterValue GetSemaphoreCounterValue;
+    PFN_vkWaitSemaphores WaitSemaphores;
+    PFN_vkSignalSemaphore SignalSemaphore;
     PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID;
     PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID;
+    PFN_vkCmdDrawIndirectCount CmdDrawIndirectCount;
+    PFN_vkCmdDrawIndexedIndirectCount CmdDrawIndexedIndirectCount;
+    PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
+    PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
+    PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 74ef0e7..d7fdab5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -28,20 +28,17 @@
 #include <android/dlext.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
-#include <cutils/properties.h>
 #include <graphicsenv/GraphicsEnv.h>
 #include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
 #include <sys/prctl.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
+#include <vndksupport/linker.h>
 
 #include <algorithm>
 #include <array>
 #include <climits>
 #include <new>
-#include <string_view>
-#include <sstream>
 #include <vector>
 
 #include "stubhal.h"
@@ -84,6 +81,8 @@
     Hal(const Hal&) = delete;
     Hal& operator=(const Hal&) = delete;
 
+    bool ShouldUnloadBuiltinDriver();
+    void UnloadBuiltinDriver();
     bool InitDebugReportIndex();
 
     static Hal hal_;
@@ -95,15 +94,15 @@
 class CreateInfoWrapper {
    public:
     CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+                      uint32_t icd_api_version,
                       const VkAllocationCallbacks& allocator);
     CreateInfoWrapper(VkPhysicalDevice physical_dev,
                       const VkDeviceCreateInfo& create_info,
+                      uint32_t icd_api_version,
                       const VkAllocationCallbacks& allocator);
     ~CreateInfoWrapper();
 
     VkResult Validate();
-    void DowngradeApiVersion();
-    void UpgradeDeviceCoreApiVersion(uint32_t api_version);
 
     const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
     const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
@@ -118,10 +117,12 @@
 
         const char** names;
         uint32_t name_count;
+        ExtensionFilter()
+            : exts(nullptr), ext_count(0), names(nullptr), name_count(0) {}
     };
 
+    VkResult SanitizeApiVersion();
     VkResult SanitizePNext();
-
     VkResult SanitizeLayers();
     VkResult SanitizeExtensions();
 
@@ -133,6 +134,8 @@
 
     const bool is_instance_;
     const VkAllocationCallbacks& allocator_;
+    const uint32_t loader_api_version_;
+    const uint32_t icd_api_version_;
 
     VkPhysicalDevice physical_dev_;
 
@@ -151,19 +154,11 @@
 
 Hal Hal::hal_;
 
-void* LoadLibrary(const android_dlextinfo& dlextinfo,
-                  const std::string_view subname) {
-    ATRACE_CALL();
-
-    std::stringstream ss;
-    ss << "vulkan." << subname << ".so";
-    return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
-}
-
 const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
     "ro.hardware.vulkan",
     "ro.board.platform",
 }};
+constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
 
 // LoadDriver returns:
 // * 0 when succeed, or
@@ -174,20 +169,28 @@
                const hwvulkan_module_t** module) {
     ATRACE_CALL();
 
-    const android_dlextinfo dlextinfo = {
-        .flags = ANDROID_DLEXT_USE_NAMESPACE,
-        .library_namespace = library_namespace,
-    };
     void* so = nullptr;
-    char prop[PROPERTY_VALUE_MAX];
     for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
-        int prop_len = property_get(key, prop, nullptr);
-        if (prop_len > 0 && prop_len <= UINT_MAX) {
-            std::string_view lib_name(prop, static_cast<unsigned int>(prop_len));
-            so = LoadLibrary(dlextinfo, lib_name);
-            if (so)
-                break;
+        std::string lib_name = android::base::GetProperty(key, "");
+        if (lib_name.empty())
+            continue;
+
+        lib_name = "vulkan." + lib_name + ".so";
+        if (library_namespace) {
+            // load updated driver
+            const android_dlextinfo dlextinfo = {
+                .flags = ANDROID_DLEXT_USE_NAMESPACE,
+                .library_namespace = library_namespace,
+            };
+            so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
+            ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
+                  lib_name.c_str(), dlerror());
+        } else {
+            // load built-in driver
+            so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
         }
+        if (so)
+            break;
     }
     if (!so)
         return -ENOENT;
@@ -211,12 +214,9 @@
 int LoadBuiltinDriver(const hwvulkan_module_t** module) {
     ATRACE_CALL();
 
-    auto ns = android_get_exported_namespace("sphal");
-    if (!ns)
-        return -ENOENT;
     android::GraphicsEnv::getInstance().setDriverToLoad(
         android::GpuStatsInfo::Driver::VULKAN);
-    return LoadDriver(ns, module);
+    return LoadDriver(nullptr, module);
 }
 
 int LoadUpdatedDriver(const hwvulkan_module_t** module) {
@@ -241,7 +241,12 @@
 
     const nsecs_t openTime = systemTime();
 
-    ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
+    if (hal_.ShouldUnloadBuiltinDriver()) {
+        hal_.UnloadBuiltinDriver();
+    }
+
+    if (hal_.dev_)
+        return true;
 
     // Use a stub device unless we successfully open a real HAL device.
     hal_.dev_ = &stubhal::kDevice;
@@ -286,6 +291,38 @@
     return true;
 }
 
+bool Hal::ShouldUnloadBuiltinDriver() {
+    // Should not unload since the driver was not loaded
+    if (!hal_.dev_)
+        return false;
+
+    // Should not unload if stubhal is used on the device
+    if (hal_.dev_ == &stubhal::kDevice)
+        return false;
+
+    // Unload the driver if updated driver is chosen
+    if (android::GraphicsEnv::getInstance().getDriverNamespace())
+        return true;
+
+    return false;
+}
+
+void Hal::UnloadBuiltinDriver() {
+    ATRACE_CALL();
+
+    ALOGD("Unload builtin Vulkan driver.");
+
+    // Close the opened device
+    ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common),
+                "hw_device_t::close() failed.");
+
+    // Close the opened shared library in the hw_module_t
+    android_unload_sphal_library(hal_.dev_->common.module->dso);
+
+    hal_.dev_ = nullptr;
+    hal_.debug_report_index_ = -1;
+}
+
 bool Hal::InitDebugReportIndex() {
     ATRACE_CALL();
 
@@ -324,32 +361,27 @@
 }
 
 CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+                                     uint32_t icd_api_version,
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(true),
       allocator_(allocator),
+      loader_api_version_(VK_API_VERSION_1_1),
+      icd_api_version_(icd_api_version),
       physical_dev_(VK_NULL_HANDLE),
       instance_info_(create_info),
-      extension_filter_() {
-    // instance core versions need to match the loader api version
-    for (uint32_t i = ProcHook::EXTENSION_CORE_1_0;
-         i != ProcHook::EXTENSION_COUNT; ++i) {
-        hook_extensions_.set(i);
-        hal_extensions_.set(i);
-    }
-}
+      extension_filter_() {}
 
 CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
                                      const VkDeviceCreateInfo& create_info,
+                                     uint32_t icd_api_version,
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(false),
       allocator_(allocator),
+      loader_api_version_(VK_API_VERSION_1_1),
+      icd_api_version_(icd_api_version),
       physical_dev_(physical_dev),
       dev_info_(create_info),
-      extension_filter_() {
-    // initialize with baseline core API version
-    hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
-    hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
-}
+      extension_filter_() {}
 
 CreateInfoWrapper::~CreateInfoWrapper() {
     allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
@@ -357,7 +389,9 @@
 }
 
 VkResult CreateInfoWrapper::Validate() {
-    VkResult result = SanitizePNext();
+    VkResult result = SanitizeApiVersion();
+    if (result == VK_SUCCESS)
+        result = SanitizePNext();
     if (result == VK_SUCCESS)
         result = SanitizeLayers();
     if (result == VK_SUCCESS)
@@ -384,6 +418,22 @@
     return &dev_info_;
 }
 
+VkResult CreateInfoWrapper::SanitizeApiVersion() {
+    if (!is_instance_ || !instance_info_.pApplicationInfo)
+        return VK_SUCCESS;
+
+    if (icd_api_version_ > VK_API_VERSION_1_0 ||
+        instance_info_.pApplicationInfo->apiVersion < VK_API_VERSION_1_1)
+        return VK_SUCCESS;
+
+    // override apiVersion to avoid error return from 1.0 icd
+    application_info_ = *instance_info_.pApplicationInfo;
+    application_info_.apiVersion = VK_API_VERSION_1_0;
+    instance_info_.pApplicationInfo = &application_info_;
+
+    return VK_SUCCESS;
+}
+
 VkResult CreateInfoWrapper::SanitizePNext() {
     const struct StructHeader {
         VkStructureType type;
@@ -431,15 +481,33 @@
                                      : dev_info_.ppEnabledExtensionNames;
     auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
                                      : dev_info_.enabledExtensionCount;
-    if (!ext_count)
-        return VK_SUCCESS;
 
     VkResult result = InitExtensionFilter();
     if (result != VK_SUCCESS)
         return result;
 
-    for (uint32_t i = 0; i < ext_count; i++)
-        FilterExtension(ext_names[i]);
+    if (is_instance_ && icd_api_version_ < loader_api_version_) {
+        for (uint32_t i = 0; i < ext_count; i++) {
+            // Upon api downgrade, skip the promoted instance extensions in the
+            // first pass to avoid duplicate extensions.
+            const std::optional<uint32_t> version =
+                GetInstanceExtensionPromotedVersion(ext_names[i]);
+            if (version && *version > icd_api_version_ &&
+                *version <= loader_api_version_)
+                continue;
+
+            FilterExtension(ext_names[i]);
+        }
+
+        // Enable the required extensions to support core functionalities.
+        const auto promoted_extensions = GetPromotedInstanceExtensions(
+            icd_api_version_, loader_api_version_);
+        for (const auto& promoted_extension : promoted_extensions)
+            FilterExtension(promoted_extension);
+    } else {
+        for (uint32_t i = 0; i < ext_count; i++)
+            FilterExtension(ext_names[i]);
+    }
 
     // Enable device extensions that contain physical-device commands, so that
     // vkGetInstanceProcAddr will return those physical-device commands.
@@ -447,6 +515,23 @@
         hook_extensions_.set(ProcHook::KHR_swapchain);
     }
 
+    const uint32_t api_version =
+        is_instance_ ? loader_api_version_
+                     : std::min(icd_api_version_, loader_api_version_);
+    switch (api_version) {
+        case VK_API_VERSION_1_1:
+            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+            [[clang::fallthrough]];
+        case VK_API_VERSION_1_0:
+            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+            break;
+        default:
+            ALOGE("Unknown API version[%u]", api_version);
+            break;
+    }
+
     ext_names = extension_filter_.names;
     ext_count = extension_filter_.name_count;
 
@@ -504,10 +589,24 @@
     filter.ext_count = count;
 
     // allocate name array
-    uint32_t enabled_ext_count = (is_instance_)
-                                     ? instance_info_.enabledExtensionCount
-                                     : dev_info_.enabledExtensionCount;
-    count = std::min(filter.ext_count, enabled_ext_count);
+    if (is_instance_) {
+        uint32_t enabled_ext_count = instance_info_.enabledExtensionCount;
+
+        // It requires enabling additional promoted extensions to downgrade api,
+        // so we reserve enough space here.
+        if (icd_api_version_ < loader_api_version_) {
+            enabled_ext_count += CountPromotedInstanceExtensions(
+                icd_api_version_, loader_api_version_);
+        }
+
+        count = std::min(filter.ext_count, enabled_ext_count);
+    } else {
+        count = std::min(filter.ext_count, dev_info_.enabledExtensionCount);
+    }
+
+    if (!count)
+        return VK_SUCCESS;
+
     filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
         allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
         VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@@ -535,6 +634,10 @@
                 hook_extensions_.set(ext_bit);
                 break;
             case ProcHook::KHR_get_physical_device_properties2:
+            case ProcHook::KHR_device_group_creation:
+            case ProcHook::KHR_external_memory_capabilities:
+            case ProcHook::KHR_external_semaphore_capabilities:
+            case ProcHook::KHR_external_fence_capabilities:
             case ProcHook::EXTENSION_UNKNOWN:
                 // Extensions we don't need to do anything about at this level
                 break;
@@ -549,6 +652,7 @@
             case ProcHook::GOOGLE_display_timing:
             case ProcHook::EXTENSION_CORE_1_0:
             case ProcHook::EXTENSION_CORE_1_1:
+            case ProcHook::EXTENSION_CORE_1_2:
             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
@@ -591,6 +695,10 @@
 
             case ProcHook::KHR_android_surface:
             case ProcHook::KHR_get_physical_device_properties2:
+            case ProcHook::KHR_device_group_creation:
+            case ProcHook::KHR_external_memory_capabilities:
+            case ProcHook::KHR_external_semaphore_capabilities:
+            case ProcHook::KHR_external_fence_capabilities:
             case ProcHook::KHR_get_surface_capabilities2:
             case ProcHook::KHR_surface:
             case ProcHook::EXT_debug_report:
@@ -598,6 +706,7 @@
             case ProcHook::ANDROID_native_buffer:
             case ProcHook::EXTENSION_CORE_1_0:
             case ProcHook::EXTENSION_CORE_1_1:
+            case ProcHook::EXTENSION_CORE_1_2:
             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
@@ -636,40 +745,6 @@
     }
 }
 
-void CreateInfoWrapper::DowngradeApiVersion() {
-    // If pApplicationInfo is NULL, apiVersion is assumed to be 1.0:
-    if (instance_info_.pApplicationInfo) {
-        application_info_ = *instance_info_.pApplicationInfo;
-        instance_info_.pApplicationInfo = &application_info_;
-        application_info_.apiVersion = VK_API_VERSION_1_0;
-    }
-}
-
-void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) {
-    ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper.");
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
-    api_version ^= VK_VERSION_PATCH(api_version);
-#pragma clang diagnostic pop
-
-    // cap the API version to the loader supported highest version
-    if (api_version > VK_API_VERSION_1_1)
-        api_version = VK_API_VERSION_1_1;
-
-    switch (api_version) {
-        case VK_API_VERSION_1_1:
-            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
-            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
-            [[clang::fallthrough]];
-        case VK_API_VERSION_1_0:
-            break;
-        default:
-            ALOGD("Unknown upgrade API version[%u]", api_version);
-            break;
-    }
-}
-
 VKAPI_ATTR void* DefaultAllocate(void*,
                                  size_t size,
                                  size_t alignment,
@@ -901,21 +976,14 @@
     return result;
 }
 
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
-    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) {
-    const InstanceData& data = GetData(physicalDevice);
-
-    // GPDP2 must be present and enabled on the instance.
-    if (!data.driver.GetPhysicalDeviceProperties2KHR &&
-        !data.driver.GetPhysicalDeviceProperties2)
-        return false;
-
+    VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
     // Request the android-specific presentation properties via GPDP2
-    VkPhysicalDeviceProperties2KHR properties = {
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+    VkPhysicalDeviceProperties2 properties = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
         presentation_properties,
-        {}
+        {},
     };
 
 #pragma clang diagnostic push
@@ -926,14 +994,7 @@
     presentation_properties->pNext = nullptr;
     presentation_properties->sharedImage = VK_FALSE;
 
-    if (data.driver.GetPhysicalDeviceProperties2KHR) {
-        data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
-                                                    &properties);
-    } else {
-        data.driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
-    }
-
-    return true;
+    GetPhysicalDeviceProperties2(physicalDevice, &properties);
 }
 
 VkResult EnumerateDeviceExtensionProperties(
@@ -955,8 +1016,8 @@
     }
 
     VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
-    if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
-        presentation_properties.sharedImage) {
+    QueryPresentationProperties(physicalDevice, &presentation_properties);
+    if (presentation_properties.sharedImage) {
         loader_extensions.push_back({
             VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
             VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
@@ -964,9 +1025,7 @@
 
     // conditionally add VK_GOOGLE_display_timing if present timestamps are
     // supported by the driver:
-    const std::string timestamp_property("service.sf.present_timestamp");
-    android::base::WaitForPropertyCreation(timestamp_property);
-    if (android::base::GetBoolProperty(timestamp_property, true)) {
+    if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
         loader_extensions.push_back({
                 VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
                 VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
@@ -1027,49 +1086,32 @@
     const VkAllocationCallbacks& data_allocator =
         (pAllocator) ? *pAllocator : GetDefaultAllocator();
 
-    CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
-    VkResult result = wrapper.Validate();
-    if (result != VK_SUCCESS)
-        return result;
-
-    ATRACE_BEGIN("AllocateInstanceData");
-    InstanceData* data = AllocateInstanceData(data_allocator);
-    ATRACE_END();
-    if (!data)
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
-
-    data->hook_extensions |= wrapper.GetHookExtensions();
-
-    ATRACE_BEGIN("autoDowngradeApiVersion");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
-    uint32_t api_version = ((pCreateInfo->pApplicationInfo)
-                                ? pCreateInfo->pApplicationInfo->apiVersion
-                                : VK_API_VERSION_1_0);
-    uint32_t api_major_version = VK_VERSION_MAJOR(api_version);
-    uint32_t api_minor_version = VK_VERSION_MINOR(api_version);
-    uint32_t icd_api_version;
+    VkResult result = VK_SUCCESS;
+    uint32_t icd_api_version = VK_API_VERSION_1_0;
     PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
         reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
             Hal::Device().GetInstanceProcAddr(nullptr,
                                               "vkEnumerateInstanceVersion"));
-    if (!pfn_enumerate_instance_version) {
-        icd_api_version = VK_API_VERSION_1_0;
-    } else {
+    if (pfn_enumerate_instance_version) {
         ATRACE_BEGIN("pfn_enumerate_instance_version");
         result = (*pfn_enumerate_instance_version)(&icd_api_version);
         ATRACE_END();
-    }
-    uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version);
-    uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version);
+        if (result != VK_SUCCESS)
+            return result;
 
-    if ((icd_api_major_version == 1) && (icd_api_minor_version == 0) &&
-        ((api_major_version > 1) || (api_minor_version > 0))) {
-        api_version = VK_API_VERSION_1_0;
-        wrapper.DowngradeApiVersion();
+        icd_api_version ^= VK_VERSION_PATCH(icd_api_version);
     }
-#pragma clang diagnostic pop
-    ATRACE_END();
+
+    CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator);
+    result = wrapper.Validate();
+    if (result != VK_SUCCESS)
+        return result;
+
+    InstanceData* data = AllocateInstanceData(data_allocator);
+    if (!data)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    data->hook_extensions |= wrapper.GetHookExtensions();
 
     // call into the driver
     VkInstance instance;
@@ -1133,7 +1175,16 @@
     const VkAllocationCallbacks& data_allocator =
         (pAllocator) ? *pAllocator : instance_data.allocator;
 
-    CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
+    VkPhysicalDeviceProperties properties;
+    ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
+    instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+                                                     &properties);
+    ATRACE_END();
+
+    CreateInfoWrapper wrapper(
+        physicalDevice, *pCreateInfo,
+        properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion),
+        data_allocator);
     VkResult result = wrapper.Validate();
     if (result != VK_SUCCESS)
         return result;
@@ -1145,13 +1196,6 @@
     if (!data)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-    VkPhysicalDeviceProperties properties;
-    ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
-    instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
-                                                     &properties);
-    ATRACE_END();
-
-    wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion);
     data->hook_extensions |= wrapper.GetHookExtensions();
 
     // call into the driver
@@ -1248,7 +1292,8 @@
     VkResult result = VK_SUCCESS;
     const auto& data = GetData(instance);
 
-    if (!data.driver.EnumeratePhysicalDeviceGroups) {
+    if (!data.driver.EnumeratePhysicalDeviceGroups &&
+        !data.driver.EnumeratePhysicalDeviceGroupsKHR) {
         uint32_t device_count = 0;
         result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
         if (result < 0)
@@ -1280,9 +1325,15 @@
             pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
         }
     } else {
-        result = data.driver.EnumeratePhysicalDeviceGroups(
-            instance, pPhysicalDeviceGroupCount,
-            pPhysicalDeviceGroupProperties);
+        if (data.driver.EnumeratePhysicalDeviceGroups) {
+            result = data.driver.EnumeratePhysicalDeviceGroups(
+                instance, pPhysicalDeviceGroupCount,
+                pPhysicalDeviceGroupProperties);
+        } else {
+            result = data.driver.EnumeratePhysicalDeviceGroupsKHR(
+                instance, pPhysicalDeviceGroupCount,
+                pPhysicalDeviceGroupProperties);
+        }
         if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
             *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
             for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
@@ -1323,10 +1374,10 @@
     if (*pQueue != VK_NULL_HANDLE) SetData(*pQueue, data);
 }
 
-VKAPI_ATTR VkResult
-AllocateCommandBuffers(VkDevice device,
-                       const VkCommandBufferAllocateInfo* pAllocateInfo,
-                       VkCommandBuffer* pCommandBuffers) {
+VkResult AllocateCommandBuffers(
+    VkDevice device,
+    const VkCommandBufferAllocateInfo* pAllocateInfo,
+    VkCommandBuffer* pCommandBuffers) {
     ATRACE_CALL();
 
     const auto& data = GetData(device);
@@ -1341,10 +1392,10 @@
     return result;
 }
 
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
-                                uint32_t submitCount,
-                                const VkSubmitInfo* pSubmits,
-                                VkFence fence) {
+VkResult QueueSubmit(VkQueue queue,
+                     uint32_t submitCount,
+                     const VkSubmitInfo* pSubmits,
+                     VkFence fence) {
     ATRACE_CALL();
 
     const auto& data = GetData(queue);
@@ -1352,5 +1403,198 @@
     return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
 }
 
+void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
+                                VkPhysicalDeviceFeatures2* pFeatures) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceFeatures2) {
+        driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+        return;
+    }
+
+    driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+}
+
+void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
+                                  VkPhysicalDeviceProperties2* pProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceProperties2) {
+        driver.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+        return;
+    }
+
+    driver.GetPhysicalDeviceProperties2KHR(physicalDevice, pProperties);
+}
+
+void GetPhysicalDeviceFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    VkFormat format,
+    VkFormatProperties2* pFormatProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceFormatProperties2) {
+        driver.GetPhysicalDeviceFormatProperties2(physicalDevice, format,
+                                                  pFormatProperties);
+        return;
+    }
+
+    driver.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format,
+                                                 pFormatProperties);
+}
+
+VkResult GetPhysicalDeviceImageFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+    VkImageFormatProperties2* pImageFormatProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceImageFormatProperties2) {
+        return driver.GetPhysicalDeviceImageFormatProperties2(
+            physicalDevice, pImageFormatInfo, pImageFormatProperties);
+    }
+
+    return driver.GetPhysicalDeviceImageFormatProperties2KHR(
+        physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+void GetPhysicalDeviceQueueFamilyProperties2(
+    VkPhysicalDevice physicalDevice,
+    uint32_t* pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties2* pQueueFamilyProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceQueueFamilyProperties2) {
+        driver.GetPhysicalDeviceQueueFamilyProperties2(
+            physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+        return;
+    }
+
+    driver.GetPhysicalDeviceQueueFamilyProperties2KHR(
+        physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+void GetPhysicalDeviceMemoryProperties2(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceMemoryProperties2) {
+        driver.GetPhysicalDeviceMemoryProperties2(physicalDevice,
+                                                  pMemoryProperties);
+        return;
+    }
+
+    driver.GetPhysicalDeviceMemoryProperties2KHR(physicalDevice,
+                                                 pMemoryProperties);
+}
+
+void GetPhysicalDeviceSparseImageFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+    uint32_t* pPropertyCount,
+    VkSparseImageFormatProperties2* pProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceSparseImageFormatProperties2) {
+        driver.GetPhysicalDeviceSparseImageFormatProperties2(
+            physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+        return;
+    }
+
+    driver.GetPhysicalDeviceSparseImageFormatProperties2KHR(
+        physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+void GetPhysicalDeviceExternalBufferProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+    VkExternalBufferProperties* pExternalBufferProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceExternalBufferProperties) {
+        driver.GetPhysicalDeviceExternalBufferProperties(
+            physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+        return;
+    }
+
+    if (driver.GetPhysicalDeviceExternalBufferPropertiesKHR) {
+        driver.GetPhysicalDeviceExternalBufferPropertiesKHR(
+            physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+        return;
+    }
+
+    memset(&pExternalBufferProperties->externalMemoryProperties, 0,
+           sizeof(VkExternalMemoryProperties));
+}
+
+void GetPhysicalDeviceExternalSemaphoreProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+    VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceExternalSemaphoreProperties) {
+        driver.GetPhysicalDeviceExternalSemaphoreProperties(
+            physicalDevice, pExternalSemaphoreInfo,
+            pExternalSemaphoreProperties);
+        return;
+    }
+
+    if (driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR) {
+        driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR(
+            physicalDevice, pExternalSemaphoreInfo,
+            pExternalSemaphoreProperties);
+        return;
+    }
+
+    pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+    pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+    pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+}
+
+void GetPhysicalDeviceExternalFenceProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+    VkExternalFenceProperties* pExternalFenceProperties) {
+    ATRACE_CALL();
+
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceExternalFenceProperties) {
+        driver.GetPhysicalDeviceExternalFenceProperties(
+            physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+        return;
+    }
+
+    if (driver.GetPhysicalDeviceExternalFencePropertiesKHR) {
+        driver.GetPhysicalDeviceExternalFencePropertiesKHR(
+            physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+        return;
+    }
+
+    pExternalFenceProperties->exportFromImportedHandleTypes = 0;
+    pExternalFenceProperties->compatibleHandleTypes = 0;
+    pExternalFenceProperties->externalFenceFeatures = 0;
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 23c717c..14c516b 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -103,30 +103,95 @@
 bool OpenHAL();
 const VkAllocationCallbacks& GetDefaultAllocator();
 
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
-    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+    VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
 
-// clang-format off
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-
-VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-
-VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
-// clang-format on
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
+                                                  const char* pName);
+VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
+                                                const char* pName);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+                                     uint32_t* pPropertyCount,
+                                     VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+                                   const char* pLayerName,
+                                   uint32_t* pPropertyCount,
+                                   VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+                                   const VkAllocationCallbacks* pAllocator,
+                                   VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+                                const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+                                 const VkDeviceCreateInfo* pCreateInfo,
+                                 const VkAllocationCallbacks* pAllocator,
+                                 VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+                              const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumeratePhysicalDevices(VkInstance instance,
+                         uint32_t* pPhysicalDeviceCount,
+                         VkPhysicalDevice* pPhysicalDevices);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(
+    VkInstance instance,
+    uint32_t* pPhysicalDeviceGroupCount,
+    VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceQueue(VkDevice device,
+                               uint32_t queueFamilyIndex,
+                               uint32_t queueIndex,
+                               VkQueue* pQueue);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device,
+                                const VkDeviceQueueInfo2* pQueueInfo,
+                                VkQueue* pQueue);
+VKAPI_ATTR VkResult
+AllocateCommandBuffers(VkDevice device,
+                       const VkCommandBufferAllocateInfo* pAllocateInfo,
+                       VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+                                uint32_t submitCount,
+                                const VkSubmitInfo* pSubmits,
+                                VkFence fence);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    VkFormat format,
+    VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+    VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(
+    VkPhysicalDevice physicalDevice,
+    uint32_t* pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+    uint32_t* pPropertyCount,
+    VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+    VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+    VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+    VkExternalFenceProperties* pExternalFenceProperties);
 
 template <typename DispatchableType>
 void StaticAssertDispatchable(DispatchableType) {
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 52205e9..5f37a50 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -363,6 +363,55 @@
         reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
     },
     {
+        "vkGetPhysicalDeviceExternalBufferProperties",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalBufferProperties),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceExternalFenceProperties",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalFenceProperties),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceExternalSemaphoreProperties",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalSemaphoreProperties),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceFeatures2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFeatures2),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceFormatProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFormatProperties2),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceImageFormatProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceImageFormatProperties2),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceMemoryProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceMemoryProperties2),
+        nullptr,
+    },
+    {
         "vkGetPhysicalDevicePresentRectanglesKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_swapchain,
@@ -370,6 +419,27 @@
         nullptr,
     },
     {
+        "vkGetPhysicalDeviceProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceProperties2),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceQueueFamilyProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceQueueFamilyProperties2),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceSparseImageFormatProperties2",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE_1_1,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSparseImageFormatProperties2),
+        nullptr,
+    },
+    {
         "vkGetPhysicalDeviceSurfaceCapabilities2KHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_get_surface_capabilities2,
@@ -480,10 +550,9 @@
 }  // namespace
 
 const ProcHook* GetProcHook(const char* name) {
-    const auto& begin = g_proc_hooks;
-    const auto& end =
-        g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
-    const auto hook = std::lower_bound(
+    auto begin = std::cbegin(g_proc_hooks);
+    auto end = std::cend(g_proc_hooks);
+    auto hook = std::lower_bound(
         begin, end, name,
         [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
     return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -505,6 +574,10 @@
     if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
     if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
     if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
+    if (strcmp(name, "VK_KHR_device_group_creation") == 0) return ProcHook::KHR_device_group_creation;
+    if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities;
+    if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities;
+    if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities;
     // clang-format on
     return ProcHook::EXTENSION_UNKNOWN;
 }
@@ -543,9 +616,28 @@
     INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+    INIT_PROC(false, instance, GetPhysicalDeviceFeatures2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFeatures2KHR);
     INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
     INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFormatProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceImageFormatProperties2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceImageFormatProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceQueueFamilyProperties2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceQueueFamilyProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceMemoryProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceSparseImageFormatProperties2KHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties);
+    INIT_PROC_EXT(KHR_external_memory_capabilities, true, instance, GetPhysicalDeviceExternalBufferPropertiesKHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
+    INIT_PROC_EXT(KHR_external_semaphore_capabilities, true, instance, GetPhysicalDeviceExternalSemaphorePropertiesKHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
+    INIT_PROC_EXT(KHR_external_fence_capabilities, true, instance, GetPhysicalDeviceExternalFencePropertiesKHR);
     INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
+    INIT_PROC_EXT(KHR_device_group_creation, true, instance, EnumeratePhysicalDeviceGroupsKHR);
     // clang-format on
 
     return success;
@@ -577,5 +669,53 @@
     return success;
 }
 
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+    // clang-format off
+    std::make_pair("VK_KHR_device_group_creation", VK_API_VERSION_1_1),
+    std::make_pair("VK_KHR_external_fence_capabilities", VK_API_VERSION_1_1),
+    std::make_pair("VK_KHR_external_memory_capabilities", VK_API_VERSION_1_1),
+    std::make_pair("VK_KHR_external_semaphore_capabilities", VK_API_VERSION_1_1),
+    std::make_pair("VK_KHR_get_physical_device_properties2", VK_API_VERSION_1_1),
+    // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    auto iter =
+        std::lower_bound(begin, end, name,
+                         [](const std::pair<const char*, uint32_t>& e,
+                            const char* n) { return strcmp(e.first, n) < 0; });
+    return (iter < end && strcmp(iter->first, name) == 0)
+               ? std::optional<uint32_t>(iter->second)
+               : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+                                         uint32_t end_version) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    uint32_t count = 0;
+
+    for (auto iter = begin; iter != end; iter++)
+        if (iter->second > begin_version && iter->second <= end_version)
+            count++;
+
+    return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+                                                       uint32_t end_version) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    std::vector<const char*> extensions;
+
+    for (auto iter = begin; iter != end; iter++)
+        if (iter->second > begin_version && iter->second <= end_version)
+            extensions.emplace_back(iter->first);
+
+    return extensions;
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 43c4d14..047e774 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -23,6 +23,8 @@
 #include <vulkan/vulkan.h>
 
 #include <bitset>
+#include <optional>
+#include <vector>
 
 namespace vulkan {
 namespace driver {
@@ -48,9 +50,14 @@
         ANDROID_external_memory_android_hardware_buffer,
         KHR_bind_memory2,
         KHR_get_physical_device_properties2,
+        KHR_device_group_creation,
+        KHR_external_memory_capabilities,
+        KHR_external_semaphore_capabilities,
+        KHR_external_fence_capabilities,
 
         EXTENSION_CORE_1_0,
         EXTENSION_CORE_1_1,
+        EXTENSION_CORE_1_2,
         EXTENSION_COUNT,
         EXTENSION_UNKNOWN,
     };
@@ -74,9 +81,28 @@
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
     PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+    PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2;
+    PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR;
     PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
     PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
+    PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
+    PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR;
+    PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2;
+    PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR;
+    PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2;
+    PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR;
+    PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
+    PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR;
+    PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2;
+    PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR;
+    PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
+    PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR GetPhysicalDeviceExternalBufferPropertiesKHR;
+    PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
+    PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR GetPhysicalDeviceExternalSemaphorePropertiesKHR;
+    PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
+    PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR GetPhysicalDeviceExternalFencePropertiesKHR;
     PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
+    PFN_vkEnumeratePhysicalDeviceGroupsKHR EnumeratePhysicalDeviceGroupsKHR;
     // clang-format on
 };
 
@@ -109,6 +135,12 @@
                      PFN_vkGetDeviceProcAddr get_proc,
                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
 
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+                                         uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+                                                       uint32_t end_version);
+
 }  // namespace driver
 }  // namespace vulkan
 
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 0be66c9..df97d7f 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -12,6 +12,7 @@
     vkBindImageMemory2; # introduced=28
     vkCmdBeginQuery;
     vkCmdBeginRenderPass;
+    vkCmdBeginRenderPass2; # introduced=31
     vkCmdBindDescriptorSets;
     vkCmdBindIndexBuffer;
     vkCmdBindPipeline;
@@ -31,12 +32,16 @@
     vkCmdDraw;
     vkCmdDrawIndexed;
     vkCmdDrawIndexedIndirect;
+    vkCmdDrawIndexedIndirectCount; # introduced=31
     vkCmdDrawIndirect;
+    vkCmdDrawIndirectCount; # introduced=31
     vkCmdEndQuery;
     vkCmdEndRenderPass;
+    vkCmdEndRenderPass2; # introduced=31
     vkCmdExecuteCommands;
     vkCmdFillBuffer;
     vkCmdNextSubpass;
+    vkCmdNextSubpass2; # introduced=31
     vkCmdPipelineBarrier;
     vkCmdPushConstants;
     vkCmdResetEvent;
@@ -76,6 +81,7 @@
     vkCreatePipelineLayout;
     vkCreateQueryPool;
     vkCreateRenderPass;
+    vkCreateRenderPass2; # introduced=31
     vkCreateSampler;
     vkCreateSamplerYcbcrConversion; # introduced=28
     vkCreateSemaphore;
@@ -119,13 +125,16 @@
     vkFreeDescriptorSets;
     vkFreeMemory;
     vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
+    vkGetBufferDeviceAddress; # introduced=31
     vkGetBufferMemoryRequirements;
     vkGetBufferMemoryRequirements2; # introduced=28
+    vkGetBufferOpaqueCaptureAddress; # introduced=31
     vkGetDescriptorSetLayoutSupport; # introduced=28
     vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
     vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
     vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
     vkGetDeviceMemoryCommitment;
+    vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
     vkGetDeviceProcAddr;
     vkGetDeviceQueue;
     vkGetDeviceQueue2; # introduced=28
@@ -163,6 +172,7 @@
     vkGetPipelineCacheData;
     vkGetQueryPoolResults;
     vkGetRenderAreaGranularity;
+    vkGetSemaphoreCounterValue; # introduced=31
     vkGetSwapchainImagesKHR;
     vkInvalidateMappedMemoryRanges;
     vkMapMemory;
@@ -176,12 +186,15 @@
     vkResetDescriptorPool;
     vkResetEvent;
     vkResetFences;
+    vkResetQueryPool; # introduced=31
     vkSetEvent;
+    vkSignalSemaphore; # introduced=31
     vkTrimCommandPool; # introduced=28
     vkUnmapMemory;
     vkUpdateDescriptorSets;
     vkUpdateDescriptorSetWithTemplate; # introduced=28
     vkWaitForFences;
+    vkWaitSemaphores; # introduced=31
   local:
     *;
 };
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c7ff640..48090af 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -258,7 +258,11 @@
     bool shared;
 
     struct Image {
-        Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
+        Image()
+            : image(VK_NULL_HANDLE),
+              dequeue_fence(-1),
+              release_fence(-1),
+              dequeued(false) {}
         VkImage image;
         android::sp<ANativeWindowBuffer> buffer;
         // The fence is only valid when the buffer is dequeued, and should be
@@ -266,6 +270,10 @@
         // closed: either by closing it explicitly when queueing the buffer,
         // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
         int dequeue_fence;
+        // This fence is a dup of the sync fd returned from the driver via
+        // vkQueueSignalReleaseImageANDROID upon vkQueuePresentKHR. We must
+        // ensure it is closed upon re-presenting or releasing the image.
+        int release_fence;
         bool dequeued;
     } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
 
@@ -280,10 +288,19 @@
     return reinterpret_cast<Swapchain*>(handle);
 }
 
+static bool IsFencePending(int fd) {
+    if (fd < 0)
+        return false;
+
+    errno = 0;
+    return sync_wait(fd, 0 /* timeout */) == -1 && errno == ETIME;
+}
+
 void ReleaseSwapchainImage(VkDevice device,
                            ANativeWindow* window,
                            int release_fence,
-                           Swapchain::Image& image) {
+                           Swapchain::Image& image,
+                           bool defer_if_pending) {
     ATRACE_CALL();
 
     ALOG_ASSERT(release_fence == -1 || image.dequeued,
@@ -319,10 +336,18 @@
                 close(release_fence);
             }
         }
-
+        release_fence = -1;
         image.dequeued = false;
     }
 
+    if (defer_if_pending && IsFencePending(image.release_fence))
+        return;
+
+    if (image.release_fence >= 0) {
+        close(image.release_fence);
+        image.release_fence = -1;
+    }
+
     if (image.image) {
         ATRACE_BEGIN("DestroyImage");
         GetData(device).driver.DestroyImage(device, image.image, nullptr);
@@ -338,7 +363,8 @@
         return;
     for (uint32_t i = 0; i < swapchain->num_images; i++) {
         if (!swapchain->images[i].dequeued)
-            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i],
+                                  true);
     }
     swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
     swapchain->timing.clear();
@@ -881,11 +907,10 @@
     present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
 
     VkPhysicalDevicePresentationPropertiesANDROID present_properties;
-    if (QueryPresentationProperties(pdev, &present_properties)) {
-        if (present_properties.sharedImage) {
-            present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
-            present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
-        }
+    QueryPresentationProperties(pdev, &present_properties);
+    if (present_properties.sharedImage) {
+        present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+        present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
     }
 
     uint32_t num_modes = uint32_t(present_modes.size());
@@ -997,7 +1022,7 @@
     }
 
     for (uint32_t i = 0; i < swapchain->num_images; i++) {
-        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+        ReleaseSwapchainImage(device, window, -1, swapchain->images[i], false);
     }
 
     if (active) {
@@ -1629,6 +1654,9 @@
             ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
             swapchain_result = result;
         }
+        if (img.release_fence >= 0)
+            close(img.release_fence);
+        img.release_fence = fence < 0 ? -1 : dup(fence);
 
         if (swapchain.surface.swapchain_handle ==
             present_info->pSwapchains[sc]) {
@@ -1762,7 +1790,7 @@
                     WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
             }
         } else {
-            ReleaseSwapchainImage(device, nullptr, fence, img);
+            ReleaseSwapchainImage(device, nullptr, fence, img, true);
             swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
         }
 
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4647a80..b94233b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1123,6 +1123,14 @@
     return VK_SUCCESS;
 }
 
+VkResult CreateRenderPass2(VkDevice device,
+                           const VkRenderPassCreateInfo2*,
+                           const VkAllocationCallbacks* /*allocator*/,
+                           VkRenderPass* pRenderPass) {
+    *pRenderPass = AllocHandle<VkRenderPass>(device, HandleType::kRenderPass);
+    return VK_SUCCESS;
+}
+
 // -----------------------------------------------------------------------------
 // No-op entrypoints
 
@@ -1568,6 +1576,55 @@
 void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
 }
 
+void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+}
+
+void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return 0;
+}
+
+VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return (VkDeviceAddress)0;
+}
+
+uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return 0;
+}
+
 #pragma clang diagnostic pop
 // clang-format on
 
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index b8d7d2b..edda12c 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -67,6 +67,7 @@
     {"vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkBindImageMemory2>(BindImageMemory2))},
     {"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))},
     {"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))},
@@ -86,12 +87,16 @@
     {"vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDraw>(CmdDraw))},
     {"vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexed>(CmdDrawIndexed))},
     {"vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirect>(CmdDrawIndexedIndirect))},
+    {"vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirectCount>(CmdDrawIndexedIndirectCount))},
     {"vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirect>(CmdDrawIndirect))},
+    {"vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirectCount>(CmdDrawIndirectCount))},
     {"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))},
     {"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))},
     {"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
     {"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
@@ -131,6 +136,7 @@
     {"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))},
     {"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))},
     {"vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSampler>(CreateSampler))},
     {"vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSamplerYcbcrConversion>(CreateSamplerYcbcrConversion))},
     {"vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSemaphore>(CreateSemaphore))},
@@ -172,11 +178,14 @@
     {"vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeCommandBuffers>(FreeCommandBuffers))},
     {"vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeDescriptorSets>(FreeDescriptorSets))},
     {"vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeMemory>(FreeMemory))},
+    {"vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferDeviceAddress>(GetBufferDeviceAddress))},
     {"vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements>(GetBufferMemoryRequirements))},
     {"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))},
     {"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
     {"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))},
     {"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue>(GetDeviceQueue))},
     {"vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue2>(GetDeviceQueue2))},
@@ -215,6 +224,7 @@
     {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
     {"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))},
     {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
     {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
@@ -229,12 +239,15 @@
     {"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetDescriptorPool>(ResetDescriptorPool))},
     {"vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetEvent>(ResetEvent))},
     {"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))},
+    {"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))},
     {"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
     {"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
     {"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
+    {"vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitSemaphores>(WaitSemaphores))},
     // clang-format on
 };
 
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d3f688..e59cae9 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -79,6 +79,7 @@
 VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
 VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
 VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -207,6 +208,18 @@
 VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+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);
 // clang-format on
 
 }  // namespace null_driver
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index a64a702..6a73023 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -40,6 +40,10 @@
     'VK_ANDROID_external_memory_android_hardware_buffer',
     'VK_KHR_bind_memory2',
     'VK_KHR_get_physical_device_properties2',
+    'VK_KHR_device_group_creation',
+    'VK_KHR_external_memory_capabilities',
+    'VK_KHR_external_semaphore_capabilities',
+    'VK_KHR_external_fence_capabilities',
 ]
 
 # Functions needed at vulkan::driver level.
@@ -71,12 +75,41 @@
     'vkDestroyImage',
 
     'vkGetPhysicalDeviceProperties',
-    'vkGetPhysicalDeviceProperties2',
-    'vkGetPhysicalDeviceProperties2KHR',
 
     # VK_KHR_swapchain v69 requirement
     'vkBindImageMemory2',
     'vkBindImageMemory2KHR',
+
+    # For promoted VK_KHR_device_group_creation
+    'vkEnumeratePhysicalDeviceGroupsKHR',
+
+    # For promoted VK_KHR_get_physical_device_properties2
+    'vkGetPhysicalDeviceFeatures2',
+    'vkGetPhysicalDeviceFeatures2KHR',
+    'vkGetPhysicalDeviceProperties2',
+    'vkGetPhysicalDeviceProperties2KHR',
+    'vkGetPhysicalDeviceFormatProperties2',
+    'vkGetPhysicalDeviceFormatProperties2KHR',
+    'vkGetPhysicalDeviceImageFormatProperties2',
+    'vkGetPhysicalDeviceImageFormatProperties2KHR',
+    'vkGetPhysicalDeviceQueueFamilyProperties2',
+    'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
+    'vkGetPhysicalDeviceMemoryProperties2',
+    'vkGetPhysicalDeviceMemoryProperties2KHR',
+    'vkGetPhysicalDeviceSparseImageFormatProperties2',
+    'vkGetPhysicalDeviceSparseImageFormatProperties2KHR',
+
+    # For promoted VK_KHR_external_memory_capabilities
+    'vkGetPhysicalDeviceExternalBufferProperties',
+    'vkGetPhysicalDeviceExternalBufferPropertiesKHR',
+
+    # For promoted VK_KHR_external_semaphore_capabilities
+    'vkGetPhysicalDeviceExternalSemaphoreProperties',
+    'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR',
+
+    # For promoted VK_KHR_external_fence_capabilities
+    'vkGetPhysicalDeviceExternalFenceProperties',
+    'vkGetPhysicalDeviceExternalFencePropertiesKHR',
 ]
 
 # Functions intercepted at vulkan::driver level.
@@ -106,6 +139,24 @@
     # VK_KHR_swapchain v69 requirement
     'vkBindImageMemory2',
     'vkBindImageMemory2KHR',
+
+    # For promoted VK_KHR_get_physical_device_properties2
+    'vkGetPhysicalDeviceFeatures2',
+    'vkGetPhysicalDeviceProperties2',
+    'vkGetPhysicalDeviceFormatProperties2',
+    'vkGetPhysicalDeviceImageFormatProperties2',
+    'vkGetPhysicalDeviceQueueFamilyProperties2',
+    'vkGetPhysicalDeviceMemoryProperties2',
+    'vkGetPhysicalDeviceSparseImageFormatProperties2',
+
+    # For promoted VK_KHR_external_memory_capabilities
+    'vkGetPhysicalDeviceExternalBufferProperties',
+
+    # For promoted VK_KHR_external_semaphore_capabilities
+    'vkGetPhysicalDeviceExternalSemaphoreProperties',
+
+    # For promoted VK_KHR_external_fence_capabilities
+    'vkGetPhysicalDeviceExternalFenceProperties',
 ]
 
 
@@ -162,6 +213,8 @@
 #include <vulkan/vulkan.h>
 
 #include <bitset>
+#include <optional>
+#include <vector>
 
 namespace vulkan {
 namespace driver {
@@ -229,6 +282,12 @@
                      PFN_vkGetDeviceProcAddr get_proc,
                      const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
 
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+                                         uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+                                                       uint32_t end_version);
+
 }  // namespace driver
 }  // namespace vulkan
 
@@ -464,10 +523,9 @@
 }  // namespace
 
 const ProcHook* GetProcHook(const char* name) {
-    const auto& begin = g_proc_hooks;
-    const auto& end =
-        g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
-    const auto hook = std::lower_bound(
+    auto begin = std::cbegin(g_proc_hooks);
+    auto end = std::cend(g_proc_hooks);
+    auto hook = std::lower_bound(
         begin, end, name,
         [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
     return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -539,6 +597,54 @@
     return success;
 }
 
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+    // clang-format off\n""")
+
+    for key, value in sorted(gencom.promoted_inst_ext_dict.items()):
+      f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    auto iter =
+        std::lower_bound(begin, end, name,
+                         [](const std::pair<const char*, uint32_t>& e,
+                            const char* n) { return strcmp(e.first, n) < 0; });
+    return (iter < end && strcmp(iter->first, name) == 0)
+               ? std::optional<uint32_t>(iter->second)
+               : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+                                         uint32_t end_version) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    uint32_t count = 0;
+
+    for (auto iter = begin; iter != end; iter++)
+        if (iter->second > begin_version && iter->second <= end_version)
+            count++;
+
+    return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+                                                       uint32_t end_version) {
+    auto begin = std::cbegin(g_promoted_instance_extensions);
+    auto end = std::cend(g_promoted_instance_extensions);
+    std::vector<const char*> extensions;
+
+    for (auto iter = begin; iter != end; iter++)
+        if (iter->second > begin_version && iter->second <= end_version)
+            extensions.emplace_back(iter->first);
+
+    return extensions;
+}
+
 }  // namespace driver
 }  // namespace vulkan\n""")
 
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index cf370fa..72fd4fb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -22,9 +22,10 @@
 import xml.etree.ElementTree as element_tree
 
 # Extensions unsupported on Android.
-_BLACKLISTED_EXTENSIONS = [
+_BLOCKED_EXTENSIONS = [
     'VK_EXT_acquire_xlib_display',
     'VK_EXT_direct_mode_display',
+    'VK_EXT_directfb_surface',
     'VK_EXT_display_control',
     'VK_EXT_display_surface_counter',
     'VK_EXT_full_screen_exclusive',
@@ -97,6 +98,9 @@
 # Dict for mapping a function to the core Vulkan API version.
 version_dict = {}
 
+# Dict for mapping a promoted instance extension to the core Vulkan API version.
+promoted_inst_ext_dict = {}
+
 
 def indent(num):
   """Returns the requested indents.
@@ -183,6 +187,15 @@
   return version[11:]
 
 
+def version_2_api_version(version):
+  """Returns the api version from a version string.
+
+  Args:
+    version: Vulkan version string.
+  """
+  return 'VK_API' + version[2:]
+
+
 def is_function_supported(cmd):
   """Returns true if a function is core or from a supportable extension.
 
@@ -192,7 +205,7 @@
   if cmd not in extension_dict:
     return True
   else:
-    if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS:
+    if extension_dict[cmd] not in _BLOCKED_EXTENSIONS:
       return True
   return False
 
@@ -302,12 +315,12 @@
   else:
     f.write('INIT_PROC(')
 
-  if name in version_dict and version_dict[name] == 'VK_VERSION_1_1':
+  if name in _OPTIONAL_COMMANDS:
     f.write('false, ')
-  elif name in _OPTIONAL_COMMANDS:
-    f.write('false, ')
-  else:
+  elif version_dict[name] == 'VK_VERSION_1_0':
     f.write('true, ')
+  else:
+    f.write('false, ')
 
   if is_instance_dispatched(name):
     f.write('instance, ')
@@ -327,6 +340,7 @@
   return_type_dict
   version_code_list
   version_dict
+  promoted_inst_ext_dict
   """
   registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
                           'external', 'vulkan-headers', 'registry', 'vk.xml')
@@ -376,9 +390,13 @@
 
   for exts in root.iter('extensions'):
     for extension in exts:
-      apiversion = ''
+      apiversion = 'VK_VERSION_1_0'
       if extension.tag == 'extension':
         extname = extension.get('name')
+        if (extension.get('type') == 'instance' and
+            extension.get('promotedto') is not None):
+          promoted_inst_ext_dict[extname] = \
+              version_2_api_version(extension.get('promotedto'))
         for req in extension:
           if req.get('feature') is not None:
             apiversion = req.get('feature')
@@ -387,8 +405,7 @@
               cmd_name = commands.get('name')
               if cmd_name not in extension_dict:
                 extension_dict[cmd_name] = extname
-                if apiversion:
-                  version_dict[cmd_name] = apiversion
+                version_dict[cmd_name] = apiversion
 
   for feature in root.iter('feature'):
     apiversion = feature.get('name')
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index b0b466c..bfc240e 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -57,9 +57,17 @@
 
 template <typename T> struct EnumTraits;
 template <> struct EnumTraits<VkPhysicalDeviceType> {
-  static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
-  static uint32_t max() { return VK_PHYSICAL_DEVICE_TYPE_END_RANGE; }
-  static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+      case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+      case VK_PHYSICAL_DEVICE_TYPE_CPU:
+        return true;
+    }
+    return false;
+  }
 };
 
 template <> struct EnumTraits<VkFormat> {
@@ -250,6 +258,40 @@
       case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
       case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
       case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+      case VK_FORMAT_G8B8G8R8_422_UNORM:
+      case VK_FORMAT_B8G8R8G8_422_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+      case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
+      case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
+      case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+      case VK_FORMAT_R10X6_UNORM_PACK16:
+      case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
+      case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+      case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
+      case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
+      case VK_FORMAT_R12X4_UNORM_PACK16:
+      case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
+      case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
+      case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
+      case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
+      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
+      case VK_FORMAT_G16B16G16R16_422_UNORM:
+      case VK_FORMAT_B16G16R16G16_422_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
+      case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
+      case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
+      case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
       case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
       case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
       case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
@@ -258,40 +300,22 @@
       case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
       case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
       case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
-      case VK_FORMAT_G8B8G8R8_422_UNORM_KHR:
-      case VK_FORMAT_B8G8R8G8_422_UNORM_KHR:
-      case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR:
-      case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR:
-      case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR:
-      case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR:
-      case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR:
-      case VK_FORMAT_R10X6_UNORM_PACK16_KHR:
-      case VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR:
-      case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR:
-      case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR:
-      case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR:
-      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR:
-      case VK_FORMAT_R12X4_UNORM_PACK16_KHR:
-      case VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR:
-      case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR:
-      case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR:
-      case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR:
-      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR:
-      case VK_FORMAT_G16B16G16R16_422_UNORM_KHR:
-      case VK_FORMAT_B16G16R16G16_422_UNORM_KHR:
-      case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR:
-      case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR:
-      case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR:
-      case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR:
-      case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR:
+      case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
+      case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
+      case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
         return true;
     }
     return false;
@@ -300,9 +324,14 @@
 
 template <>
 struct EnumTraits<VkPointClippingBehavior> {
-  static uint32_t min() { return VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE; }
-  static uint32_t max() { return VK_POINT_CLIPPING_BEHAVIOR_END_RANGE; }
-  static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
+      case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
+        return true;
+    }
+    return false;
+  }
 };
 
 template <>
@@ -336,9 +365,26 @@
 
 template <>
 struct EnumTraits<VkDriverIdKHR> {
-  static uint32_t min() { return VK_DRIVER_ID_BEGIN_RANGE_KHR; }
-  static uint32_t max() { return VK_DRIVER_ID_END_RANGE_KHR; }
-  static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_DRIVER_ID_AMD_PROPRIETARY:
+      case VK_DRIVER_ID_AMD_OPEN_SOURCE:
+      case VK_DRIVER_ID_MESA_RADV:
+      case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+      case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+      case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
+      case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
+      case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
+      case VK_DRIVER_ID_ARM_PROPRIETARY:
+      case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
+      case VK_DRIVER_ID_GGP_PROPRIETARY:
+      case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
+      case VK_DRIVER_ID_MESA_LLVMPIPE:
+      case VK_DRIVER_ID_MOLTENVK:
+        return true;
+    }
+    return false;
+  }
 };
 
 // VkSparseImageFormatProperties
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index a283b83..52e7bee 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -158,10 +158,7 @@
                             VkJsonInstance* instance,
                             std::string* errors);
 
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
-                             VkPhysicalDevice device,
-                             uint32_t instanceExtensionCount,
-                             const char* const* instanceExtensions);
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device);
 std::string VkJsonDeviceToJson(const VkJsonDevice& device);
 bool VkJsonDeviceFromJson(const std::string& json,
                           VkJsonDevice* device,
@@ -177,7 +174,7 @@
 typedef VkJsonDevice VkJsonAllProperties;
 inline VkJsonAllProperties VkJsonGetAllProperties(
     VkPhysicalDevice physicalDevice) {
-  return VkJsonGetDevice(VK_NULL_HANDLE, physicalDevice, 0, nullptr);
+  return VkJsonGetDevice(physicalDevice);
 }
 inline std::string VkJsonAllPropertiesToJson(
     const VkJsonAllProperties& properties) {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 84cfe5e..eb0fcc3 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -28,8 +28,6 @@
 #include <utility>
 
 namespace {
-const char* kSupportedInstanceExtensions[] = {
-    "VK_KHR_get_physical_device_properties2"};
 
 bool EnumerateExtensions(const char* layer_name,
                          std::vector<VkExtensionProperties>* extensions) {
@@ -47,15 +45,6 @@
 }
 
 bool HasExtension(const char* extension_name,
-                  uint32_t count,
-                  const char* const* extensions) {
-  return std::find_if(extensions, extensions + count,
-                      [extension_name](const char* extension) {
-                        return strcmp(extension, extension_name) == 0;
-                      }) != extensions + count;
-}
-
-bool HasExtension(const char* extension_name,
                   const std::vector<VkExtensionProperties>& extensions) {
   return std::find_if(extensions.cbegin(), extensions.cend(),
                       [extension_name](const VkExtensionProperties& extension) {
@@ -65,27 +54,9 @@
 }
 }  // anonymous namespace
 
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
-                             VkPhysicalDevice physical_device,
-                             uint32_t instance_extension_count,
-                             const char* const* instance_extensions) {
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) {
   VkJsonDevice device;
 
-  PFN_vkGetPhysicalDeviceProperties2KHR vkpGetPhysicalDeviceProperties2KHR =
-      nullptr;
-  PFN_vkGetPhysicalDeviceFeatures2KHR vkpGetPhysicalDeviceFeatures2KHR =
-      nullptr;
-  if (instance != VK_NULL_HANDLE &&
-      HasExtension("VK_KHR_get_physical_device_properties2",
-                   instance_extension_count, instance_extensions)) {
-    vkpGetPhysicalDeviceProperties2KHR =
-        reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
-            vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"));
-    vkpGetPhysicalDeviceFeatures2KHR =
-        reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
-            vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR"));
-  }
-
   uint32_t extension_count = 0;
   vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
                                        &extension_count, nullptr);
@@ -103,55 +74,48 @@
                                      device.layers.data());
   }
 
-  if (HasExtension("VK_KHR_get_physical_device_properties2",
-                   instance_extension_count, instance_extensions)) {
-    VkPhysicalDeviceProperties2KHR properties = {
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
-        nullptr,
-        {} // properties
-    };
-    if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
-      device.ext_driver_properties.reported = true;
-      device.ext_driver_properties.driver_properties_khr.sType =
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
-      device.ext_driver_properties.driver_properties_khr.pNext =
-          properties.pNext;
-      properties.pNext =
-          &device.ext_driver_properties.driver_properties_khr;
-    }
-    vkpGetPhysicalDeviceProperties2KHR(physical_device, &properties);
-    device.properties = properties.properties;
-
-    VkPhysicalDeviceFeatures2KHR features = {
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
-        nullptr,
-        {}  // features
-    };
-    if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
-      device.ext_variable_pointer_features.reported = true;
-      device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
-      device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
-          features.pNext;
-      features.pNext =
-          &device.ext_variable_pointer_features.variable_pointer_features_khr;
-    }
-    if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
-      device.ext_shader_float16_int8_features.reported = true;
-      device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
-          .sType =
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
-      device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
-          .pNext = features.pNext;
-      features.pNext = &device.ext_shader_float16_int8_features
-                            .shader_float16_int8_features_khr;
-    }
-    vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
-    device.features = features.features;
-  } else {
-    vkGetPhysicalDeviceProperties(physical_device, &device.properties);
-    vkGetPhysicalDeviceFeatures(physical_device, &device.features);
+  VkPhysicalDeviceProperties2 properties = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+      nullptr,
+      {},
+  };
+  if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
+    device.ext_driver_properties.reported = true;
+    device.ext_driver_properties.driver_properties_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
+    device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext;
+    properties.pNext = &device.ext_driver_properties.driver_properties_khr;
   }
+  vkGetPhysicalDeviceProperties2(physical_device, &properties);
+  device.properties = properties.properties;
+
+  VkPhysicalDeviceFeatures2 features = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+      nullptr,
+      {},
+  };
+  if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
+    device.ext_variable_pointer_features.reported = true;
+    device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
+    device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
+        features.pNext;
+    features.pNext =
+        &device.ext_variable_pointer_features.variable_pointer_features_khr;
+  }
+  if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+    device.ext_shader_float16_int8_features.reported = true;
+    device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+        .sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+    device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+        .pNext = features.pNext;
+    features.pNext = &device.ext_shader_float16_int8_features
+                          .shader_float16_int8_features_khr;
+  }
+  vkGetPhysicalDeviceFeatures2(physical_device, &features);
+  device.features = features.features;
+
   vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory);
 
   uint32_t queue_family_count = 0;
@@ -165,7 +129,9 @@
 
   VkFormatProperties format_properties = {};
   for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8;
-       format <= VK_FORMAT_END_RANGE;
+       // TODO(http://b/171403054): avoid hard-coding last value in the
+       // contiguous range
+       format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
        format = static_cast<VkFormat>(format + 1)) {
     vkGetPhysicalDeviceFormatProperties(physical_device, format,
                                         &format_properties);
@@ -178,6 +144,8 @@
 
   if (device.properties.apiVersion >= VK_API_VERSION_1_1) {
     for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM;
+         // TODO(http://b/171403054): avoid hard-coding last value in the
+         // contiguous range
          format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
          format = static_cast<VkFormat>(format + 1)) {
       vkGetPhysicalDeviceFormatProperties(physical_device, format,
@@ -189,135 +157,117 @@
       }
     }
 
-    PFN_vkGetPhysicalDeviceProperties2 vkpGetPhysicalDeviceProperties2 =
-        reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
-            vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"));
-    if (vkpGetPhysicalDeviceProperties2) {
-      VkPhysicalDeviceProperties2 properties2 = {
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}};
+    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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+    device.subgroup_properties.pNext = properties2.pNext;
+    properties2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
+    device.point_clipping_properties.pNext = properties2.pNext;
+    properties2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+    device.multiview_properties.pNext = properties2.pNext;
+    properties2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
+    device.id_properties.pNext = properties2.pNext;
+    properties2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
+    device.maintenance3_properties.pNext = properties2.pNext;
+    properties2.pNext = &device.maintenance3_properties;
 
-      (*vkpGetPhysicalDeviceProperties2)(physical_device, &properties2);
-    }
+    vkGetPhysicalDeviceProperties2(physical_device, &properties2);
 
-    PFN_vkGetPhysicalDeviceFeatures2 vkpGetPhysicalDeviceFeatures2 =
-        reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
-            vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2"));
-    if (vkpGetPhysicalDeviceFeatures2) {
-      VkPhysicalDeviceFeatures2 features2 = {
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}};
+    VkPhysicalDeviceFeatures2 features2 = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+        nullptr,
+        {},
+    };
 
-      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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+    device.bit16_storage_features.pNext = features2.pNext;
+    features2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+    device.multiview_features.pNext = features2.pNext;
+    features2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+    device.variable_pointer_features.pNext = features2.pNext;
+    features2.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.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+    device.protected_memory_features.pNext = features2.pNext;
+    features2.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.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.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.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;
 
-      (*vkpGetPhysicalDeviceFeatures2)(physical_device, &features2);
-    }
+    vkGetPhysicalDeviceFeatures2(physical_device, &features2);
 
-    PFN_vkGetPhysicalDeviceExternalFenceProperties
-        vkpGetPhysicalDeviceExternalFenceProperties =
-            reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(
-                vkGetInstanceProcAddr(
-                    instance, "vkGetPhysicalDeviceExternalFenceProperties"));
-    if (vkpGetPhysicalDeviceExternalFenceProperties) {
-      VkPhysicalDeviceExternalFenceInfo external_fence_info = {
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
-          VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
-      VkExternalFenceProperties external_fence_properties = {};
+    VkPhysicalDeviceExternalFenceInfo external_fence_info = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
+        VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
+    VkExternalFenceProperties external_fence_properties = {};
 
-      for (VkExternalFenceHandleTypeFlagBits handle_type =
-               VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
-           handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
-           handle_type = static_cast<VkExternalFenceHandleTypeFlagBits>(
-               handle_type << 1)) {
-        external_fence_info.handleType = handle_type;
-        (*vkpGetPhysicalDeviceExternalFenceProperties)(
-            physical_device, &external_fence_info, &external_fence_properties);
-        if (external_fence_properties.exportFromImportedHandleTypes ||
-            external_fence_properties.compatibleHandleTypes ||
-            external_fence_properties.externalFenceFeatures) {
-          device.external_fence_properties.insert(
-              std::make_pair(handle_type, external_fence_properties));
-        }
+    for (VkExternalFenceHandleTypeFlagBits handle_type =
+             VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
+         handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+         handle_type =
+             static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) {
+      external_fence_info.handleType = handle_type;
+      vkGetPhysicalDeviceExternalFenceProperties(
+          physical_device, &external_fence_info, &external_fence_properties);
+      if (external_fence_properties.exportFromImportedHandleTypes ||
+          external_fence_properties.compatibleHandleTypes ||
+          external_fence_properties.externalFenceFeatures) {
+        device.external_fence_properties.insert(
+            std::make_pair(handle_type, external_fence_properties));
       }
     }
 
-    PFN_vkGetPhysicalDeviceExternalSemaphoreProperties
-        vkpGetPhysicalDeviceExternalSemaphoreProperties = reinterpret_cast<
-            PFN_vkGetPhysicalDeviceExternalSemaphoreProperties>(
-            vkGetInstanceProcAddr(
-                instance, "vkGetPhysicalDeviceExternalSemaphoreProperties"));
-    if (vkpGetPhysicalDeviceExternalSemaphoreProperties) {
-      VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
-          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
-          VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
-      VkExternalSemaphoreProperties external_semaphore_properties = {};
+    VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
+        VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
+    VkExternalSemaphoreProperties external_semaphore_properties = {};
 
-      for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
-               VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
-           handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-           handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
-               handle_type << 1)) {
-        external_semaphore_info.handleType = handle_type;
-        (*vkpGetPhysicalDeviceExternalSemaphoreProperties)(
-            physical_device, &external_semaphore_info,
-            &external_semaphore_properties);
-        if (external_semaphore_properties.exportFromImportedHandleTypes ||
-            external_semaphore_properties.compatibleHandleTypes ||
-            external_semaphore_properties.externalSemaphoreFeatures) {
-          device.external_semaphore_properties.insert(
-              std::make_pair(handle_type, external_semaphore_properties));
-        }
+    for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
+             VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+         handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+         handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
+             handle_type << 1)) {
+      external_semaphore_info.handleType = handle_type;
+      vkGetPhysicalDeviceExternalSemaphoreProperties(
+          physical_device, &external_semaphore_info,
+          &external_semaphore_properties);
+      if (external_semaphore_properties.exportFromImportedHandleTypes ||
+          external_semaphore_properties.compatibleHandleTypes ||
+          external_semaphore_properties.externalSemaphoreFeatures) {
+        device.external_semaphore_properties.insert(
+            std::make_pair(handle_type, external_semaphore_properties));
       }
     }
   }
@@ -351,19 +301,15 @@
   if (!EnumerateExtensions(nullptr, &instance.extensions))
     return VkJsonInstance();
 
-  std::vector<const char*> instance_extensions;
-  for (const auto extension : kSupportedInstanceExtensions) {
-    if (HasExtension(extension, instance.extensions))
-      instance_extensions.push_back(extension);
-  }
-
-  const VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO,
-                                      nullptr,
-                                      "vkjson_info",
-                                      1,
-                                      "",
-                                      0,
-                                      VK_API_VERSION_1_1};
+  const VkApplicationInfo app_info = {
+      VK_STRUCTURE_TYPE_APPLICATION_INFO,
+      nullptr,
+      "vkjson_info",
+      1,
+      "",
+      0,
+      VK_API_VERSION_1_1,
+  };
   VkInstanceCreateInfo instance_info = {
       VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
       nullptr,
@@ -371,8 +317,9 @@
       &app_info,
       0,
       nullptr,
-      static_cast<uint32_t>(instance_extensions.size()),
-      instance_extensions.data()};
+      0,
+      nullptr,
+  };
   VkInstance vkinstance;
   result = vkCreateInstance(&instance_info, nullptr, &vkinstance);
   if (result != VK_SUCCESS)
@@ -397,52 +344,38 @@
   instance.devices.reserve(sz);
   for (uint32_t i = 0; i < sz; ++i) {
     device_map.insert(std::make_pair(devices[i], i));
-    instance.devices.emplace_back(VkJsonGetDevice(vkinstance, devices[i],
-                                                  instance_extensions.size(),
-                                                  instance_extensions.data()));
+    instance.devices.emplace_back(VkJsonGetDevice(devices[i]));
   }
 
-  PFN_vkEnumerateInstanceVersion vkpEnumerateInstanceVersion =
-      reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
-          vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
-  if (!vkpEnumerateInstanceVersion) {
-    instance.api_version = VK_API_VERSION_1_0;
-  } else {
-    result = (*vkpEnumerateInstanceVersion)(&instance.api_version);
-    if (result != VK_SUCCESS) {
-      vkDestroyInstance(vkinstance, nullptr);
-      return VkJsonInstance();
-    }
+  result = vkEnumerateInstanceVersion(&instance.api_version);
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
   }
 
-  PFN_vkEnumeratePhysicalDeviceGroups vkpEnumeratePhysicalDeviceGroups =
-      reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroups>(
-          vkGetInstanceProcAddr(vkinstance, "vkEnumeratePhysicalDeviceGroups"));
-  if (vkpEnumeratePhysicalDeviceGroups) {
-    count = 0;
-    result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, nullptr);
-    if (result != VK_SUCCESS) {
-      vkDestroyInstance(vkinstance, nullptr);
-      return VkJsonInstance();
-    }
+  count = 0;
+  result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr);
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
 
-    VkJsonDeviceGroup device_group;
-    std::vector<VkPhysicalDeviceGroupProperties> group_properties;
-    group_properties.resize(count);
-    result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count,
-                                                 group_properties.data());
-    if (result != VK_SUCCESS) {
-      vkDestroyInstance(vkinstance, nullptr);
-      return VkJsonInstance();
+  VkJsonDeviceGroup device_group;
+  std::vector<VkPhysicalDeviceGroupProperties> group_properties;
+  group_properties.resize(count);
+  result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
+                                           group_properties.data());
+  if (result != VK_SUCCESS) {
+    vkDestroyInstance(vkinstance, nullptr);
+    return VkJsonInstance();
+  }
+  for (auto properties : group_properties) {
+    device_group.properties = properties;
+    for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
+      device_group.device_inds.push_back(
+          device_map[properties.physicalDevices[i]]);
     }
-    for (auto properties : group_properties) {
-      device_group.properties = properties;
-      for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
-        device_group.device_inds.push_back(
-            device_map[properties.physicalDevices[i]]);
-      }
-      instance.device_groups.push_back(device_group);
-    }
+    instance.device_groups.push_back(device_group);
   }
 
   vkDestroyInstance(vkinstance, nullptr);