Revert "SurfaceFlinger: refresh rate calculation when no timestamps" am: b28c5cc186 am: bec285a0d3

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

Change-Id: Iff1da78c809459e1e8968e8caa9ea6d86aeb878c
diff --git a/.gitignore b/.gitignore
index 0d20b64..685e379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.iml
 *.pyc
+.idea/
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0473bb8..a686dfb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
 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/binderthreadstate/
@@ -18,7 +19,9 @@
                opengl/libs/
                services/bufferhub/
                services/inputflinger/
+               services/powermanager/
                services/surfaceflinger/
+               services/vibratorservice/
                services/vr/
                vulkan/
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8173c89..5db44c7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -58,6 +58,14 @@
     },
     {
       "name": "libsurfaceflinger_unittest"
+    },
+    {
+      "name": "CtsGraphicsTestCases",
+      "options": [
+        {
+          "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+        }
+      ]
     }
   ]
 }
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..2519ffa 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -124,6 +124,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 +173,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" },
@@ -239,6 +242,10 @@
         { OPT,      "events/kmem/ion_heap_shrink/enable" },
         { OPT,      "events/ion/ion_stat/enable" },
     } },
+    { "thermal",  "Thermal event", 0, {
+        { REQ,      "events/thermal/thermal_temperature/enable" },
+        { OPT,      "events/thermal/cdev_update/enable" },
+    } },
 };
 
 struct TracingVendorCategory {
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 9b2f4a8..994375b 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,8 @@
     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
 
     # disk
     chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
@@ -164,6 +170,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..1f055f3 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,34 +105,26 @@
     name: "dumpstate",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.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 +133,7 @@
     name: "dumpstate_test",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.cpp",
         "dumpstate.cpp",
         "tests/dumpstate_test.cpp",
     ],
@@ -157,10 +150,13 @@
     name: "dumpstate_smoke_test",
     defaults: ["dumpstate_defaults"],
     srcs: [
+        "DumpPool.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..e174c8e
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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::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..a4ea875
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,201 @@
+/*
+ * 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);
+
+    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/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/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9ba4819..7d195b4 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,6 +114,7 @@
 using android::os::IDumpstateListener;
 using android::os::dumpstate::CommandOptions;
 using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
 using android::os::dumpstate::PropertiesHelper;
 
 // Keep in sync with
@@ -195,8 +198,26 @@
     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();
+
 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";
+
 namespace android {
 namespace os {
 namespace {
@@ -209,6 +230,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,6 +299,27 @@
     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
@@ -305,8 +351,9 @@
 
 /*
  * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
- * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
- * is set, the vector only contains files that were written in the last 30 minutes.
+ * The returned vector is sorted by the mtimes of the dumps with descending
+ * order. If |limit_by_mtime| is set, the vector only contains files that
+ * were written in the last 30 minutes.
  */
 static std::vector<DumpData> GetDumpFds(const std::string& dir_path,
                                         const std::string& file_prefix,
@@ -353,6 +400,10 @@
 
         dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime});
     }
+    if (!dump_data.empty()) {
+        std::sort(dump_data.begin(), dump_data.end(),
+            [](const auto& a, const auto& b) { return a.mtime > b.mtime; });
+    }
 
     return dump_data;
 }
@@ -631,14 +682,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;
 }
@@ -721,8 +782,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");
 }
 
@@ -1266,12 +1328,12 @@
 
 static void DumpHals() {
     if (!ds.IsZipping()) {
-        RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
+        RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
                    CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
         return;
     }
     DurationReporter duration_reporter("DUMP HALS");
-    RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
+    RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
                CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
 
     using android::hidl::manager::V1_0::IServiceManager;
@@ -1362,6 +1424,46 @@
     printf("\n");
 }
 
+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 networking logs). See b/136273873 and b/138459828
+    // for context.
+    DurationReporter duration_reporter("DUMPSTATE");
+    unsigned long timeout_ms;
+    // calculate timeout
+    timeout_ms = logcat_timeout({"main", "system", "crash"});
+    RunCommand("SYSTEM LOG",
+               {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    timeout_ms = logcat_timeout({"events"});
+    RunCommand(
+        "EVENT LOG",
+        {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+
+    printf("========================================================\n");
+    printf("== Networking Service\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
+               CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+
+    printf("========================================================\n");
+    printf("== Dropbox crashes\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
+    RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
+
+    printf("========================================================\n");
+    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
+    printf("========================================================\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
+    printf("========================================================\n");
+}
+
 // 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
@@ -1411,8 +1513,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();
@@ -1600,7 +1701,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());
@@ -1643,6 +1755,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;
     }
@@ -1801,8 +1922,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]);
@@ -2047,11 +2166,12 @@
 
 static void ShowUsage() {
     fprintf(stderr,
-            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
-            "[-z] [-s] [-S] [-q] [-P] [-R] [-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"
@@ -2061,6 +2181,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"
+            "  -L: output limited information that is safe for submission in feedback reports\n"
             "  -v: prints the dumpstate header and exit\n");
 }
 
@@ -2221,6 +2342,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_,
@@ -2228,7 +2356,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());
         }
     }
 }
@@ -2306,13 +2434,12 @@
         "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 "
         "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
-        "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.do_progress_updates,
-        options.bugreport_fd.get(), options.bugreport_mode.c_str(),
-        toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
+        options.do_start_service, 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.limited_only, options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2334,11 +2461,12 @@
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
     RunStatus status = RunStatus::OK;
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBRSV: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;
@@ -2346,6 +2474,7 @@
             case 'p': do_screenshot = true;          break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
+            case 'L': limited_only = true;           break;
             case 'V':                                break;  // compatibility no-op
             case 'w':
                 // This was already processed
@@ -2459,11 +2588,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");
@@ -2625,11 +2755,19 @@
     // 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(b/158737089) reduce code repetition in if branches
     if (options_->telephony_only) {
         MaybeTakeEarlyScreenshot();
         onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2641,6 +2779,11 @@
         onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
+    } else if (options_->limited_only) {
+        MaybeTakeEarlyScreenshot();
+        onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+        MaybeCheckUserConsent(calling_uid, calling_package);
+        DumpstateLimitedOnly();
     } else {
         // Invoke critical dumpsys first to preserve system state, before doing anything else.
         RunDumpsysCritical();
@@ -2785,6 +2928,23 @@
     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_);
+}
+
+void Dumpstate::ShutdownDumpPool() {
+    if (dump_pool_) {
+        dump_pool_->shutdown();
+        dump_pool_ = nullptr;
+    }
+}
+
 Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
     MYLOGD("User denied consent; deleting files and returning\n");
     CleanupTmpFiles();
@@ -2903,8 +3063,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();
     }
@@ -2918,7 +3079,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());
         }
     }
 }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 28d8936..d400dc7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,7 @@
 #include <ziparchive/zip_writer.h>
 
 #include "DumpstateUtil.h"
+#include "DumpPool.h"
 
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
@@ -75,7 +76,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 +85,7 @@
     bool logcat_only_;
     bool verbose_;
     uint64_t started_;
+    int duration_fd_;
 
     DISALLOW_COPY_AND_ASSIGN(DurationReporter);
 };
@@ -193,7 +195,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 };
@@ -377,6 +379,8 @@
         bool do_start_service = false;
         bool telephony_only = false;
         bool wifi_only = false;
+        // Trimmed-down version of dumpstate to only include whitelisted logs.
+        bool limited_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
         // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
@@ -384,10 +388,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
@@ -413,6 +419,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
@@ -480,6 +492,9 @@
     // 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 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.
@@ -518,6 +533,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
diff --git a/libs/binder/ndk/test/AndroidTest.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
similarity index 70%
rename from libs/binder/ndk/test/AndroidTest.xml
rename to cmds/dumpstate/dumpstate_smoke_test.xml
index 89646f7..0aff200 100644
--- a/libs/binder/ndk/test/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 68d3733..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, "wdho:svqzpPBRSV:")) != -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 6f2d754..bb0e5ad 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -209,13 +209,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));
@@ -236,20 +235,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, Is3MBMBinSize) {
     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 */);
 }
 
-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.";
 }
 
@@ -266,7 +265,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);
@@ -285,7 +285,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) {
@@ -301,8 +301,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) {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 9871a3b..b3cb434 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;
@@ -95,6 +97,10 @@
         PropertiesHelper::unroot_ = unroot;
     }
 
+    void SetParallelRun(bool parallel_run) const {
+        PropertiesHelper::parallel_run_ = parallel_run;
+    }
+
     bool IsStandalone() const {
         return calls_ == 1;
     }
@@ -172,6 +178,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);
@@ -179,6 +186,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -206,6 +214,7 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -231,6 +240,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -249,6 +259,7 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.do_start_service);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
@@ -266,6 +277,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
@@ -282,6 +294,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
@@ -299,6 +312,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
@@ -316,6 +330,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
@@ -333,6 +348,39 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.limited_only);
+}
+
+TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) {
+    // clang-format off
+    char* argv[] = {
+        const_cast<char*>("dumpstatez"),
+        const_cast<char*>("-S"),
+        const_cast<char*>("-d"),
+        const_cast<char*>("-z"),
+        const_cast<char*>("-q"),
+        const_cast<char*>("-L"),
+        const_cast<char*>("-o abc")
+    };
+    // clang-format on
+
+    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
+
+    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    EXPECT_TRUE(options_.do_add_date);
+    EXPECT_TRUE(options_.do_zip_file);
+    EXPECT_TRUE(options_.use_control_socket);
+    EXPECT_FALSE(options_.do_vibrate);
+    EXPECT_TRUE(options_.limited_only);
+    EXPECT_EQ(" abc", std::string(options_.out_dir));
+
+    // Other options retain default values
+    EXPECT_FALSE(options_.show_header_only);
+    EXPECT_FALSE(options_.do_screenshot);
+    EXPECT_FALSE(options_.do_progress_updates);
+    EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.use_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeDefaultBugReport) {
@@ -361,6 +409,7 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.wifi_only);
+    EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial1) {
@@ -390,6 +439,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -419,6 +469,7 @@
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -497,6 +548,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) {
@@ -524,6 +579,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";
@@ -962,6 +1021,27 @@
     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.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+    ds.options_->use_socket = true;
+    EnableParallelRunIfNeeded();
+    EXPECT_FALSE(ds.options_->OutputToFile());
+    EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+    SetParallelRun(false);
+    EnableParallelRunIfNeeded();
+    EXPECT_FALSE(ds.dump_pool_);
+}
+
 class DumpstateServiceTest : public DumpstateBaseTest {
   public:
     DumpstateService dss;
@@ -1573,6 +1653,103 @@
     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));
+}
+
+
 }  // namespace dumpstate
 }  // namespace os
 }  // namespace android
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a427c8d..1327cfd 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -230,7 +230,7 @@
     }
 
     const size_t N = services.size();
-    if (N > 1) {
+    if (N > 1 || showListOnly) {
         // first print a list of the current services
         std::cout << "Currently running services:" << std::endl;
 
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index b9395ba..3467898 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -206,10 +206,7 @@
     }
 
     void AssertRunningServices(const std::vector<std::string>& services) {
-        std::string expected;
-        if (services.size() > 1) {
-            expected.append("Currently running services:\n");
-        }
+        std::string expected = "Currently running services:\n";
         for (const std::string& service : services) {
             expected.append("  ").append(service).append("\n");
         }
@@ -263,6 +260,21 @@
     AssertRunningServices({"Locksmith", "Valet"});
 }
 
+TEST_F(DumpsysTest, ListServicesOneRegistered) {
+    ExpectListServices({"Locksmith"});
+    ExpectCheckService("Locksmith");
+
+    CallMain({"-l"});
+
+    AssertRunningServices({"Locksmith"});
+}
+
+TEST_F(DumpsysTest, ListServicesEmpty) {
+    CallMain({"-l"});
+
+    AssertRunningServices({});
+}
+
 // Tests 'dumpsys -l' when a service is not running
 TEST_F(DumpsysTest, ListRunningServices) {
     ExpectListServices({"Locksmith", "Valet"});
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 402767a..64bfdf9 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -37,10 +37,16 @@
     defaults: ["idlcli-defaults"],
     srcs: [
         "CommandVibrator.cpp",
+        "vibrator/CommandAlwaysOnDisable.cpp",
+        "vibrator/CommandAlwaysOnEnable.cpp",
         "vibrator/CommandCompose.cpp",
         "vibrator/CommandGetCapabilities.cpp",
         "vibrator/CommandGetCompositionDelayMax.cpp",
         "vibrator/CommandGetCompositionSizeMax.cpp",
+        "vibrator/CommandGetPrimitiveDuration.cpp",
+        "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+        "vibrator/CommandGetSupportedEffects.cpp",
+        "vibrator/CommandGetSupportedPrimitives.cpp",
         "vibrator/CommandOff.cpp",
         "vibrator/CommandOn.cpp",
         "vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index a8e5954..b874455 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -17,6 +17,7 @@
 #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
 
+#include <android/binder_enums.h>
 #include <hidl/HidlSupport.h>
 
 #include <iomanip>
@@ -66,7 +67,7 @@
 
 } // namespace overrides
 
-template <typename T, typename R = hardware::hidl_enum_range<T>>
+template <typename T, typename R = ndk::enum_range<T>>
 inline std::istream &operator>>(std::istream &stream, T &out) {
     using overrides::operator>>;
     auto validRange = R();
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..6c30a9e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -16,8 +16,12 @@
 #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
 #include <aidl/android/hardware/vibrator/IVibrator.h>
 #include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 
 #include "utils.h"
@@ -101,6 +105,18 @@
 namespace V1_3 = ::android::hardware::vibrator::V1_3;
 namespace aidl = ::aidl::android::hardware::vibrator;
 
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+    ndk::ScopedAStatus onComplete() override {
+        mPromise.set_value();
+        return ndk::ScopedAStatus::ok();
+    }
+    void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+    std::promise<void> mPromise;
+};
+
 } // namespace vibrator
 } // namespace idlcli
 
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
new file mode 100644
index 0000000..9afa300
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandAlwaysOnDisable : public Command {
+    std::string getDescription() const override { return "Disarm always-on haptic source."; }
+
+    std::string getUsageSummary() const override { return "<id>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<id>", {"Source ID (device-specific)."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto id = args.pop<decltype(mId)>()) {
+            mId = *id;
+            std::cout << "Source ID: " << mId << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Source ID!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
+
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+
+        return ret;
+    }
+
+    int32_t mId;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
new file mode 100644
index 0000000..bb7f9f2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+using aidl::EffectStrength;
+
+class CommandAlwaysOnEnable : public Command {
+    std::string getDescription() const override {
+        return "Arm always-on haptic source with an effect.";
+    }
+
+    std::string getUsageSummary() const override { return "<id> <effect> <strength>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<id>", {"Source ID (device-specific)."}},
+                {"<effect>", {"Effect ID."}},
+                {"<strength>", {"0-2."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto id = args.pop<decltype(mId)>()) {
+            mId = *id;
+            std::cout << "Source ID: " << mId << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Source ID!" << std::endl;
+            return USAGE;
+        }
+        if (auto effect = args.pop<decltype(mEffect)>()) {
+            mEffect = *effect;
+            std::cout << "Effect: " << toString(mEffect) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Effect!" << std::endl;
+            return USAGE;
+        }
+        if (auto strength = args.pop<decltype(mStrength)>()) {
+            mStrength = *strength;
+            std::cout << "Strength: " << toString(mStrength) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Strength!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
+
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+
+        return ret;
+    }
+
+    int32_t mId;
+    Effect mEffect;
+    EffectStrength mStrength;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..eb9008b 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,19 +28,33 @@
 class CommandCompose : public Command {
     std::string getDescription() const override { return "Compose vibration."; }
 
-    std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+    std::string getUsageSummary() const override {
+        return "[options] <delay> <primitive> <scale> ...";
+    }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-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;
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         while (!args.empty()) {
             CompositeEffect effect;
             if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -50,16 +64,15 @@
                 std::cerr << "Missing or Invalid Delay!" << std::endl;
                 return USAGE;
             }
-            // TODO: Use range validation when supported by AIDL
-            if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
-                effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+            if (auto primitive = args.pop<decltype(effect.primitive)>()) {
+                effect.primitive = *primitive;
                 std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
             } else {
                 std::cerr << "Missing or Invalid Primitive!" << std::endl;
                 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 {
@@ -76,21 +89,33 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        auto hal = getHal<aidl::IVibrator>();
+
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
 
-        return ret;
+        std::shared_ptr<VibratorCallback> callback;
+
+        if (mBlocking) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+        if (status.isOk() && callback) {
+            callback->waitForComplete();
+        }
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
+    bool mBlocking;
     std::vector<CompositeEffect> mComposite;
 };
 
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
new file mode 100644
index 0000000..460d39e
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 <future>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetPrimitiveDuration : public Command {
+    std::string getDescription() const override {
+        return "Retrieve effect primitive's duration in milliseconds.";
+    }
+
+    std::string getUsageSummary() const override { return "<primitive>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<primitive>", {"Primitive ID."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto primitive = args.pop<decltype(mPrimitive)>()) {
+            mPrimitive = *primitive;
+            std::cout << "Primitive: " << toString(mPrimitive) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Primitive!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        int32_t duration;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Duration: " << duration << std::endl;
+
+        return ret;
+    }
+
+    CompositePrimitive mPrimitive;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>(
+        "getPrimitiveDuration");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
new file mode 100644
index 0000000..edfcd91
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedAlwaysOnEffects : public Command {
+    std::string getDescription() const override { return "List of supported always-on effects."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        std::vector<Effect> effects;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Effects:" << std::endl;
+        for (auto &e : effects) {
+            std::cout << "  " << toString(e) << std::endl;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>(
+                "getSupportedAlwaysOnEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
new file mode 100644
index 0000000..7658f22
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedEffects : public Command {
+    std::string getDescription() const override { return "List supported effects."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        std::vector<Effect> effects;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Effects:" << std::endl;
+        for (auto &e : effects) {
+            std::cout << "  " << toString(e) << std::endl;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>(
+        "getSupportedEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
new file mode 100644
index 0000000..d101681
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetSupportedPrimitives : public Command {
+    std::string getDescription() const override { return "List of supported effect primitive."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        std::vector<CompositePrimitive> primitives;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Primitives:" << std::endl;
+        for (auto &e : primitives) {
+            std::cout << "  " << toString(e) << std::endl;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>(
+                "getSupportedPrimitives");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include "utils.h"
 #include "vibrator.h"
 
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
 namespace android {
 namespace idlcli {
 
@@ -26,16 +31,28 @@
 class CommandOn : public Command {
     std::string getDescription() const override { return "Turn on vibrator."; }
 
-    std::string getUsageSummary() const override { return "<duration>"; }
+    std::string getUsageSummary() const override { return "[options] <duration>"; }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
                 {"<duration>", {"In milliseconds."}},
         };
         return details;
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         if (auto duration = args.pop<decltype(mDuration)>()) {
             mDuration = *duration;
         } else {
@@ -52,9 +69,21 @@
     Status doMain(Args && /*args*/) override {
         std::string statusStr;
         Status ret;
+        std::shared_ptr<VibratorCallback> callback;
 
         if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+            ABinderProcess_setThreadPoolMaxThreadCount(1);
+            ABinderProcess_startThreadPool();
+
+            int32_t cap;
+            hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+            if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+                callback = ndk::SharedRefBase::make<VibratorCallback>();
+            }
+
+            auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
             statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
             return UNAVAILABLE;
         }
 
+        if (ret == OK && mBlocking) {
+            if (callback) {
+                callback->waitForComplete();
+            } else {
+                sleep_for(milliseconds(mDuration));
+            }
+        }
+
         std::cout << "Status: " << statusStr << std::endl;
 
         return ret;
     }
 
+    bool mBlocking;
     uint32_t mDuration;
 };
 
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..c897686 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include "utils.h"
 #include "vibrator.h"
 
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
 namespace android {
 namespace idlcli {
 
@@ -51,16 +56,17 @@
 static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
               static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
 
-using V1_0::EffectStrength;
-using V1_3::Effect;
+using aidl::Effect;
+using aidl::EffectStrength;
 
 class CommandPerform : public Command {
     std::string getDescription() const override { return "Perform vibration effect."; }
 
-    std::string getUsageSummary() const override { return "<effect> <strength>"; }
+    std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
                 {"<effect>", {"Effect ID."}},
                 {"<strength>", {"0-2."}},
         };
@@ -68,6 +74,17 @@
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         if (auto effect = args.pop<decltype(mEffect)>()) {
             mEffect = *effect;
             std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,23 @@
         std::string statusStr;
         uint32_t lengthMs;
         Status ret;
+        std::shared_ptr<VibratorCallback> callback;
 
         if (auto hal = getHal<aidl::IVibrator>()) {
+            ABinderProcess_setThreadPoolMaxThreadCount(1);
+            ABinderProcess_startThreadPool();
+
+            int32_t cap;
+            hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+            if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+                callback = ndk::SharedRefBase::make<VibratorCallback>();
+            }
+
             int32_t aidlLengthMs;
-            auto status =
-                    hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
-                              static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+            auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
+                                    &aidlLengthMs);
+
             statusStr = status.getDescription();
             lengthMs = static_cast<uint32_t>(aidlLengthMs);
             ret = status.isOk() ? OK : ERROR;
@@ -111,17 +139,20 @@
             };
 
             if (auto hal = getHal<V1_3::IVibrator>()) {
-                hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
-                                    static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+                hidlRet =
+                        hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+                                  static_cast<V1_0::EffectStrength>(mStrength), callback);
             } else if (auto hal = getHal<V1_2::IVibrator>()) {
-                hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
-                                    static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+                hidlRet =
+                        hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+                                  static_cast<V1_0::EffectStrength>(mStrength), callback);
             } else if (auto hal = getHal<V1_1::IVibrator>()) {
                 hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
-                                    static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+                                    static_cast<V1_1::Effect_1_1>(mEffect),
+                                    static_cast<V1_0::EffectStrength>(mStrength), callback);
             } else if (auto hal = getHal<V1_0::IVibrator>()) {
                 hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
-                                    mStrength, callback);
+                                    static_cast<V1_0::EffectStrength>(mStrength), callback);
             } else {
                 return UNAVAILABLE;
             }
@@ -130,12 +161,21 @@
             ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
         }
 
+        if (ret == OK && mBlocking) {
+            if (callback) {
+                callback->waitForComplete();
+            } else {
+                sleep_for(milliseconds(lengthMs));
+            }
+        }
+
         std::cout << "Status: " << statusStr << std::endl;
         std::cout << "Length: " << lengthMs << std::endl;
 
         return ret;
     }
 
+    bool mBlocking;
     Effect mEffect;
     EffectStrength mStrength;
 };
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/CrateManager.cpp b/cmds/installd/CrateManager.cpp
index 6e079eb..b17cba1 100644
--- a/cmds/installd/CrateManager.cpp
+++ b/cmds/installd/CrateManager.cpp
@@ -86,7 +86,7 @@
 }
 
 void CrateManager::traverseAllPackagesForUser(
-        const std::unique_ptr<std::string>& uuid, userid_t userId,
+        const std::optional<std::string>& uuid, userid_t userId,
         std::function<void(FTSENT*)>& onHandlingPackage) {
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
@@ -96,21 +96,21 @@
 
 void CrateManager::createCrate(
         CratedFolder cratedFolder,
-        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+        std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
     const char* path = cratedFolder->fts_path;
     if (path == nullptr || *path == '\0') {
         return;
     }
 
-    std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
-    crateMetadata->uid = cratedFolder->fts_statp->st_uid;
-    crateMetadata->packageName = mPackageName;
-    crateMetadata->id = getValidatedCratedPath(path);
+    CrateMetadata crateMetadata;
+    crateMetadata.uid = cratedFolder->fts_statp->st_uid;
+    crateMetadata.packageName = mPackageName;
+    crateMetadata.id = getValidatedCratedPath(path);
 
-    onCreateCrate(cratedFolder, crateMetadata);
+    onCreateCrate(cratedFolder, std::move(crateMetadata));
 }
 
-void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
     std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
         createCrate(cratedFolder, onCreateCrate);
     };
@@ -118,11 +118,11 @@
 }
 
 #if CRATE_DEBUG
-void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+void CrateManager::dump(const CrateMetadata& CrateMetadata) {
     LOG(DEBUG) << "CrateMetadata = {"
-            << "uid : \"" << CrateMetadata->uid
-            << "\", packageName : \"" << CrateMetadata->packageName
-            << "\", id : \"" << CrateMetadata->id
+            << "uid : \"" << CrateMetadata.uid
+            << "\", packageName : \"" << CrateMetadata.packageName
+            << "\", id : \"" << CrateMetadata.id
             << "\"}";
 }
 #endif
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
index 4332d4c..1f30b5d 100644
--- a/cmds/installd/CrateManager.h
+++ b/cmds/installd/CrateManager.h
@@ -25,7 +25,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -55,18 +55,18 @@
     CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
     ~CrateManager();
 
-    void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+    void traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
 
     static void traverseChildDir(const std::string& targetDir,
             std::function<void(FTSENT*)>& onVisitChildDir);
 
     static void traverseAllPackagesForUser(
-        const std::unique_ptr<std::string>& uuid,
+        const std::optional<std::string>& uuid,
         userid_t userId,
         std::function<void(FTSENT*)>& onHandlingPackage);
 
 #if CRATE_DEBUG
-    static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+    static void dump(const CrateMetadata& CrateMetadata);
 #endif
 private:
     std::string mRoot;
@@ -75,7 +75,7 @@
 
     void createCrate(
         CratedFolder cratedFolder,
-        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+        std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
 };
 
 } // namespace installd
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 7d1c1ad..eb1bbd9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -166,7 +166,7 @@
     }
 }
 
-binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuid(const std::optional<std::string>& uuid) {
     if (!uuid || is_valid_filename(*uuid)) {
         return ok();
     } else {
@@ -175,7 +175,7 @@
     }
 }
 
-binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuidTestOrNull(const std::optional<std::string>& uuid) {
     if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
         return ok();
     } else {
@@ -214,7 +214,7 @@
     return ok();
 }
 
-binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) {
+binder::Status checkArgumentPath(const std::optional<std::string>& path) {
     if (path) {
         return checkArgumentPath(*path);
     } else {
@@ -420,33 +420,7 @@
     return true;
 }
 
-binder::Status InstalldNativeService::createAppDataBatched(
-        const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
-        const std::unique_ptr<std::vector<std::unique_ptr<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++) {
-        if (!packageNames->at(i)) {
-            continue;
-        }
-        ret = createAppData(uuids->at(i), *packageNames->at(i), 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::unique_ptr<std::string>& uuid,
+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) {
     ENFORCE_UID(AID_SYSTEM);
@@ -527,7 +501,39 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+
+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);
     CHECK_ARGUMENT_UUID(uuid);
@@ -588,7 +594,7 @@
     return res;
 }
 
-binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -708,7 +714,7 @@
     return res;
 }
 
-binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -780,7 +786,7 @@
     return (gid != -1) ? gid : uid;
 }
 
-binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::fixupAppData(const std::optional<std::string>& uuid,
         int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -900,7 +906,7 @@
 }
 
 binder::Status InstalldNativeService::snapshotAppData(
-        const std::unique_ptr<std::string>& volumeUuid,
+        const std::optional<std::string>& volumeUuid,
         const std::string& packageName, int32_t user, int32_t snapshotId,
         int32_t storageFlags, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1027,7 +1033,7 @@
 }
 
 binder::Status InstalldNativeService::restoreAppDataSnapshot(
-        const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName,
+        const std::optional<std::string>& volumeUuid, const std::string& packageName,
         const int32_t appId, const std::string& seInfo, const int32_t user,
         const int32_t snapshotId, int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1074,6 +1080,7 @@
             res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
             return res;
         }
+        delete_dir_contents_and_dir(from_ce, true /* ignore_if_missing */);
     }
 
     if (needs_de_rollback) {
@@ -1090,6 +1097,7 @@
             res = error(rc, "Failed copying " + from_de + " to " + to_de);
             return res;
         }
+        delete_dir_contents_and_dir(from_de, true /* ignore_if_missing */);
     }
 
     // Finally, restore the SELinux label on the app data.
@@ -1097,7 +1105,7 @@
 }
 
 binder::Status InstalldNativeService::destroyAppDataSnapshot(
-        const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
+        const std::optional<std::string> &volumeUuid, const std::string& packageName,
         const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
         int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1130,7 +1138,7 @@
 }
 
 binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified(
-        const std::unique_ptr<std::string> &volumeUuid, const int32_t userId,
+        const std::optional<std::string> &volumeUuid, const int32_t user,
         const std::vector<int32_t>& retainSnapshotIds) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
@@ -1138,7 +1146,7 @@
 
     const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
 
-    auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId);
+    auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user);
 
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir);
     if (!dir) {
@@ -1157,7 +1165,7 @@
                 std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(),
                           snapshot_id) == retainSnapshotIds.end()) {
             auto rollback_path = create_data_misc_ce_rollback_path(
-                volume_uuid, userId, snapshot_id);
+                volume_uuid, user, snapshot_id);
             int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */);
             if (res != 0) {
                 return error(res, "Failed clearing snapshot " + rollback_path);
@@ -1167,8 +1175,8 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
-        const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::string>& fromUuid,
+        const std::optional<std::string>& toUuid, const std::string& packageName,
         int32_t appId, const std::string& seInfo,
         int32_t targetSdkVersion, const std::string& fromCodePath) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1275,7 +1283,7 @@
     return res;
 }
 
-binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid,
         int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -1293,7 +1301,7 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid,
         int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -1330,13 +1338,13 @@
     return res;
 }
 
-binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid,
         int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    auto uuidString = uuid ? *uuid : "";
+    auto uuidString = uuid.value_or("");
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto data_path = create_data_path(uuid_);
     auto noop = (flags & FLAG_FREE_CACHE_NOOP);
@@ -1734,7 +1742,7 @@
     fts_close(fts);
 }
 
-binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid,
         const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
         int32_t appId, const std::vector<int64_t>& ceDataInodes,
         const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
@@ -1774,7 +1782,7 @@
     memset(&stats, 0, sizeof(stats));
     memset(&extStats, 0, sizeof(extStats));
 
-    auto uuidString = uuid ? *uuid : "";
+    auto uuidString = uuid.value_or("");
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
     if (!IsQuotaSupported(uuidString)) {
@@ -1961,7 +1969,7 @@
     return sizes;
 }
 
-binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getUserSize(const std::optional<std::string>& uuid,
         int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1981,7 +1989,7 @@
     memset(&stats, 0, sizeof(stats));
     memset(&extStats, 0, sizeof(extStats));
 
-    auto uuidString = uuid ? *uuid : "";
+    auto uuidString = uuid.value_or("");
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
     if (!IsQuotaSupported(uuidString)) {
@@ -2093,7 +2101,7 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getExternalSize(const std::optional<std::string>& uuid,
         int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
@@ -2108,7 +2116,7 @@
     LOG(INFO) << "Measuring external " << userId;
 #endif
 
-    auto uuidString = uuid ? *uuid : "";
+    auto uuidString = uuid.value_or("");
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
     int64_t totalSize = 0;
@@ -2210,9 +2218,9 @@
 }
 
 binder::Status InstalldNativeService::getAppCrates(
-        const std::unique_ptr<std::string>& uuid,
+        const std::optional<std::string>& uuid,
         const std::vector<std::string>& packageNames, int32_t userId,
-        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+        std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     for (const auto& packageName : packageNames) {
@@ -2221,15 +2229,15 @@
 #ifdef ENABLE_STORAGE_CRATES
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+    auto retVector = std::vector<std::optional<CrateMetadata>>();
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
 
-    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
-            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+    std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+            [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
         if (cratedFolder == nullptr) {
             return;
         }
-        retVector->push_back(std::move(crateMetadata));
+        retVector.push_back(std::move(crateMetadata));
     };
 
     for (const auto& packageName : packageNames) {
@@ -2241,15 +2249,15 @@
     }
 
 #if CRATE_DEBUG
-    LOG(WARNING) << "retVector->size() =" << retVector->size();
-    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
-        CrateManager::dump(*iter);
+    LOG(WARNING) << "retVector.size() =" << retVector.size();
+    for (auto& item : retVector) {
+        CrateManager::dump(*item);
     }
 #endif
 
     *_aidl_return = std::move(retVector);
 #else // ENABLE_STORAGE_CRATES
-    *_aidl_return = nullptr;
+    _aidl_return->reset();
 
     /* prevent compile warning fail */
     if (userId < 0) {
@@ -2260,22 +2268,22 @@
 }
 
 binder::Status InstalldNativeService::getUserCrates(
-        const std::unique_ptr<std::string>& uuid, int32_t userId,
-        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+        const std::optional<std::string>& uuid, int32_t userId,
+        std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
 #ifdef ENABLE_STORAGE_CRATES
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
-    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+    auto retVector = std::vector<std::optional<CrateMetadata>>();
 
-    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
-            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+    std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+            [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
         if (cratedFolder == nullptr) {
             return;
         }
-        retVector->push_back(std::move(crateMetadata));
+        retVector.push_back(std::move(crateMetadata));
     };
 
     std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
@@ -2285,15 +2293,15 @@
     CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
 
 #if CRATE_DEBUG
-    LOG(DEBUG) << "retVector->size() =" << retVector->size();
-    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
-        CrateManager::dump(*iter);
+    LOG(DEBUG) << "retVector.size() =" << retVector.size();
+    for (auto& item : retVector) {
+        CrateManager::dump(*item);
     }
 #endif
 
     *_aidl_return = std::move(retVector);
 #else // ENABLE_STORAGE_CRATES
-    *_aidl_return = nullptr;
+    _aidl_return->reset();
 
     /* prevent compile warning fail */
     if (userId < 0) {
@@ -2303,7 +2311,7 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid,
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -2374,19 +2382,19 @@
     return ok();
 }
 
-static const char* getCStr(const std::unique_ptr<std::string>& data,
+static const char* getCStr(const std::optional<std::string>& data,
         const char* default_value = nullptr) {
-    return data == nullptr ? default_value : data->c_str();
+    return data ? data->c_str() : default_value;
 }
 binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
-        const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
-        int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
-        const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-        const std::unique_ptr<std::string>& classLoaderContext,
-        const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
-        const std::unique_ptr<std::string>& profileName,
-        const std::unique_ptr<std::string>& dexMetadataPath,
-        const std::unique_ptr<std::string>& compilationReason) {
+        const std::optional<std::string>& packageName, const std::string& instructionSet,
+        int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+        const std::string& compilerFilter, const std::optional<std::string>& uuid,
+        const std::optional<std::string>& classLoaderContext,
+        const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
+        const std::optional<std::string>& profileName,
+        const std::optional<std::string>& dexMetadataPath,
+        const std::optional<std::string>& compilationReason) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PATH(apkPath);
@@ -2432,7 +2440,7 @@
 }
 
 binder::Status InstalldNativeService::linkNativeLibraryDirectory(
-        const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+        const std::optional<std::string>& uuid, const std::string& packageName,
         const std::string& nativeLibPath32, int32_t userId) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -2523,7 +2531,7 @@
     return res;
 }
 
-binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::restoreconAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
         const std::string& seInfo) {
     ENFORCE_UID(AID_SYSTEM);
@@ -2641,7 +2649,7 @@
 }
 
 binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
-        const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) {
+        const std::string& instructionSet, const std::optional<std::string>& outputPath) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
@@ -2793,7 +2801,7 @@
 
 binder::Status InstalldNativeService::reconcileSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
-        const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+        const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid,
         int32_t storage_flag, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2808,7 +2816,7 @@
 
 binder::Status InstalldNativeService::hashSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
-        const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag,
+        const std::optional<std::string>& volumeUuid, int32_t storageFlag,
         std::vector<uint8_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2867,7 +2875,7 @@
 
 // Mount volume's CE and DE storage to mirror
 binder::Status InstalldNativeService::tryMountDataMirror(
-        const std::unique_ptr<std::string>& uuid) {
+        const std::optional<std::string>& uuid) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (!sAppDataIsolationEnabled) {
@@ -2931,7 +2939,7 @@
 
 // Unmount volume's CE and DE storage from mirror
 binder::Status InstalldNativeService::onPrivateVolumeRemoved(
-        const std::unique_ptr<std::string>& uuid) {
+        const std::optional<std::string>& uuid) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (!sAppDataIsolationEnabled) {
@@ -2975,7 +2983,7 @@
 }
 
 std::string InstalldNativeService::findDataMediaPath(
-        const std::unique_ptr<std::string>& uuid, userid_t userid) {
+        const std::optional<std::string>& uuid, userid_t userid) {
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
@@ -2988,15 +2996,14 @@
 }
 
 binder::Status InstalldNativeService::isQuotaSupported(
-        const std::unique_ptr<std::string>& uuid, bool* _aidl_return) {
-    auto uuidString = uuid ? *uuid : "";
-    *_aidl_return = IsQuotaSupported(uuidString);
+        const std::optional<std::string>& uuid, bool* _aidl_return) {
+    *_aidl_return = IsQuotaSupported(uuid.value_or(""));
     return ok();
 }
 
 binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,
         int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
-        const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) {
+        const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(codePath);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 8e7d98b..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -40,81 +40,84 @@
     static char const* getServiceName() { return "installd"; }
     virtual status_t dump(int fd, const Vector<String16> &args) override;
 
-    binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+    binder::Status createUserData(const std::optional<std::string>& uuid, int32_t userId,
             int32_t userSerial, int32_t flags);
-    binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+    binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
             int32_t flags);
-    binder::Status createAppDataBatched(
-            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
-            const std::unique_ptr<std::vector<std::unique_ptr<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::unique_ptr<std::string>& uuid,
+
+    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 restoreconAppData(const std::unique_ptr<std::string>& uuid,
+
+    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);
-    binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+    binder::Status migrateAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags);
-    binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+    binder::Status clearAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
-    binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+    binder::Status destroyAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
 
-    binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
+    binder::Status fixupAppData(const std::optional<std::string>& uuid, int32_t flags);
 
-    binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
+    binder::Status snapshotAppData(const std::optional<std::string>& volumeUuid,
             const std::string& packageName, const int32_t user, const int32_t snapshotId,
             int32_t storageFlags, int64_t* _aidl_return);
-    binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
+    binder::Status restoreAppDataSnapshot(const std::optional<std::string>& volumeUuid,
             const std::string& packageName, const int32_t appId, const std::string& seInfo,
             const int32_t user, const int32_t snapshotId, int32_t storageFlags);
-    binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
+    binder::Status destroyAppDataSnapshot(const std::optional<std::string> &volumeUuid,
             const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
             const int32_t snapshotId, int32_t storageFlags);
-    binder::Status destroyCeSnapshotsNotSpecified(const std::unique_ptr<std::string> &volumeUuid,
-            const int32_t userId, const std::vector<int32_t>& retainSnapshotIds);
+    binder::Status destroyCeSnapshotsNotSpecified(const std::optional<std::string> &volumeUuid,
+            const int32_t user, const std::vector<int32_t>& retainSnapshotIds);
 
-    binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+    binder::Status getAppSize(const std::optional<std::string>& uuid,
             const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
             int32_t appId, const std::vector<int64_t>& ceDataInodes,
             const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
-    binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+    binder::Status getUserSize(const std::optional<std::string>& uuid,
             int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
             std::vector<int64_t>* _aidl_return);
-    binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+    binder::Status getExternalSize(const std::optional<std::string>& uuid,
             int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
             std::vector<int64_t>* _aidl_return);
 
-    binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+    binder::Status getAppCrates(const std::optional<std::string>& uuid,
             const std::vector<std::string>& packageNames,
             int32_t userId,
-            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+            std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
                     _aidl_return);
     binder::Status getUserCrates(
-            const std::unique_ptr<std::string>& uuid, int32_t userId,
-            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+            const std::optional<std::string>& uuid, int32_t userId,
+            std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
                     _aidl_return);
 
-    binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
+    binder::Status setAppQuota(const std::optional<std::string>& uuid,
             int32_t userId, int32_t appId, int64_t cacheQuota);
 
-    binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
-            const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+    binder::Status moveCompleteApp(const std::optional<std::string>& fromUuid,
+            const std::optional<std::string>& toUuid, const std::string& packageName,
             int32_t appId, const std::string& seInfo,
             int32_t targetSdkVersion, const std::string& fromCodePath);
 
     binder::Status dexopt(const std::string& apkPath, int32_t uid,
-            const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
-            int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
-            const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-            const std::unique_ptr<std::string>& classLoaderContext,
-            const std::unique_ptr<std::string>& seInfo, bool downgrade,
-            int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName,
-            const std::unique_ptr<std::string>& dexMetadataPath,
-            const std::unique_ptr<std::string>& compilationReason);
+            const std::optional<std::string>& packageName, const std::string& instructionSet,
+            int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+            const std::string& compilerFilter, const std::optional<std::string>& uuid,
+            const std::optional<std::string>& classLoaderContext,
+            const std::optional<std::string>& seInfo, bool downgrade,
+            int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+            const std::optional<std::string>& dexMetadataPath,
+            const std::optional<std::string>& compilationReason);
 
     binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
                                   const std::string& outDexFile, int uid, bool* _aidl_return);
@@ -137,9 +140,9 @@
             const std::string& profileName);
 
     binder::Status rmPackageDir(const std::string& packageDir);
-    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+    binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
             int64_t cacheReservedBytes, int32_t flags);
-    binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+    binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
             const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
     binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
     binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
@@ -147,27 +150,27 @@
     binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
-            const std::unique_ptr<std::string>& outputPath);
+            const std::optional<std::string>& outputPath);
     binder::Status installApkVerity(const std::string& filePath,
             android::base::unique_fd verityInput, int32_t contentSize);
     binder::Status assertFsverityRootHashMatches(const std::string& filePath,
             const std::vector<uint8_t>& expectedHash);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
-        const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+        const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
     binder::Status hashSecondaryDexFile(const std::string& dexPath,
-        const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid,
+        const std::string& packageName, int32_t uid, const std::optional<std::string>& volumeUuid,
         int32_t storageFlag, std::vector<uint8_t>* _aidl_return);
 
     binder::Status invalidateMounts();
-    binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+    binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid,
             bool* _aidl_return);
-    binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid);
-    binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
+    binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid);
+    binder::Status onPrivateVolumeRemoved(const std::optional<std::string>& volumeUuid);
 
     binder::Status prepareAppProfile(const std::string& packageName,
             int32_t userId, int32_t appId, const std::string& profileName,
-            const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata,
+            const std::string& codePath, const std::optional<std::string>& dexMetadata,
             bool* _aidl_return);
 
     binder::Status migrateLegacyObbData();
@@ -184,7 +187,7 @@
     /* Map from UID to cache quota size */
     std::unordered_map<uid_t, int64_t> mCacheQuotas;
 
-    std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid);
+    std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid);
 };
 
 }  // namespace installd
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/ui/UiConfig.cpp b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
similarity index 67%
rename from libs/ui/UiConfig.cpp
rename to cmds/installd/binder/android/os/CreateAppDataResult.aidl
index 0ac863d..3b8fa6b 100644
--- a/libs/ui/UiConfig.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.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,11 @@
  * 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 CreateAppDataResult {
+    long ceDataInode;
+    int exceptionCode;
+    @utf8InCpp String exceptionMessage;
 }
-
-
-}; // namespace android
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 ffa8724..5076ae6 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);
@@ -868,6 +504,7 @@
                   /*for_boot_image*/false);
     }
 
+    using ExecVHelper::Exec;  // To suppress -Wno-overloaded-virtual
     void Exec() {
         ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
     }
@@ -1022,7 +659,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 +853,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 +868,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 +891,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 +923,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 +941,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 +951,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 +994,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 +1012,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 +1029,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 +1057,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 +1681,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 +1696,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 +1728,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 +1802,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 +1820,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;
 }
@@ -2325,7 +1876,7 @@
 //   out_secondary_dex_exists will be set to false.
 bool reconcile_secondary_dex_file(const std::string& dex_path,
         const std::string& pkgname, int uid, const std::vector<std::string>& isas,
-        const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+        const std::optional<std::string>& volume_uuid, int storage_flag,
         /*out*/bool* out_secondary_dex_exists) {
     *out_secondary_dex_exists = false;  // start by assuming the file does not exist.
     if (isas.size() == 0) {
@@ -2346,7 +1897,7 @@
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
 
-        const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+        const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
                 uid, storage_flag)) {
             LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
@@ -2447,11 +1998,11 @@
 // the app.
 // For any other errors (e.g. if any of the parameters are invalid) returns false.
 bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid,
-        const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+        const std::optional<std::string>& volume_uuid, int storage_flag,
         std::vector<uint8_t>* out_secondary_dex_hash) {
     out_secondary_dex_hash->clear();
 
-    const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+    const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
 
     if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
         LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: "
@@ -2978,7 +2529,7 @@
                          appid_t app_id,
                          const std::string& profile_name,
                          const std::string& code_path,
-                         const std::unique_ptr<std::string>& dex_metadata) {
+                         const std::optional<std::string>& dex_metadata) {
     // Prepare the current profile.
     std::string cur_profile  = create_current_profile_path(user_id, package_name, profile_name,
             /*is_secondary_dex*/ false);
@@ -2989,7 +2540,7 @@
     }
 
     // Check if we need to install the profile from the dex metadata.
-    if (dex_metadata == nullptr) {
+    if (!dex_metadata) {
         return true;
     }
 
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index cc44873..d35953c 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -21,6 +21,8 @@
 
 #include <sys/types.h>
 
+#include <optional>
+
 #include <cutils/multiuser.h>
 
 namespace android {
@@ -100,17 +102,17 @@
                          appid_t app_id,
                          const std::string& profile_name,
                          const std::string& code_path,
-                         const std::unique_ptr<std::string>& dex_metadata);
+                         const std::optional<std::string>& dex_metadata);
 
 bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
 
 bool reconcile_secondary_dex_file(const std::string& dex_path,
         const std::string& pkgname, int uid, const std::vector<std::string>& isas,
-        const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+        const std::optional<std::string>& volumeUuid, int storage_flag,
         /*out*/bool* out_secondary_dex_exists);
 
 bool hash_secondary_dex_file(const std::string& dex_path,
-        const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid,
+        const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid,
         int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
 
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
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/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 5a5cb53..863cdfe 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -114,15 +114,14 @@
 class CacheTest : public testing::Test {
 protected:
     InstalldNativeService* service;
-    std::unique_ptr<std::string> testUuid;
+    std::optional<std::string> testUuid;
 
     virtual void SetUp() {
         setenv("ANDROID_LOG_TAGS", "*:v", 1);
         android::base::InitLogging(nullptr);
 
         service = new InstalldNativeService();
-        testUuid = std::make_unique<std::string>();
-        *testUuid = std::string(kTestUuid);
+        testUuid = kTestUuid;
         system("mkdir -p /data/local/tmp/user/0");
     }
 
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 16e4055..fbf1e0c 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -193,7 +193,7 @@
     const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId);
 
     InstalldNativeService* service_;
-    std::unique_ptr<std::string> volume_uuid_;
+    std::optional<std::string> volume_uuid_;
     std::string package_name_;
     std::string apk_path_;
     std::string empty_dm_file_;
@@ -221,7 +221,7 @@
         ASSERT_TRUE(init_selinux());
         service_ = new InstalldNativeService();
 
-        volume_uuid_ = nullptr;
+        volume_uuid_ = std::nullopt;
         package_name_ = "com.installd.test.dexopt";
         se_info_ = "default";
         app_apk_dir_ = android_app_dir + package_name_;
@@ -294,7 +294,7 @@
         }
 
         // Create a secondary dex file on CE storage
-        const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+        const char* volume_uuid_cstr = volume_uuid_ ? volume_uuid_->c_str() : nullptr;
         app_private_dir_ce_ = create_data_user_ce_package_path(
                 volume_uuid_cstr, kTestUserId, package_name_.c_str());
         secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
@@ -353,36 +353,32 @@
         if (class_loader_context == nullptr) {
             class_loader_context = "&";
         }
-        std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
         int32_t dexopt_needed = 0;  // does not matter;
-        std::unique_ptr<std::string> out_path = nullptr;  // does not matter
+        std::optional<std::string> out_path; // does not matter
         int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
         std::string compiler_filter = "speed-profile";
-        std::unique_ptr<std::string> class_loader_context_ptr(
-                new std::string(class_loader_context));
-        std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
         bool downgrade = false;
         int32_t target_sdk_version = 0;  // default
-        std::unique_ptr<std::string> profile_name_ptr = nullptr;
-        std::unique_ptr<std::string> dm_path_ptr = nullptr;
-        std::unique_ptr<std::string> compilation_reason_ptr = nullptr;
+        std::optional<std::string> profile_name;
+        std::optional<std::string> dm_path;
+        std::optional<std::string> compilation_reason;
 
         binder::Status result = service_->dexopt(path,
                                                  uid,
-                                                 package_name_ptr,
+                                                 package_name_,
                                                  kRuntimeIsa,
                                                  dexopt_needed,
                                                  out_path,
                                                  dex_flags,
                                                  compiler_filter,
                                                  volume_uuid_,
-                                                 class_loader_context_ptr,
-                                                 se_info_ptr,
+                                                 class_loader_context,
+                                                 se_info_,
                                                  downgrade,
                                                  target_sdk_version,
-                                                 profile_name_ptr,
-                                                 dm_path_ptr,
-                                                 compilation_reason_ptr);
+                                                 profile_name,
+                                                 dm_path,
+                                                 compilation_reason);
         ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
         int expected_access = should_dex_be_compiled ? 0 : -1;
         std::string odex = GetSecondaryDexArtifact(path, "odex");
@@ -481,41 +477,35 @@
                            bool downgrade,
                            bool should_binder_call_succeed,
                            /*out */ binder::Status* binder_result) {
-        std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
-        std::unique_ptr<std::string> out_path(
-                oat_dir == nullptr ? nullptr : new std::string(oat_dir));
-        std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
-        std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+        std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
+        std::string class_loader_context = "&";
         int32_t target_sdk_version = 0;  // default
-        std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof"));
-        std::unique_ptr<std::string> dm_path_ptr = nullptr;
-        if (dm_path != nullptr) {
-            dm_path_ptr.reset(new std::string(dm_path));
-        }
-        std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
+        std::string profile_name = "primary.prof";
+        std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt;
+        std::string compilation_reason = "test-reason";
 
         bool prof_result;
         ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
-                package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
-                dm_path_ptr, &prof_result));
+                package_name_, kTestUserId, kTestAppId, profile_name, apk_path_,
+                dm_path_opt, &prof_result));
         ASSERT_TRUE(prof_result);
 
         binder::Status result = service_->dexopt(apk_path_,
                                                  uid,
-                                                 package_name_ptr,
+                                                 package_name_,
                                                  kRuntimeIsa,
                                                  dexopt_needed,
                                                  out_path,
                                                  dex_flags,
                                                  compiler_filter,
                                                  volume_uuid_,
-                                                 class_loader_context_ptr,
-                                                 se_info_ptr,
+                                                 class_loader_context,
+                                                 se_info_,
                                                  downgrade,
                                                  target_sdk_version,
-                                                 profile_name_ptr,
-                                                 dm_path_ptr,
-                                                 compilation_reason_ptr);
+                                                 profile_name,
+                                                 dm_path_opt,
+                                                 compilation_reason);
         ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
 
         if (!should_binder_call_succeed) {
@@ -673,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) {
@@ -992,7 +982,7 @@
         bool result;
         ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
                 package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
-                /*dex_metadata*/ nullptr, &result));
+                /*dex_metadata*/ {}, &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 0fb62ae..1e7559d 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -99,15 +99,14 @@
 class ServiceTest : public testing::Test {
 protected:
     InstalldNativeService* service;
-    std::unique_ptr<std::string> testUuid;
+    std::optional<std::string> testUuid;
 
     virtual void SetUp() {
         setenv("ANDROID_LOG_TAGS", "*:v", 1);
         android::base::InitLogging(nullptr);
 
         service = new InstalldNativeService();
-        testUuid = std::make_unique<std::string>();
-        *testUuid = std::string(kTestUuid);
+        testUuid = kTestUuid;
         system("mkdir -p /data/local/tmp/user/0");
 
         init_globals_from_data_and_root();
@@ -322,7 +321,7 @@
 
   // Request a snapshot of the CE content but not the DE content.
   int64_t ce_snapshot_inode;
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode));
   struct stat buf;
   memset(&buf, 0, sizeof(buf));
@@ -344,7 +343,7 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // Request a snapshot of the DE content but not the CE content.
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode));
   // Only DE content snapshot was requested.
   ASSERT_EQ(ce_snapshot_inode, 0);
@@ -365,7 +364,7 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // Request a snapshot of both the CE as well as the DE content.
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
 
   ASSERT_TRUE(android::base::ReadFileToString(
@@ -407,10 +406,10 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   // Request snapshot for the package com.foo.
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
   // Now request snapshot with the same id for the package com.bar
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
 
   // Check that both snapshots have correct data in them.
@@ -439,9 +438,9 @@
   ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true));
 
   int64_t ce_snapshot_inode;
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode));
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr));
   // No CE content snapshot was performed.
   ASSERT_EQ(ce_snapshot_inode, 0);
@@ -476,7 +475,7 @@
           "TEST_CONTENT_2_DE", fake_package_de_path + "/file2",
           0700, 10000, 20000, false /* follow_symlinks */));
 
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
 
   // Previous snapshot (with data for file1) must be cleared.
@@ -497,7 +496,7 @@
   ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
   ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
 
-  EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+  EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_optional<std::string>("FOO"),
           "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr));
 }
 
@@ -524,7 +523,7 @@
   ASSERT_TRUE(android::base::WriteStringToFile(
           "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
           0700, 10000, 20000, false /* follow_symlinks */));
-  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
   // The snapshot call must clear cache.
   struct stat sb;
@@ -558,7 +557,7 @@
           "TEST_CONTENT_DE", fake_package_de_path + "/file1",
           0700, 10000, 20000, false /* follow_symlinks */));
 
-  ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+  ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_optional<std::string>("TEST"),
           "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
 
   std::string ce_content, de_content;
@@ -584,7 +583,7 @@
 
   int64_t ce_snapshot_inode;
   // Request a snapshot of both the CE as well as the DE content.
-  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+  ASSERT_TRUE(service->snapshotAppData(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
   // Because CE data snapshot was requested, ce_snapshot_inode can't be null.
   ASSERT_NE(0, ce_snapshot_inode);
@@ -594,7 +593,7 @@
   ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
 
 
-  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
           "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
   // Check snapshot is deleted.
   ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
@@ -615,7 +614,7 @@
           "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
           0700, 10000, 20000, false /* follow_symlinks */));
 
-  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
 
   // Check snapshot is deleted.
@@ -624,7 +623,7 @@
   ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
 
   // Check that deleting already deleted snapshot is no-op.
-  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
           "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
 }
 
@@ -637,7 +636,7 @@
   ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
   ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
 
-  ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
+  ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_optional<std::string>("BAR"),
           "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk());
 }
 
@@ -669,7 +668,7 @@
           0700, 10000, 20000, false /* follow_symlinks */));
 
   ASSERT_TRUE(service->destroyCeSnapshotsNotSpecified(
-          std::make_unique<std::string>("TEST"), 0, { 1543, 77 }).isOk());
+          std::make_optional<std::string>("TEST"), 0, { 1543, 77 }).isOk());
 
   // Check only snapshots not specified are deleted.
   struct stat sb;
@@ -690,7 +689,7 @@
   ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
   ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
 
-  EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+  EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_optional<std::string>("BAR"),
           "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
 }
 
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..2c3efe5 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,6 +134,9 @@
             LOG(ERROR) << msg;
         }
         if (descriptor != interfaceName) {
+            if (parentDebugInfoLevel == ParentDebugInfoLevel::FQNAME_ONLY) {
+                out << "[See " << descriptor << "/" << instanceName << "]";
+            }
             return OK;
         }
     }
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/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..9964888 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() {}
@@ -681,8 +708,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 +793,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..e80c321 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -213,17 +213,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);
         }
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/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index b574098..3ada05c 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,8 +25,9 @@
     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;
 }
 
 message SurfaceChange {
@@ -208,4 +209,9 @@
 
 message ShadowRadiusChange {
     required float radius = 1;
+}
+
+message Origin {
+    required int32 pid = 1;
+    required int32 uid = 2;
 }
\ No newline at end of file
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/android.software.controls.xml b/data/etc/android.software.controls.xml
new file mode 100644
index 0000000..2cba34b
--- /dev/null
+++ b/data/etc/android.software.controls.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<!-- This is the standard feature indicating device controls support on the device,
+     as specified in the CDD. ONLY devices that meet the CDD's requirements may
+     declare this feature. -->
+<permissions>
+    <feature name="android.software.controls" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 619d017..dc6963f 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -59,6 +59,9 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" notLowRam="true"/>
 
+    <!-- Feature to specify if the device supports controls. -->
+    <feature name="android.software.controls" />
+
     <!-- Devices with all optimizations required to support VR Mode and
          pass all CDD requirements for this feature may include
          android.hardware.vr.high_performance -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 52524ca..e878f86 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -59,6 +59,9 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" />
 
+    <!-- Feature to specify if the device supports controls. -->
+    <feature name="android.software.controls" />
+
     <!-- devices with GPS must include android.hardware.location.gps.xml -->
     <!-- devices with a rear-facing camera must include one of these as appropriate:
          android.hardware.camera.xml or
diff --git a/docs/Doxyfile b/docs/Doxyfile
index efa639d..a1bd960 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1621,7 +1621,23 @@
 # undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = __attribute__(x)=
+PREDEFINED             = \
+    "__ANDROID_API__=10000" \
+    "__BEGIN_DECLS=" \
+    "__END_DECLS=" \
+    "__INTRODUCED_IN(x)=" \
+    "__INTRODUCED_IN_32(x)=" \
+    "__INTRODUCED_IN_64(x)=" \
+    "__RENAME(x)=" \
+    "__RENAME_LDBL(x,y,z)=" \
+    "__printflike(x,y)=" \
+    "__attribute__(x)=" \
+    "__wur=" \
+    "__mallocfunc=" \
+    "__attribute_pure__=" \
+    "__attribute__(x)=" \
+    __ANDROID__ \
+    __BIONIC__ \
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
diff --git a/headers/Android.bp b/headers/Android.bp
index 5337235..8f41c2b 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -18,4 +18,11 @@
         "libstagefright_foundation_headers",
     ],
     min_sdk_version: "29",
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
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/bitmap.h b/include/android/bitmap.h
index 2631b14..f195399 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -125,6 +125,8 @@
  * Note that {@link ADataSpace} only exposes a few values. This may return
  * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
  * corresponding ADataSpace.
+ *
+ * Available since API level 30.
  */
 int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap)  __INTRODUCED_IN(30);
 
@@ -189,6 +191,8 @@
 /**
  *  User-defined function for writing the output of compression.
  *
+ *  Available since API level 30.
+ *
  *  @param userContext Pointer to user-defined data passed to
  *         {@link AndroidBitmap_compress}.
  *  @param data Compressed data of |size| bytes to write.
@@ -202,6 +206,8 @@
 /**
  *  Compress |pixels| as described by |info|.
  *
+ *  Available since API level 30.
+ *
  *  @param info Description of the pixels to compress.
  *  @param dataspace {@link ADataSpace} describing the color space of the
  *                   pixels.
@@ -234,6 +240,8 @@
  *
  *  Client must not modify it while a Bitmap is wrapping it.
  *
+ *  Available since API level 30.
+ *
  *  @param bitmap Handle to an android.graphics.Bitmap.
  *  @param outBuffer On success, is set to a pointer to the
  *         {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index c1c4a72..e9f559c 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -129,9 +129,19 @@
  *
  * 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,
-                                                AChoreographer_refreshRateCallback, void* data);
+                                                AChoreographer_refreshRateCallback, void* data)
+        __INTRODUCED_IN(30);
 
 /**
  * Unregisters a callback to be run when the display refresh rate changes, along
@@ -144,9 +154,12 @@
  * callback and associated data pointer are unregistered, then there is a
  * guarantee that when the unregistration completes that that callback will not
  * be run with the data pointer passed.
+ *
+ * Available since API level 30.
  */
 void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
-                                                  AChoreographer_refreshRateCallback, void* data);
+                                                  AChoreographer_refreshRateCallback, void* data)
+        __INTRODUCED_IN(30);
 #endif /* __ANDROID_API__ >= 30 */
 
 __END_DECLS
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 05f4340..ccf3e59 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -58,13 +58,13 @@
     ACONFIGURATION_ORIENTATION_ANY  = 0x0000,
     /**
      * Orientation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
      * resource qualifier.
      */
     ACONFIGURATION_ORIENTATION_PORT = 0x0001,
     /**
      * Orientation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
      * resource qualifier.
      */
     ACONFIGURATION_ORIENTATION_LAND = 0x0002,
@@ -75,7 +75,7 @@
     ACONFIGURATION_TOUCHSCREEN_ANY  = 0x0000,
     /**
      * Touchscreen: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
      * resource qualifier.
      */
     ACONFIGURATION_TOUCHSCREEN_NOTOUCH  = 0x0001,
@@ -83,7 +83,7 @@
     ACONFIGURATION_TOUCHSCREEN_STYLUS  = 0x0002,
     /**
      * Touchscreen: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
      * resource qualifier.
      */
     ACONFIGURATION_TOUCHSCREEN_FINGER  = 0x0003,
@@ -92,43 +92,43 @@
     ACONFIGURATION_DENSITY_DEFAULT = 0,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_LOW = 120,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_MEDIUM = 160,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_TV = 213,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_HIGH = 240,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XHIGH = 320,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XXHIGH = 480,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XXXHIGH = 640,
@@ -141,19 +141,19 @@
     ACONFIGURATION_KEYBOARD_ANY  = 0x0000,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_NOKEYS  = 0x0001,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_QWERTY  = 0x0002,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_12KEY  = 0x0003,
@@ -162,25 +162,25 @@
     ACONFIGURATION_NAVIGATION_ANY  = 0x0000,
     /**
      * Navigation: value corresponding to the
-     * <a href="@@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+     * <a href="@/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_NONAV  = 0x0001,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_DPAD  = 0x0002,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_TRACKBALL  = 0x0003,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_WHEEL  = 0x0004,
@@ -189,19 +189,19 @@
     ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
@@ -210,13 +210,13 @@
     ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
     /**
      * Navigation availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
     /**
      * Navigation availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
@@ -226,28 +226,28 @@
     /**
      * Screen size: value indicating the screen is at least
      * approximately 320x426 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 320x470 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 480x640 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 720x960 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
@@ -256,13 +256,13 @@
     ACONFIGURATION_SCREENLONG_ANY = 0x00,
     /**
      * Screen layout: value that corresponds to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENLONG_NO = 0x1,
     /**
      * Screen layout: value that corresponds to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENLONG_YES = 0x2,
@@ -275,13 +275,13 @@
     ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
     /**
      * Wide color gamut: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
      * nowidecg</a> resource qualifier specified.
      */
     ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
     /**
      * Wide color gamut: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
      * widecg</a> resource qualifier specified.
      */
     ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
@@ -290,13 +290,13 @@
     ACONFIGURATION_HDR_ANY = 0x00,
     /**
      * HDR: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
      * lowdr</a> resource qualifier specified.
      */
     ACONFIGURATION_HDR_NO = 0x1,
     /**
      * HDR: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
      * highdr</a> resource qualifier specified.
      */
     ACONFIGURATION_HDR_YES = 0x2,
@@ -305,38 +305,38 @@
     ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">no
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">no
      * UI mode type</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
 
@@ -344,12 +344,12 @@
     ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
     /**
      * UI night mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
     /**
      * UI night mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2,
 
@@ -366,78 +366,78 @@
     ACONFIGURATION_LAYOUTDIR_ANY  = 0x00,
     /**
      * Layout direction: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
      */
     ACONFIGURATION_LAYOUTDIR_LTR  = 0x01,
     /**
      * Layout direction: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
      */
     ACONFIGURATION_LAYOUTDIR_RTL  = 0x02,
 
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+     * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
      * configuration.
      */
     ACONFIGURATION_MCC = 0x0001,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+     * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
      * configuration.
      */
     ACONFIGURATION_MNC = 0x0002,
     /**
      * Bit mask for
-     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+     * <a href="/guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
      * configuration.
      */
     ACONFIGURATION_LOCALE = 0x0004,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
      * configuration.
      */
     ACONFIGURATION_TOUCHSCREEN = 0x0008,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
      * configuration.
      */
     ACONFIGURATION_KEYBOARD = 0x0010,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
      * configuration.
      */
     ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
      * configuration.
      */
     ACONFIGURATION_NAVIGATION = 0x0040,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
      * configuration.
      */
     ACONFIGURATION_ORIENTATION = 0x0080,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
      * configuration.
      */
     ACONFIGURATION_DENSITY = 0x0100,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
      * configuration.
      */
     ACONFIGURATION_SCREEN_SIZE = 0x0200,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
+     * <a href="/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
      * configuration.
      */
     ACONFIGURATION_VERSION = 0x0400,
@@ -447,27 +447,27 @@
     ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
      * configuration.
      */
     ACONFIGURATION_UI_MODE = 0x1000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
+     * <a href="/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
      * configuration.
      */
     ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
      * configuration.
      */
     ACONFIGURATION_LAYOUTDIR = 0x4000,
     ACONFIGURATION_SCREEN_ROUND = 0x8000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
-     * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+     * and <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
      */
     ACONFIGURATION_COLOR_MODE = 0x10000,
     /**
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 3a87da0..d7e6e41 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -133,6 +133,8 @@
 /**
  * Create a new {@link AImageDecoder} from an {@link AAsset}.
  *
+ * Available since API level 30.
+ *
  * @param asset {@link AAsset} containing encoded image data. Client is still
  *              responsible for calling {@link AAsset_close} on it, which may be
  *              done after deleting the returned {@link AImageDecoder}.
@@ -162,6 +164,8 @@
 /**
  * Create a new {@link AImageDecoder} from a file descriptor.
  *
+ * Available since API level 30.
+ *
  * @param fd Seekable, readable, open file descriptor for encoded data.
  *           Client is still responsible for closing it, which may be done
  *           after deleting the returned {@link AImageDecoder}.
@@ -190,6 +194,8 @@
 /**
  * Create a new AImageDecoder from a buffer.
  *
+ * Available since API level 30.
+ *
  * @param buffer Pointer to encoded data. Must be valid for the entire time
  *               the {@link AImageDecoder} is used.
  * @param length Byte length of buffer.
@@ -217,12 +223,16 @@
 
 /**
  * Delete the AImageDecoder.
+ *
+ * Available since API level 30.
  */
 void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
 
 /**
  * Choose the desired output format.
  *
+ * Available since API level 30.
+ *
  * @param format {@link AndroidBitmapFormat} to use for the output.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure. On failure, the
@@ -247,6 +257,8 @@
  * Pass true to this method to leave them unpremultiplied. This has no effect on an
  * opaque image.
  *
+ * Available since API level 30.
+ *
  * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
@@ -267,6 +279,8 @@
  * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
  * an {@link ADataSpace}.
  *
+ * Available since API level 30.
+ *
  * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
  *                  specifies how to interpret the colors. By default,
  *                  AImageDecoder will decode into the ADataSpace specified by
@@ -292,6 +306,8 @@
  * specified by width and height, and the output image will be the size of the
  * crop rect.
  *
+ * Available since API level 30.
+ *
  * @param width Width of the output (prior to cropping).
  *              This will affect future calls to
  *              {@link AImageDecoder_getMinimumStride}, which will now return
@@ -319,6 +335,8 @@
  * others. This computes the most efficient target size to use to reach a
  * particular sampleSize.
  *
+ * Available since API level 30.
+ *
  * @param sampleSize A subsampling rate of the original image. Must be greater
  *                   than or equal to 1. A sampleSize of 2 means to skip every
  *                   other pixel/line, resulting in a width and height that are
@@ -344,6 +362,8 @@
  * the specified {@link ARect}. Clients will only need to allocate enough memory
  * for the cropped ARect.
  *
+ * Available since API level 30.
+ *
  * @param crop Rectangle describing a crop of the decode. It must be contained inside of
  *             the (possibly scaled, by {@link AImageDecoder_setTargetSize})
  *             image dimensions. This will affect future calls to
@@ -376,6 +396,8 @@
  *
  * This is owned by the {@link AImageDecoder} and will be destroyed when the
  * AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ *
+ * Available since API level 30.
  */
 const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
         const AImageDecoder*) __INTRODUCED_IN(30);
@@ -385,6 +407,8 @@
  * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
  * used to choose a different size or {@link AImageDecoder_setCrop} is used to
  * set a crop rect.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
 
@@ -393,12 +417,16 @@
  * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
  * used to choose a different size or {@link AImageDecoder_setCrop} is used to
  * set a crop rect.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
 
 /**
  * Report the mimeType of the encoded image.
  *
+ * Available since API level 30.
+ *
  * @return a string literal describing the mime type.
  */
 const char* AImageDecoderHeaderInfo_getMimeType(
@@ -409,6 +437,8 @@
  * by default. {@link AImageDecoder} will try to choose one that is sensible
  * for the image and the system. Note that this does not indicate the
  * encoded format of the image.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
         const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -419,6 +449,8 @@
  * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
  * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
  * {@link AImageDecoder_decodeImage} will premultiply pixels by default.
+ *
+ * Available since API level 30.
  */
 int AImageDecoderHeaderInfo_getAlphaFlags(
         const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -429,6 +461,8 @@
  * By default, {@link AImageDecoder_decodeImage} will not do any color
  * conversion.
  *
+ * Available since API level 30.
+ *
  * @return The {@link ADataSpace} representing the way the colors
  *         are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
  *         corresponding ADataSpace). This specifies how to interpret the colors
@@ -452,12 +486,16 @@
  *
  * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
  * cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
+ *
+ * Available since API level 30.
  */
 size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
 
 /**
  * Decode the image into pixels, using the settings of the {@link AImageDecoder}.
  *
+ * Available since API level 30.
+ *
  * @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.
diff --git a/include/android/input.h b/include/android/input.h
index dbfd61e..b04775b 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -55,6 +55,7 @@
 #include <sys/types.h>
 #include <android/keycodes.h>
 #include <android/looper.h>
+#include <jni.h>
 
 #if !defined(__INTRODUCED_IN)
 #define __INTRODUCED_IN(__api_level) /* nothing */
@@ -931,6 +932,15 @@
 /** Get the input event source. */
 int32_t AInputEvent_getSource(const AInputEvent* event);
 
+/**
+ * Releases interface objects created by {@link AKeyEvent_fromJava()}
+ * and {@link AMotionEvent_fromJava()}.
+ * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
+ * The underlying Java object remains valid and does not change its state.
+ */
+
+void AInputEvent_release(const AInputEvent* event);
+
 /*** Accessors for key events only. ***/
 
 /** Get the key event action. */
@@ -977,6 +987,13 @@
  */
 int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
 
+/**
+ * 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);
+
 /*** Accessors for motion events only. ***/
 
 /** Get the combined motion event action code and pointer index. */
@@ -1292,6 +1309,13 @@
 float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
         int32_t axis, size_t pointer_index, size_t history_index);
 
+/**
+ * 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);
 
 struct AInputQueue;
 /**
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/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/android/thermal.h b/include/android/thermal.h
index 3247fa1..83582d6 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,8 +60,6 @@
 extern "C" {
 #endif
 
-#if __ANDROID_API__ >= 30
-
 enum AThermalStatus {
     /** Error in thermal status. */
     ATHERMAL_STATUS_ERROR = -1,
@@ -111,36 +109,45 @@
  */
 typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
 
+#if __ANDROID_API__ >= 30
+
 /**
   * Acquire an instance of the thermal manager. This must be freed using
   * {@link AThermal_releaseManager}.
   *
+  * Available since API level 30.
+  *
   * @return manager instance on success, nullptr on failure.
- */
-AThermalManager* AThermal_acquireManager();
+  */
+AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
 
 /**
  * Release the thermal manager pointer acquired via
  * {@link AThermal_acquireManager}.
  *
- * @param manager The manager to be released.
+ * Available since API level 30.
  *
+ * @param manager The manager to be released.
  */
-void AThermal_releaseManager(AThermalManager *manager);
+void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
 
 /**
   * Gets the current thermal status.
   *
+  * Available since API level 30.
+  *
   * @param manager The manager instance to use to query the thermal status.
   * Acquired via {@link AThermal_acquireManager}.
   *
   * @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
-*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager);
+  */
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
 
 /**
  * Register the thermal status listener for thermal status change.
  *
+ * Available since API level 30.
+ *
  * @param manager The manager instance to use to register.
  * Acquired via {@link AThermal_acquireManager}.
  * @param callback The callback function to be called when thermal status updated.
@@ -152,11 +159,13 @@
  *         EPIPE if communication with the system service has failed.
  */
 int AThermal_registerThermalStatusListener(AThermalManager *manager,
-        AThermal_StatusCallback callback, void *data);
+        AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
 
 /**
  * Unregister the thermal status listener previously resgistered.
  *
+ * Available since API level 30.
+ *
  * @param manager The manager instance to use to unregister.
  * Acquired via {@link AThermal_acquireManager}.
  * @param callback The callback function to be called when thermal status updated.
@@ -168,8 +177,7 @@
  *         EPIPE if communication with the system service has failed.
  */
 int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
-        AThermal_StatusCallback callback, void *data);
-
+        AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
 
 #endif  //  __ANDROID_API__ >= 30
 
diff --git a/include/android/trace.h b/include/android/trace.h
index d59690a..dbad6f6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -115,7 +115,7 @@
 #endif /* __ANDROID_API__ >= 29 */
 
 #ifdef __cplusplus
-};
+}
 #endif
 
 #endif // ANDROID_NATIVE_TRACE_H
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/include/attestation/HmacKeyManager.h
similarity index 60%
copy from libs/binder/ndk/test/IBinderNdkUnitTest.aidl
copy to include/attestation/HmacKeyManager.h
index 6e8e463..571a361 100644
--- a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
+++ b/include/attestation/HmacKeyManager.h
@@ -14,14 +14,19 @@
  * limitations under the License.
  */
 
-// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
-// because it requires libbinder_ndk implementation details or APIs not
-// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
-// over here.
+#include <array>
 
-import IEmpty;
+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};
 
-interface IBinderNdkUnitTest {
-    void takeInterface(IEmpty test);
-    void forceFlushCommands();
-}
+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/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 6100626..334fe34 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,12 +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 <cinttypes>
+#include <optional>
+
 using android::base::StringPrintf;
 
 namespace android {
@@ -39,22 +39,21 @@
  * 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:
+    switch (type) {
+        case ViewportType::INTERNAL:
             return "INTERNAL";
-        case ViewportType::VIEWPORT_EXTERNAL:
+        case ViewportType::EXTERNAL:
             return "EXTERNAL";
-        case ViewportType::VIEWPORT_VIRTUAL:
+        case ViewportType::VIRTUAL:
             return "VIRTUAL";
-        default:
-            return "UNKNOWN";
     }
+    return "UNKNOWN";
 }
 
 /*
@@ -74,36 +73,40 @@
     int32_t physicalBottom;
     int32_t deviceWidth;
     int32_t deviceHeight;
+    bool isActive;
     std::string uniqueId;
     // The actual (hardware) port that the associated display is connected to.
     // Not all viewports will have this specified.
     std::optional<uint8_t> physicalPort;
     ViewportType type;
 
-    DisplayViewport() :
-            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
-            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
-            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
-            type(ViewportType::VIEWPORT_INTERNAL) {
-    }
+    DisplayViewport()
+          : displayId(ADISPLAY_ID_NONE),
+            orientation(DISPLAY_ORIENTATION_0),
+            logicalLeft(0),
+            logicalTop(0),
+            logicalRight(0),
+            logicalBottom(0),
+            physicalLeft(0),
+            physicalTop(0),
+            physicalRight(0),
+            physicalBottom(0),
+            deviceWidth(0),
+            deviceHeight(0),
+            isActive(false),
+            uniqueId(),
+            physicalPort(std::nullopt),
+            type(ViewportType::INTERNAL) {}
 
     bool operator==(const DisplayViewport& other) const {
-        return displayId == other.displayId
-                && orientation == other.orientation
-                && logicalLeft == other.logicalLeft
-                && logicalTop == other.logicalTop
-                && logicalRight == other.logicalRight
-                && logicalBottom == other.logicalBottom
-                && physicalLeft == other.physicalLeft
-                && physicalTop == other.physicalTop
-                && physicalRight == other.physicalRight
-                && physicalBottom == other.physicalBottom
-                && deviceWidth == other.deviceWidth
-                && deviceHeight == other.deviceHeight
-                && uniqueId == other.uniqueId
-                && physicalPort == other.physicalPort
-                && type == other.type;
+        return displayId == other.displayId && orientation == other.orientation &&
+                logicalLeft == other.logicalLeft && logicalTop == other.logicalTop &&
+                logicalRight == other.logicalRight && logicalBottom == other.logicalBottom &&
+                physicalLeft == other.physicalLeft && physicalTop == other.physicalTop &&
+                physicalRight == other.physicalRight && physicalBottom == other.physicalBottom &&
+                deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight &&
+                isActive == other.isActive && uniqueId == other.uniqueId &&
+                physicalPort == other.physicalPort && type == other.type;
     }
 
     bool operator!=(const DisplayViewport& other) const {
@@ -127,25 +130,24 @@
         physicalBottom = height;
         deviceWidth = width;
         deviceHeight = height;
+        isActive = false;
         uniqueId.clear();
         physicalPort = std::nullopt;
-        type = ViewportType::VIEWPORT_INTERNAL;
+        type = ViewportType::INTERNAL;
     }
 
     std::string toString() const {
         return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
-            "logicalFrame=[%d, %d, %d, %d], "
-            "physicalFrame=[%d, %d, %d, %d], "
-            "deviceSize=[%d, %d]",
-            viewportTypeToString(type), displayId,
-            uniqueId.c_str(),
-            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
-            orientation,
-            logicalLeft, logicalTop,
-            logicalRight, logicalBottom,
-            physicalLeft, physicalTop,
-            physicalRight, physicalBottom,
-            deviceWidth, deviceHeight);
+                            "logicalFrame=[%d, %d, %d, %d], "
+                            "physicalFrame=[%d, %d, %d, %d], "
+                            "deviceSize=[%d, %d], "
+                            "isActive=[%d]",
+                            viewportTypeToString(type), displayId, uniqueId.c_str(),
+                            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
+                                         : "<none>",
+                            orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
+                            physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
+                            deviceHeight, isActive);
     }
 };
 
diff --git a/include/input/Flags.h b/include/input/Flags.h
new file mode 100644
index 0000000..4ad9056
--- /dev/null
+++ b/include/input/Flags.h
@@ -0,0 +1,314 @@
+/*
+ * 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 "utils/BitSet.h"
+
+#ifndef __UI_INPUT_FLAGS_H
+#define __UI_INPUT_FLAGS_H
+
+namespace android {
+
+namespace details {
+template <typename F, F 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()
+    //   [F = android::test::TestFlags, V = android::test::TestFlags::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): F = android::test::TestFlags, V = android::test::TestFlags::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::TestFlags::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 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 9e47318..9feab7b 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. */
@@ -280,9 +288,9 @@
 public:
     // Used to divide integer space to ensure no conflict among these sources./
     enum class Source : int32_t {
-        INPUT_READER = 0x0 << SOURCE_SHIFT,
-        INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
-        OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+        INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT),
+        INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT),
+        OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events
     };
     IdGenerator(Source source);
 
@@ -294,7 +302,7 @@
 private:
     const Source mSource;
 
-    static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+    static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT);
 };
 
 /**
@@ -304,11 +312,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 +344,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);
     }
@@ -462,6 +467,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 +522,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; }
 
@@ -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,7 +706,7 @@
 
     // 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__
     status_t readFromParcel(Parcel* parcel);
@@ -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 const char* 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 7f04611..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,14 +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::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.
@@ -75,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..8e00969 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,10 @@
     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);
+    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 +263,20 @@
      */
     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);
     std::string mName;
     android::base::unique_fd mFd;
 
@@ -262,13 +289,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 +322,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 +351,7 @@
     status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
 
 private:
-
-    sp<InputChannel> mChannel;
+    std::shared_ptr<InputChannel> mChannel;
 };
 
 /*
@@ -335,13 +360,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 +436,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 +453,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 +542,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 +552,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 c5e56fd..7372022 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,105 +17,114 @@
 #ifndef _UI_INPUT_WINDOW_H
 #define _UI_INPUT_WINDOW_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"
 
 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 {
-        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_SLIPPERY = 0x20000000,
-    };
+    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_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
-        TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42,
+        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
@@ -127,9 +136,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;
@@ -149,9 +158,8 @@
     // 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;
+    // Transform applied to individual windows.
+    ui::Transform transform;
 
     /*
      * This is filled in by the WM relative to the frame and then translated
@@ -159,13 +167,18 @@
      */
     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
+     * overlay other windows for the purpose of implementing the secure views feature.
+     * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+     * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+     */
+    bool trustedOverlay = false;
     int32_t ownerPid = -1;
     int32_t ownerUid = -1;
-    int32_t inputFeatures = 0;
+    Flags<Feature> inputFeatures;
     int32_t displayId = ADISPLAY_ID_NONE;
     int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
@@ -175,23 +188,19 @@
     void addTouchableRegion(const Rect& region);
 
     bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
-    bool frameContainsPoint(int32_t x, int32_t y) const;
 
-    /* Returns true if the window is of a trusted type that is allowed to silently
-     * overlay other windows for the purpose of implementing the secure views feature.
-     * Trusted overlays, such as IME windows, can partly obscure other windows without causing
-     * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
-     */
-    bool isTrustedOverlay() const;
+    bool frameContainsPoint(int32_t x, int32_t y) const;
 
     bool supportsSplitTouch() const;
 
     bool overlaps(const InputWindowInfo* other) const;
 
-    status_t write(Parcel& output) const;
-    static InputWindowInfo read(const Parcel& from);
-};
+    bool operator==(const InputWindowInfo& inputChannel) const;
 
+    status_t writeToParcel(android::Parcel* parcel) const override;
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
+};
 
 /*
  * Handle for a window that can receive input.
@@ -201,37 +210,36 @@
  */
 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 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::chrono::nanoseconds getDispatchingTimeout(
+            std::chrono::nanoseconds defaultValue) const {
+        return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
     }
 
     /**
      * 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.
@@ -244,13 +252,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..d874347 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -23,12 +23,12 @@
 #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,7 +42,7 @@
  *
  * This object is immutable after it has been loaded.
  */
-class KeyCharacterMap : public RefBase {
+class KeyCharacterMap {
 public:
     enum KeyboardType {
         KEYBOARD_TYPE_UNKNOWN = 0,
@@ -74,18 +74,18 @@
     };
 
     /* 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;
@@ -136,13 +136,14 @@
 
 #ifdef __ANDROID__
     /* 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;
+    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/PropertyMap.h b/include/input/PropertyMap.h
new file mode 100644
index 0000000..3d04331
--- /dev/null
+++ b/include/input/PropertyMap.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <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 status_t load(const String8& filename, PropertyMap** outMap);
+
+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 727865a..ee010a3 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
 #define _LIBINPUT_VELOCITY_TRACKER_H
 
 #include <input/Input.h>
-#include <utils/Timers.h>
 #include <utils/BitSet.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -30,6 +30,22 @@
  */
 class VelocityTracker {
 public:
+    enum class Strategy : int32_t {
+        DEFAULT = -1,
+        MIN = 0,
+        IMPULSE = 0,
+        LSQ1 = 1,
+        LSQ2 = 2,
+        LSQ3 = 3,
+        WLSQ2_DELTA = 4,
+        WLSQ2_CENTRAL = 5,
+        WLSQ2_RECENT = 6,
+        INT1 = 7,
+        INT2 = 8,
+        LEGACY = 9,
+        MAX = LEGACY,
+    };
+
     struct Position {
         float x, y;
     };
@@ -62,8 +78,8 @@
     };
 
     // Creates a velocity tracker using the specified strategy.
-    // If strategy is NULL, uses the default strategy for the platform.
-    VelocityTracker(const char* strategy = nullptr);
+    // If strategy is not provided, uses the default strategy for the platform.
+    VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
     ~VelocityTracker();
 
@@ -102,16 +118,21 @@
     inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
 
 private:
-    static const char* DEFAULT_STRATEGY;
+    // The default velocity tracker strategy.
+    // Although other strategies are available for testing and comparison purposes,
+    // this is the strategy that applications will actually use.  Be very careful
+    // when adjusting the default strategy because it can dramatically affect
+    // (often in a bad way) the user experience.
+    static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
 
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
-    VelocityTrackerStrategy* mStrategy;
+    std::unique_ptr<VelocityTrackerStrategy> mStrategy;
 
-    bool configureStrategy(const char* strategy);
+    bool configureStrategy(const Strategy strategy);
 
-    static VelocityTrackerStrategy* createStrategy(const char* strategy);
+    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
 };
 
 
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
deleted file mode 100644
index 853f0c9..0000000
--- a/include/powermanager/IPowerManager.h
+++ /dev/null
@@ -1,78 +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.
- */
-
-#ifndef ANDROID_IPOWERMANAGER_H
-#define ANDROID_IPOWERMANAGER_H
-
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <hardware/power.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPowerManager : public IInterface
-{
-public:
-    // These transaction IDs must be kept in sync with the method order from
-    // IPowerManager.aidl.
-    enum {
-        ACQUIRE_WAKE_LOCK            = IBinder::FIRST_CALL_TRANSACTION,
-        ACQUIRE_WAKE_LOCK_UID        = IBinder::FIRST_CALL_TRANSACTION + 1,
-        RELEASE_WAKE_LOCK            = IBinder::FIRST_CALL_TRANSACTION + 2,
-        UPDATE_WAKE_LOCK_UIDS        = IBinder::FIRST_CALL_TRANSACTION + 3,
-        POWER_HINT                   = IBinder::FIRST_CALL_TRANSACTION + 4,
-        UPDATE_WAKE_LOCK_SOURCE      = IBinder::FIRST_CALL_TRANSACTION + 5,
-        IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
-        USER_ACTIVITY                = IBinder::FIRST_CALL_TRANSACTION + 7,
-        WAKE_UP                      = IBinder::FIRST_CALL_TRANSACTION + 8,
-        GO_TO_SLEEP                  = IBinder::FIRST_CALL_TRANSACTION + 9,
-        NAP                          = IBinder::FIRST_CALL_TRANSACTION + 10,
-        IS_INTERACTIVE               = IBinder::FIRST_CALL_TRANSACTION + 11,
-        IS_POWER_SAVE_MODE           = IBinder::FIRST_CALL_TRANSACTION + 12,
-        GET_POWER_SAVE_STATE         = IBinder::FIRST_CALL_TRANSACTION + 13,
-        SET_POWER_SAVE_MODE_ENABLED  = IBinder::FIRST_CALL_TRANSACTION + 14,
-        REBOOT                       = IBinder::FIRST_CALL_TRANSACTION + 17,
-        REBOOT_SAFE_MODE             = IBinder::FIRST_CALL_TRANSACTION + 18,
-        SHUTDOWN                     = IBinder::FIRST_CALL_TRANSACTION + 19,
-        CRASH                        = IBinder::FIRST_CALL_TRANSACTION + 20,
-    };
-
-    DECLARE_META_INTERFACE(PowerManager)
-
-    // The parcels created by these methods must be kept in sync with the
-    // corresponding methods from IPowerManager.aidl.
-    // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl
-    virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
-            const String16& packageName, bool isOneWay = false) = 0;
-    virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
-            const String16& packageName, int uid, bool isOneWay = false) = 0;
-    virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
-    virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
-            bool isOneWay = false) = 0;
-    virtual status_t powerHint(int hintId, int data) = 0;
-    virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
-    virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
-    virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
-    virtual status_t crash(const String16& message) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPOWERMANAGER_H
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..dd34c0a
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,79 @@
+/*
+ * 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_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class HalConnector {
+public:
+    HalConnector() = default;
+    virtual ~HalConnector() = default;
+
+    virtual std::unique_ptr<HalWrapper> connect();
+    virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// 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<HalConnector>()) {}
+    explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+          : mHalConnector(std::move(connector)) {}
+    virtual ~PowerHalController() = default;
+
+    void init();
+
+    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<HalConnector> mHalConnector;
+
+    // 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<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
new file mode 100644
index 0000000..ed6f6f3
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,53 @@
+/*
+ * 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_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+namespace android {
+
+namespace power {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+    static void unloadAll();
+    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<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<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+            EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+    PowerHalLoader() = delete;
+    ~PowerHalLoader() = delete;
+};
+
+}; // namespace power
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
new file mode 100644
index 0000000..c3e7601
--- /dev/null
+++ b/include/powermanager/PowerHalWrapper.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.
+ */
+
+#ifndef ANDROID_POWERHALWRAPPER_H
+#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>
+
+namespace android {
+
+namespace power {
+
+// State of Power HAL support for individual apis.
+enum class HalSupport {
+    UNKNOWN = 0,
+    ON = 1,
+    OFF = 2,
+};
+
+// State of the Power HAL api call result.
+enum class HalResult {
+    SUCCESSFUL = 0,
+    FAILED = 1,
+    UNSUPPORTED = 2,
+};
+
+// Wrapper for Power HAL handlers.
+class HalWrapper {
+public:
+    virtual ~HalWrapper() = default;
+
+    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 EmptyHalWrapper : public HalWrapper {
+public:
+    EmptyHalWrapper() = default;
+    ~EmptyHalWrapper() = default;
+
+    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 HidlHalWrapperV1_0 : public HalWrapper {
+public:
+    explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+          : mHandleV1_0(std::move(Hal)) {}
+    virtual ~HidlHalWrapperV1_0() = default;
+
+    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+protected:
+    virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+
+private:
+    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 HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+    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 HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+                                    uint32_t data) override;
+
+private:
+    sp<hardware::power::V1_1::IPower> mHandleV1_1;
+};
+
+// Wrapper for the AIDL Power HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+    explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+    virtual ~AidlHalWrapper() = default;
+
+    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<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<HalSupport>,
+               static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+            mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
+    // Android framework only sends mode upto DISPLAY_INACTIVE.
+    // Need to increase the array if more mode supported.
+    std::array<std::atomic<HalSupport>,
+               static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+            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/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/Android.bp b/libs/binder/Android.bp
index db4aba8..9675a53 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",
@@ -62,6 +64,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 +78,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",
     ],
@@ -123,6 +127,10 @@
         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 2174ce2..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
@@ -91,16 +92,16 @@
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
-    return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+    return noteOp(op, uid, callingPackage, {},
             String16("Legacy AppOpsManager.noteOp call"));
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-        const std::unique_ptr<String16>& attributionTag, const String16& message) {
+        const std::optional<String16>& attributionTag, const String16& message) {
     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;
@@ -108,28 +109,29 @@
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
         bool startIfModeDefault) {
-    return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+    return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, {},
             String16("Legacy AppOpsManager.startOpNoThrow call"));
 }
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-        bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+        bool startIfModeDefault, const std::optional<String16>& attributionTag,
         const String16& message) {
     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;
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
-    finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+    finishOp(op, uid, callingPackage, {});
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
-        const std::unique_ptr<String16>& attributionTag) {
+        const std::optional<String16>& attributionTag) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
         service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e0fb543..6ca3b16 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;
@@ -279,6 +282,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/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 0714723..ee0cd62 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -22,6 +22,8 @@
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 
+#include <optional>
+
 namespace android {
 
 // ----------------------------------------------------------------------
@@ -47,16 +49,17 @@
     }
 
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-                const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
-                const String16& message) {
+                const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+                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;
@@ -64,8 +67,9 @@
     }
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-                const String16& packageName, const std::unique_ptr<String16>& attributionTag,
-                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
+                const String16& packageName, const std::optional<String16>& attributionTag,
+                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+                bool shouldCollectMessage) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
@@ -73,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;
@@ -83,7 +88,7 @@
     }
 
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
+            const String16& packageName, const std::optional<String16>& attributionTag) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
@@ -182,12 +187,13 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> attributionTag;
+            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;
@@ -198,13 +204,14 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> attributionTag;
+            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;
@@ -215,7 +222,7 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> attributionTag;
+            std::optional<String16> attributionTag;
             data.readString16(&attributionTag);
             finishOperation(token, code, uid, packageName, attributionTag);
             reply->writeNoException();
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index c2bb811..d8b44f9 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -82,10 +82,10 @@
     explicit BpMemoryHeap(const sp<IBinder>& impl);
     virtual ~BpMemoryHeap();
 
-    virtual int getHeapID() const;
-    virtual void* getBase() const;
-    virtual size_t getSize() const;
-    virtual uint32_t getFlags() const;
+    int getHeapID() const override;
+    void* getBase() const override;
+    size_t getSize() const override;
+    uint32_t getFlags() const override;
     off_t getOffset() const override;
 
 private:
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index d67ce15..0c71ed8 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -614,7 +614,7 @@
     talkWithDriver(false);
 }
 
-int IPCThreadState::setupPolling(int* fd)
+status_t IPCThreadState::setupPolling(int* fd)
 {
     if (mProcess->mDriverFD < 0) {
         return -EBADF;
@@ -679,7 +679,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 +860,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");
@@ -1316,6 +1320,26 @@
         }
 }
 
+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*/,
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9888b59..9aa82d9 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;
@@ -85,8 +86,8 @@
     sp<AidlServiceManager> mTheRealServiceManager;
 };
 
-static std::once_flag gSmOnce;
-static sp<IServiceManager> gDefaultServiceManager;
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
 
 sp<IServiceManager> defaultServiceManager()
 {
@@ -95,6 +96,7 @@
         while (sm == nullptr) {
             sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
             if (sm == nullptr) {
+                ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
                 sleep(1);
             }
         }
@@ -205,6 +207,10 @@
  : mTheRealServiceManager(impl)
 {}
 
+// This implementation could be simplified and made more efficient by delegating
+// to waitForService. However, this changes the threading structure in some
+// cases and could potentially break prebuilts. Once we have higher logistical
+// complexity, this could be attempted.
 sp<IBinder> ServiceManagerShim::getService(const String16& name) const
 {
     static bool gSystemBootCompleted = false;
@@ -214,7 +220,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__
@@ -228,15 +235,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;
@@ -321,6 +334,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, [&] {
@@ -329,6 +347,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.
@@ -342,8 +362,6 @@
             return nullptr;
         }
         if (out != nullptr) return out;
-
-        ALOGW("Waited one second for %s", name.c_str());
     }
 }
 
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6f49aa1..325e204 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -45,25 +45,31 @@
     Status onClients(const sp<IBinder>& service, bool clients) override;
 
 private:
+    struct Service {
+        sp<IBinder> service;
+        bool allowIsolated;
+        int dumpFlags;
+
+        // whether, based on onClients calls, we know we have a client for this
+        // service or not
+        bool clients = false;
+    };
+
+    /**
+     * Looks up a service guaranteed to be registered (service from onClients).
+     */
+    std::map<std::string, Service>::iterator assertRegisteredService(const sp<IBinder>& service);
+
     /**
      * Unregisters all services that we can. If we can't unregister all, re-register other
      * services.
      */
     void tryShutdown();
 
-    /**
-     * Counter of the number of services that currently have at least one client.
-     */
+    // count of services with clients
     size_t mNumConnectedServices;
 
-    struct Service {
-        sp<IBinder> service;
-        bool allowIsolated;
-        int dumpFlags;
-    };
-    /**
-     * Map of registered names and services
-     */
+    // map of registered names and services
     std::map<std::string, Service> mRegisteredServices;
 
     bool mForcePersist;
@@ -89,12 +95,28 @@
         }
 
         // Only add this when a service is added for the first time, as it is not removed
-        mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+        mRegisteredServices[name] = {
+              .service = service,
+              .allowIsolated = allowIsolated,
+              .dumpFlags = dumpFlags
+        };
     }
 
     return true;
 }
 
+std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::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;
+        (void) name;
+        if (registered.service != service) continue;
+        return it;
+    }
+    LOG_ALWAYS_FATAL("Got callback on service which we did not register: %s", String8(service->getInterfaceDescriptor()).c_str());
+    __builtin_unreachable();
+}
+
 void ClientCounterCallback::forcePersist(bool persist) {
     mForcePersist = persist;
     if(!mForcePersist) {
@@ -108,15 +130,25 @@
  * invocations could occur on different threads however.
  */
 Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
-    if (clients) {
-        mNumConnectedServices++;
-    } else {
-        mNumConnectedServices--;
+    auto & [name, registered] = *assertRegisteredService(service);
+    if (registered.clients == clients) {
+        LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
+                         "notified has clients: %d", name.c_str(), registered.clients, clients);
+    }
+    registered.clients = clients;
+
+    // update cache count of clients
+    {
+         size_t numWithClients = 0;
+         for (const auto& [name, registered] : mRegisteredServices) {
+             (void) name;
+             if (registered.clients) numWithClients++;
+         }
+         mNumConnectedServices = numWithClients;
     }
 
     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
-          mNumConnectedServices, mRegisteredServices.size(),
-          String8(service->getInterfaceDescriptor()).string(), clients);
+          mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
 
     tryShutdown();
     return Status::ok();
@@ -192,4 +224,4 @@
 }
 
 }  // namespace hardware
-}  // namespace android
\ No newline at end of file
+}  // namespace android
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/Parcel.cpp b/libs/binder/Parcel.cpp
index 9642a87..14ab60f 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>
@@ -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);
 }
 
@@ -751,6 +762,13 @@
     return NO_ERROR;
 }
 
+status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) {
+  if (!str) {
+    return writeInt32(-1);
+  }
+  return writeUtf8AsUtf16(*str);
+}
+
 status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
   if (!str) {
     return writeInt32(-1);
@@ -775,6 +793,12 @@
     return writeByteVectorInternal(val.data(), val.size());
 }
 
+status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val)
+{
+    if (!val) return writeInt32(-1);
+    return writeByteVectorInternal(val->data(), val->size());
+}
+
 status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
 {
     if (!val) return writeInt32(-1);
@@ -785,6 +809,12 @@
     return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
 }
 
+status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val)
+{
+    if (!val) return writeInt32(-1);
+    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+
 status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
 {
     if (!val) return writeInt32(-1);
@@ -796,6 +826,11 @@
     return writeTypedVector(val, &Parcel::writeInt32);
 }
 
+status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeInt32);
+}
+
 status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeInt32);
@@ -806,6 +841,11 @@
     return writeTypedVector(val, &Parcel::writeInt64);
 }
 
+status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeInt64);
+}
+
 status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeInt64);
@@ -816,6 +856,11 @@
     return writeTypedVector(val, &Parcel::writeUint64);
 }
 
+status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeUint64);
+}
+
 status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeUint64);
@@ -826,6 +871,11 @@
     return writeTypedVector(val, &Parcel::writeFloat);
 }
 
+status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeFloat);
+}
+
 status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeFloat);
@@ -836,6 +886,11 @@
     return writeTypedVector(val, &Parcel::writeDouble);
 }
 
+status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeDouble);
+}
+
 status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeDouble);
@@ -846,6 +901,11 @@
     return writeTypedVector(val, &Parcel::writeBool);
 }
 
+status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeBool);
+}
+
 status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeBool);
@@ -856,6 +916,11 @@
     return writeTypedVector(val, &Parcel::writeChar);
 }
 
+status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeChar);
+}
+
 status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeChar);
@@ -867,12 +932,23 @@
 }
 
 status_t Parcel::writeString16Vector(
+        const std::optional<std::vector<std::optional<String16>>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeString16);
+}
+
+status_t Parcel::writeString16Vector(
         const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeString16);
 }
 
 status_t Parcel::writeUtf8VectorAsUtf16Vector(
+                        const std::optional<std::vector<std::optional<std::string>>>& val) {
+    return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
+status_t Parcel::writeUtf8VectorAsUtf16Vector(
                         const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
     return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
 }
@@ -1007,6 +1083,15 @@
     return err;
 }
 
+status_t Parcel::writeString16(const std::optional<String16>& str)
+{
+    if (!str) {
+        return writeInt32(-1);
+    }
+
+    return writeString16(*str);
+}
+
 status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
 {
     if (!str) {
@@ -1049,11 +1134,20 @@
     return writeTypedVector(val, &Parcel::writeStrongBinder);
 }
 
+status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
+}
+
 status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val)
 {
     return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
 }
 
+status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
+}
+
 status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
     return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
 }
@@ -1152,6 +1246,10 @@
     return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
 }
 
+status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) {
+    return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
+}
+
 status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
     return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
 }
@@ -1427,7 +1525,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) {
@@ -1460,7 +1558,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:
@@ -1485,6 +1583,17 @@
     return readByteVectorInternal(val, size);
 }
 
+status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const {
+    size_t size;
+    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+    if (!*val) {
+        // reserveOutVector does not create the out vector if size is < 0.
+        // This occurs when writing a null byte vector.
+        return OK;
+    }
+    return readByteVectorInternal(&**val, size);
+}
+
 status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
     size_t size;
     if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1496,6 +1605,17 @@
     return readByteVectorInternal(val->get(), size);
 }
 
+status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const {
+    size_t size;
+    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+    if (!*val) {
+        // reserveOutVector does not create the out vector if size is < 0.
+        // This occurs when writing a null byte vector.
+        return OK;
+    }
+    return readByteVectorInternal(&**val, size);
+}
+
 status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
     size_t size;
     if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1507,6 +1627,10 @@
     return readByteVectorInternal(val->get(), size);
 }
 
+status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readInt32);
+}
+
 status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
     return readNullableTypedVector(val, &Parcel::readInt32);
 }
@@ -1515,6 +1639,10 @@
     return readTypedVector(val, &Parcel::readInt32);
 }
 
+status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readInt64);
+}
+
 status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const {
     return readNullableTypedVector(val, &Parcel::readInt64);
 }
@@ -1523,6 +1651,10 @@
     return readTypedVector(val, &Parcel::readInt64);
 }
 
+status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readUint64);
+}
+
 status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
     return readNullableTypedVector(val, &Parcel::readUint64);
 }
@@ -1531,6 +1663,10 @@
     return readTypedVector(val, &Parcel::readUint64);
 }
 
+status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readFloat);
+}
+
 status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
     return readNullableTypedVector(val, &Parcel::readFloat);
 }
@@ -1539,6 +1675,10 @@
     return readTypedVector(val, &Parcel::readFloat);
 }
 
+status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readDouble);
+}
+
 status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const {
     return readNullableTypedVector(val, &Parcel::readDouble);
 }
@@ -1547,6 +1687,28 @@
     return readTypedVector(val, &Parcel::readDouble);
 }
 
+status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
+    const int32_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    val->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    val->emplace();
+
+    status = readBoolVector(&**val);
+
+    if (status != OK) {
+        val->reset();
+    }
+
+    return status;
+}
+
 status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
     const int32_t start = dataPosition();
     int32_t size;
@@ -1599,6 +1761,10 @@
     return OK;
 }
 
+status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readChar);
+}
+
 status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const {
     return readNullableTypedVector(val, &Parcel::readChar);
 }
@@ -1608,6 +1774,11 @@
 }
 
 status_t Parcel::readString16Vector(
+        std::optional<std::vector<std::optional<String16>>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readString16);
+}
+
+status_t Parcel::readString16Vector(
         std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const {
     return readNullableTypedVector(val, &Parcel::readString16);
 }
@@ -1617,6 +1788,11 @@
 }
 
 status_t Parcel::readUtf8VectorFromUtf16Vector(
+        std::optional<std::vector<std::optional<std::string>>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
+
+status_t Parcel::readUtf8VectorFromUtf16Vector(
         std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
     return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
 }
@@ -1808,6 +1984,21 @@
     return NO_ERROR;
 }
 
+status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const {
+    const int32_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    str->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    str->emplace();
+    return readUtf8FromUtf16(&**str);
+}
+
 status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
     const int32_t start = dataPosition();
     int32_t size;
@@ -1886,6 +2077,29 @@
     return String16();
 }
 
+status_t Parcel::readString16(std::optional<String16>* pArg) const
+{
+    const int32_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    pArg->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    pArg->emplace();
+
+    status = readString16(&**pArg);
+
+    if (status != OK) {
+        pArg->reset();
+    }
+
+    return status;
+}
+
 status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const
 {
     const int32_t start = dataPosition();
@@ -2091,6 +2305,10 @@
     return OK;
 }
 
+status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
+}
+
 status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
     return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
 }
@@ -2436,7 +2654,7 @@
     size_t newSize = ((mDataSize+len)*3)/2;
     return (newSize <= mDataSize)
             ? (status_t) NO_MEMORY
-            : continueWrite(newSize);
+            : continueWrite(std::max(newSize, (size_t) 128));
 }
 
 status_t Parcel::restartWrite(size_t desired)
@@ -2460,7 +2678,7 @@
 
     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;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4b773e8..a530565 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));
     }
 
@@ -328,6 +336,8 @@
 }
 
 status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
+    LOG_ALWAYS_FATAL_IF(mThreadPoolStarted && maxThreads < mMaxThreads,
+           "Binder threadpool cannot be shrunk after starting");
     status_t result = NO_ERROR;
     if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) {
         mMaxThreads = maxThreads;
@@ -383,14 +393,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/Status.cpp b/libs/binder/Status.cpp
index 674f065..64ab7a9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -193,13 +193,15 @@
     }
 
     status_t status = parcel->writeInt32(mException);
-    if (status != OK) { return status; }
+    if (status != OK) return status;
     if (mException == EX_NONE) {
         // We have no more information to write.
         return status;
     }
     status = parcel->writeString16(String16(mMessage));
+    if (status != OK) return status;
     status = parcel->writeInt32(0); // Empty remote stack trace header
+    if (status != OK) return status;
     if (mException == EX_SERVICE_SPECIFIC) {
         status = parcel->writeInt32(mErrorCode);
     } else if (mException == EX_PARCELABLE) {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 9aa7651..c232283 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -7,6 +7,9 @@
       "name": "binderVendorDoubleLoadTest"
     },
     {
+      "name": "binderAllocationLimits"
+    },
+    {
       "name": "binderDriverInterfaceTest"
     },
     {
@@ -29,6 +32,17 @@
     },
     {
       "name": "libbinderthreadstateutils_test"
+    },
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
+        },
+        {
+          "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+        }
+      ]
     }
   ]
 }
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 52c730c..8c0495c 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -135,6 +135,7 @@
 
     PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
     PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+    PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16),
     [] (const ::android::Parcel& p, uint8_t /*data*/) {
         FUZZ_LOG() << "about to read c-str";
         const char* str = p.readCString();
@@ -143,6 +144,7 @@
     PARCEL_READ_OPT_STATUS(android::String8, readString8),
     PARCEL_READ_OPT_STATUS(android::String16, readString16),
     PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+    PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
     [] (const ::android::Parcel& p, uint8_t /*data*/) {
         FUZZ_LOG() << "about to readString16Inplace";
         size_t outLen = 0;
@@ -156,17 +158,22 @@
     // TODO(b/131868573): can force read of arbitrarily sized vector
     // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
     // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
     // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
 
     // only reading one parcelable type for now
     // TODO(b/131868573): can force read of arbitrarily sized vector
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
     // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
     PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
     PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+    PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
 
     // only reading one binder type for now
     PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
@@ -174,30 +181,42 @@
 
     // TODO(b/131868573): can force read of arbitrarily sized vector
     // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+    // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
     // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
 
     // TODO(b/131868573): can force read of arbitrarily sized vector
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
     // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
     // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
     // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
     // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
     // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
     // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
     // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
     // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
     // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
     // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
     // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
 
     [] (const android::Parcel& p, uint8_t /*len*/) {
@@ -234,6 +253,7 @@
 
     // TODO(b/131868573): can force read of arbitrarily sized vector
     // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+    // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
     // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
 
     [] (const android::Parcel& p, uint8_t len) {
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6afcd77..233f12a 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -21,6 +21,8 @@
 
 #include <utils/threads.h>
 
+#include <optional>
+
 #ifdef __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif
@@ -131,7 +133,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();
@@ -143,18 +149,18 @@
     //              const String16&) instead
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-            const std::unique_ptr<String16>& attributionTag, const String16& message);
+            const std::optional<String16>& attributionTag, const String16& message);
     // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
     //              const String16&) instead
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
             bool startIfModeDefault);
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-            bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+            bool startIfModeDefault, const std::optional<String16>& attributionTag,
             const String16& message);
     // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
-            const std::unique_ptr<String16>& attributionTag);
+            const std::optional<String16>& attributionTag);
     void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback);
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 74e52db..f3fea16 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -72,6 +72,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:
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 8e871b8..378a911 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -137,7 +137,6 @@
             volatile int32_t    mObitsSent;
             Vector<Obituary>*   mObituaries;
             ObjectManager       mObjects;
-            Parcel*             mConstantData;
     mutable String16            mDescriptorCache;
             int32_t             mTrackedUid;
 
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1ffb8de..de7d12f 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -21,6 +21,8 @@
 #include <binder/IAppOpsCallback.h>
 #include <binder/IInterface.h>
 
+#include <optional>
+
 #ifdef __ANDROID_VNDK__
 #error "This header is not visible to vendors"
 #endif
@@ -36,13 +38,14 @@
 
     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::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
-            const String16& message) = 0;
+            const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+            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::unique_ptr<String16>& attributionTag,
-            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
+            const String16& packageName, const std::optional<String16>& attributionTag,
+            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::unique_ptr<String16>& attributionTag) = 0;
+            const String16& packageName, const std::optional<String16>& attributionTag) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 64604b7..eea0e89 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -173,6 +173,10 @@
      * The @a cookie is optional -- if non-NULL, it should be a
      * memory address that you own (that is, you know it is unique).
      *
+     * @note When all references to the binder being linked to are dropped, the
+     * recipient is automatically unlinked. So, you must hold onto a binder in
+     * order to receive death notifications about it.
+     *
      * @note 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
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 3a401ad..468cc16 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -20,6 +20,8 @@
 
 #include <binder/Binder.h>
 
+#include <assert.h>
+
 namespace android {
 
 // ----------------------------------------------------------------------
@@ -155,7 +157,11 @@
     std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl;           \
     bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\
     {                                                                   \
-        if (!I##INTERFACE::default_impl && impl) {                      \
+        /* Only one user of this interface can use this function     */ \
+        /* at a time. This is a heuristic to detect if two different */ \
+        /* users in the same process use this function.              */ \
+        assert(!I##INTERFACE::default_impl);                            \
+        if (impl) {                                                     \
             I##INTERFACE::default_impl = std::move(impl);               \
             return true;                                                \
         }                                                               \
@@ -228,6 +234,7 @@
   "android.gui.IGraphicBufferConsumer",
   "android.gui.IRegionSamplingListener",
   "android.gui.ITransactionComposerListener",
+  "android.gui.IScreenCaptureListener",
   "android.gui.SensorEventConnection",
   "android.gui.SensorServer",
   "android.hardware.ICamera",
@@ -241,8 +248,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",
@@ -251,8 +256,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",
@@ -276,7 +279,6 @@
   "android.os.IComplexTypeInterface",
   "android.os.IPermissionController",
   "android.os.IPingResponder",
-  "android.os.IPowerManager",
   "android.os.IProcessInfoService",
   "android.os.ISchedulingPolicyService",
   "android.os.IStringConstants",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 4818889..cdeccea 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -34,17 +34,47 @@
 public:
     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);
+
             sp<ProcessState>    process();
             
             status_t            clearLastError();
 
+            /**
+             * Returns the PID of the process which has made the current binder
+             * call. If not in a binder call, this will return getpid. If the
+             * call is oneway, this will return 0.
+             */
             pid_t               getCallingPid() const;
-            // nullptr if unavailable
-            //
-            // 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.
+
+            /**
+             * 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
+             * 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.
+             */
             const char*         getCallingSid() const;
+
+            /**
+             * Returns the UID of the process which has made the current binder
+             * call. If not in a binder call, this will return 0.
+             */
             uid_t               getCallingUid() const;
 
             void                setStrictModePolicy(int32_t policy);
@@ -69,8 +99,8 @@
             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();
 
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/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 3fccddc..52bd5de 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -57,28 +57,20 @@
     virtual ~MemoryHeapBase();
 
     /* implement IMemoryHeap interface */
-    virtual int         getHeapID() const;
+    int         getHeapID() const override;
 
     /* virtual address of the heap. returns MAP_FAILED in case of error */
-    virtual void*       getBase() const;
+    void*       getBase() const override;
 
-    virtual size_t      getSize() const;
-    virtual uint32_t    getFlags() const;
-            off_t       getOffset() const override;
+    size_t      getSize() const override;
+    uint32_t    getFlags() const override;
+    off_t       getOffset() const override;
 
     const char*         getDevice() const;
 
     /* 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
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
deleted file mode 100644
index b605bd3..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 <memory>
-#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::unique_ptr<T>;
-
-template <typename T, typename... Args>
-inline nullable<T> make_nullable(Args&&... args) {
-    return std::make_unique<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 c1f64fb..b6cfb8e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -121,6 +121,7 @@
     status_t            writeString8(const String8& str);
     status_t            writeString8(const char* str, size_t len);
     status_t            writeString16(const String16& str);
+    status_t            writeString16(const std::optional<String16>& str);
     status_t            writeString16(const std::unique_ptr<String16>& str);
     status_t            writeString16(const char16_t* str, size_t len);
     status_t            writeStrongBinder(const sp<IBinder>& val);
@@ -132,33 +133,48 @@
 
     // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
     status_t            writeUtf8AsUtf16(const std::string& str);
+    status_t            writeUtf8AsUtf16(const std::optional<std::string>& str);
     status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
 
+    status_t            writeByteVector(const std::optional<std::vector<int8_t>>& val);
     status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
     status_t            writeByteVector(const std::vector<int8_t>& val);
+    status_t            writeByteVector(const std::optional<std::vector<uint8_t>>& val);
     status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
     status_t            writeByteVector(const std::vector<uint8_t>& val);
+    status_t            writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
     status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
     status_t            writeInt32Vector(const std::vector<int32_t>& val);
+    status_t            writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
     status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
     status_t            writeInt64Vector(const std::vector<int64_t>& val);
+    status_t            writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
     status_t            writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
     status_t            writeUint64Vector(const std::vector<uint64_t>& val);
+    status_t            writeFloatVector(const std::optional<std::vector<float>>& val);
     status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
     status_t            writeFloatVector(const std::vector<float>& val);
+    status_t            writeDoubleVector(const std::optional<std::vector<double>>& val);
     status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
     status_t            writeDoubleVector(const std::vector<double>& val);
+    status_t            writeBoolVector(const std::optional<std::vector<bool>>& val);
     status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
     status_t            writeBoolVector(const std::vector<bool>& val);
+    status_t            writeCharVector(const std::optional<std::vector<char16_t>>& val);
     status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
     status_t            writeCharVector(const std::vector<char16_t>& val);
     status_t            writeString16Vector(
+                            const std::optional<std::vector<std::optional<String16>>>& val);
+    status_t            writeString16Vector(
                             const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
     status_t            writeString16Vector(const std::vector<String16>& val);
     status_t            writeUtf8VectorAsUtf16Vector(
+                            const std::optional<std::vector<std::optional<std::string>>>& val);
+    status_t            writeUtf8VectorAsUtf16Vector(
                             const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
     status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
 
+    status_t            writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
     status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
     status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
 
@@ -167,14 +183,20 @@
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::vector<T>& val);
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+    status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
+    template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
     // Write an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::vector<T>& val);
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+    status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
+    template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
 
     template<typename T>
+    status_t            writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
+    template<typename T>
     status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
     template<typename T>
     status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
@@ -182,6 +204,8 @@
     status_t            writeParcelableVector(const std::vector<T>& val);
 
     template<typename T>
+    status_t            writeNullableParcelable(const std::optional<T>& parcelable);
+    template<typename T>
     status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable);
 
     status_t            writeParcelable(const Parcelable& parcelable);
@@ -195,6 +219,8 @@
     template<typename T>
     status_t            writeVectorSize(const std::vector<T>& val);
     template<typename T>
+    status_t            writeVectorSize(const std::optional<std::vector<T>>& val);
+    template<typename T>
     status_t            writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
 
     // Place a native_handle into the parcel (the native_handle's file-
@@ -230,6 +256,8 @@
     // Place a vector of file desciptors into the parcel. Each descriptor is
     // dup'd as in writeDupFileDescriptor
     status_t            writeUniqueFileDescriptorVector(
+                            const std::optional<std::vector<base::unique_fd>>& val);
+    status_t            writeUniqueFileDescriptorVector(
                             const std::unique_ptr<std::vector<base::unique_fd>>& val);
     status_t            writeUniqueFileDescriptorVector(
                             const std::vector<base::unique_fd>& val);
@@ -279,6 +307,7 @@
 
     // Read a UTF16 encoded string, convert to UTF8
     status_t            readUtf8FromUtf16(std::string* str) const;
+    status_t            readUtf8FromUtf16(std::optional<std::string>* str) const;
     status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
 
     const char*         readCString() const;
@@ -287,27 +316,34 @@
     const char*         readString8Inplace(size_t* outLen) const;
     String16            readString16() const;
     status_t            readString16(String16* pArg) const;
+    status_t            readString16(std::optional<String16>* pArg) const;
     status_t            readString16(std::unique_ptr<String16>* pArg) const;
     const char16_t*     readString16Inplace(size_t* outLen) const;
     sp<IBinder>         readStrongBinder() const;
     status_t            readStrongBinder(sp<IBinder>* val) const;
     status_t            readNullableStrongBinder(sp<IBinder>* val) const;
 
-
     // Read an Enum vector with underlying type int8_t.
     // Does not use padding; each byte is contiguous.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::vector<T>* val) const;
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+    template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+    status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
     // Read an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::vector<T>* val) const;
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+    template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+    status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
 
     template<typename T>
     status_t            readParcelableVector(
+                            std::optional<std::vector<std::optional<T>>>* val) const;
+    template<typename T>
+    status_t            readParcelableVector(
                             std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
     template<typename T>
     status_t            readParcelableVector(std::vector<T>* val) const;
@@ -315,6 +351,8 @@
     status_t            readParcelable(Parcelable* parcelable) const;
 
     template<typename T>
+    status_t            readParcelable(std::optional<T>* parcelable) const;
+    template<typename T>
     status_t            readParcelable(std::unique_ptr<T>* parcelable) const;
 
     template<typename T>
@@ -323,31 +361,45 @@
     template<typename T>
     status_t            readNullableStrongBinder(sp<T>* val) const;
 
+    status_t            readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
     status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
     status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
 
+    status_t            readByteVector(std::optional<std::vector<int8_t>>* val) const;
     status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
     status_t            readByteVector(std::vector<int8_t>* val) const;
+    status_t            readByteVector(std::optional<std::vector<uint8_t>>* val) const;
     status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
     status_t            readByteVector(std::vector<uint8_t>* val) const;
+    status_t            readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
     status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
     status_t            readInt32Vector(std::vector<int32_t>* val) const;
+    status_t            readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
     status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
     status_t            readInt64Vector(std::vector<int64_t>* val) const;
+    status_t            readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
     status_t            readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
     status_t            readUint64Vector(std::vector<uint64_t>* val) const;
+    status_t            readFloatVector(std::optional<std::vector<float>>* val) const;
     status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
     status_t            readFloatVector(std::vector<float>* val) const;
+    status_t            readDoubleVector(std::optional<std::vector<double>>* val) const;
     status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
     status_t            readDoubleVector(std::vector<double>* val) const;
+    status_t            readBoolVector(std::optional<std::vector<bool>>* val) const;
     status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
     status_t            readBoolVector(std::vector<bool>* val) const;
+    status_t            readCharVector(std::optional<std::vector<char16_t>>* val) const;
     status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
     status_t            readCharVector(std::vector<char16_t>* val) const;
     status_t            readString16Vector(
+                            std::optional<std::vector<std::optional<String16>>>* val) const;
+    status_t            readString16Vector(
                             std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
     status_t            readString16Vector(std::vector<String16>* val) const;
     status_t            readUtf8VectorFromUtf16Vector(
+                            std::optional<std::vector<std::optional<std::string>>>* val) const;
+    status_t            readUtf8VectorFromUtf16Vector(
                             std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
     status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
@@ -360,10 +412,15 @@
     template<typename T>
     status_t            resizeOutVector(std::vector<T>* val) const;
     template<typename T>
+    status_t            resizeOutVector(std::optional<std::vector<T>>* val) const;
+    template<typename T>
     status_t            resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
     template<typename T>
     status_t            reserveOutVector(std::vector<T>* val, size_t* size) const;
     template<typename T>
+    status_t            reserveOutVector(std::optional<std::vector<T>>* val,
+                                         size_t* size) const;
+    template<typename T>
     status_t            reserveOutVector(std::unique_ptr<std::vector<T>>* val,
                                          size_t* size) const;
 
@@ -399,6 +456,8 @@
 
     // Retrieve a vector of smart file descriptors from the parcel.
     status_t            readUniqueFileDescriptorVector(
+                            std::optional<std::vector<base::unique_fd>>* val) const;
+    status_t            readUniqueFileDescriptorVector(
                             std::unique_ptr<std::vector<base::unique_fd>>* val) const;
     status_t            readUniqueFileDescriptorVector(
                             std::vector<base::unique_fd>* val) const;
@@ -492,6 +551,9 @@
     status_t            unsafeReadTypedVector(std::vector<T>* val,
                                               status_t(Parcel::*read_func)(U*) const) const;
     template<typename T>
+    status_t            readNullableTypedVector(std::optional<std::vector<T>>* val,
+                                                status_t(Parcel::*read_func)(T*) const) const;
+    template<typename T>
     status_t            readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
                                                 status_t(Parcel::*read_func)(T*) const) const;
     template<typename T>
@@ -501,9 +563,15 @@
     status_t            unsafeWriteTypedVector(const std::vector<T>& val,
                                                status_t(Parcel::*write_func)(U));
     template<typename T>
+    status_t            writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+                                                 status_t(Parcel::*write_func)(const T&));
+    template<typename T>
     status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
                                                  status_t(Parcel::*write_func)(const T&));
     template<typename T>
+    status_t            writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+                                                 status_t(Parcel::*write_func)(T));
+    template<typename T>
     status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
                                                  status_t(Parcel::*write_func)(T));
     template<typename T>
@@ -691,6 +759,15 @@
 }
 
 template<typename T>
+status_t Parcel::writeVectorSize(const std::optional<std::vector<T>>& val) {
+    if (!val) {
+        return writeInt32(-1);
+    }
+
+    return writeVectorSize(*val);
+}
+
+template<typename T>
 status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) {
     if (!val) {
         return writeInt32(-1);
@@ -715,6 +792,22 @@
 }
 
 template<typename T>
+status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
+    int32_t size;
+    status_t err = readInt32(&size);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    val->reset();
+    if (size >= 0) {
+        val->emplace(size_t(size));
+    }
+
+    return OK;
+}
+
+template<typename T>
 status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
     int32_t size;
     status_t err = readInt32(&size);
@@ -747,6 +840,25 @@
 }
 
 template<typename T>
+status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
+    int32_t read_size;
+    status_t err = readInt32(&read_size);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (read_size >= 0) {
+        *size = static_cast<size_t>(read_size);
+        val->emplace();
+        (*val)->reserve(*size);
+    } else {
+        val->reset();
+    }
+
+    return OK;
+}
+
+template<typename T>
 status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
                                   size_t* size) const {
     int32_t read_size;
@@ -841,6 +953,30 @@
 }
 
 template<typename T>
+status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val,
+                                         status_t(Parcel::*read_func)(T*) const) const {
+    const size_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    val->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    val->emplace();
+
+    status = unsafeReadTypedVector(&**val, read_func);
+
+    if (status != OK) {
+       val->reset();
+    }
+
+    return status;
+}
+
+template<typename T>
 status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
                                          status_t(Parcel::*read_func)(T*) const) const {
     const size_t start = dataPosition();
@@ -901,6 +1037,16 @@
 }
 
 template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+                                          status_t(Parcel::*write_func)(const T&)) {
+    if (!val) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
 status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
                                           status_t(Parcel::*write_func)(const T&)) {
     if (val.get() == nullptr) {
@@ -911,6 +1057,16 @@
 }
 
 template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+                                          status_t(Parcel::*write_func)(T)) {
+    if (!val) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
 status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
                                           status_t(Parcel::*write_func)(T)) {
     if (val.get() == nullptr) {
@@ -926,6 +1082,30 @@
 }
 
 template<typename T>
+status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
+    const size_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    val->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    val->emplace();
+
+    using NullableT = std::optional<T>;
+    status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable);
+
+    if (status != OK) {
+        val->reset();
+    }
+
+    return status;
+}
+
+template<typename T>
 status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
     const size_t start = dataPosition();
     int32_t size;
@@ -939,7 +1119,8 @@
     setDataPosition(start);
     val->reset(new std::vector<std::unique_ptr<T>>());
 
-    status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
+    using NullableT = std::unique_ptr<T>;
+    status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable);
 
     if (status != OK) {
         val->reset();
@@ -949,6 +1130,29 @@
 }
 
 template<typename T>
+status_t Parcel::readParcelable(std::optional<T>* parcelable) const {
+    const size_t start = dataPosition();
+    int32_t present;
+    status_t status = readInt32(&present);
+    parcelable->reset();
+
+    if (status != OK || !present) {
+        return status;
+    }
+
+    setDataPosition(start);
+    parcelable->emplace();
+
+    status = readParcelable(&**parcelable);
+
+    if (status != OK) {
+        parcelable->reset();
+    }
+
+    return status;
+}
+
+template<typename T>
 status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
     const size_t start = dataPosition();
     int32_t present;
@@ -972,6 +1176,11 @@
 }
 
 template<typename T>
+status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) {
+    return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr);
+}
+
+template<typename T>
 status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
     return writeRawNullableParcelable(parcelable.get());
 }
@@ -982,6 +1191,16 @@
 }
 
 template<typename T>
+status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) {
+    if (!val) {
+        return this->writeInt32(-1);
+    }
+
+    using NullableT = std::optional<T>;
+    return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
+}
+
+template<typename T>
 status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
     if (val.get() == nullptr) {
         return this->writeInt32(-1);
@@ -996,7 +1215,8 @@
         return this->writeInt32(-1);
     }
 
-    return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+    using NullableT = std::unique_ptr<T>;
+    return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
 }
 
 template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
@@ -1013,6 +1233,11 @@
     return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
 }
 template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+    if (!val) return writeInt32(-1);
+    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
 status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
     if (!val) return writeInt32(-1);
     return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
@@ -1022,6 +1247,10 @@
     return writeTypedVector(val, &Parcel::writeEnum);
 }
 template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+    return writeNullableTypedVector(val, &Parcel::writeEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
 status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
     return writeNullableTypedVector(val, &Parcel::writeEnum);
 }
@@ -1053,6 +1282,17 @@
     return readByteVectorInternal(val, size);
 }
 template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+    size_t size;
+    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+    if (!*val) {
+        // reserveOutVector does not create the out vector if size is < 0.
+        // This occurs when writing a null Enum vector.
+        return OK;
+    }
+    return readByteVectorInternal(&**val, size);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
 status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
     size_t size;
     if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1068,6 +1308,10 @@
     return readTypedVector(val, &Parcel::readEnum);
 }
 template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
 status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
     return readNullableTypedVector(val, &Parcel::readEnum);
 }
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 4635ad8..71e1d3c 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -31,7 +31,8 @@
 public:
     ParcelFileDescriptor();
     explicit ParcelFileDescriptor(android::base::unique_fd fd);
-    ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { }
+    ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { }
+    ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default;
     ~ParcelFileDescriptor() override;
 
     int get() const { return mFd.get(); }
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index a9166e2..83c2f19 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -52,6 +52,21 @@
     //
     // Returns android::OK on success and an appropriate error otherwise.
     virtual status_t readFromParcel(const Parcel* parcel) = 0;
+
+    // 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 {
+        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: 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__)
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index e57ff1c..efb95f4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -42,20 +42,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 +86,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 +122,6 @@
 
             Vector<handle_entry>mHandleToObject;
 
-            context_check_func  mBinderContextCheckFunc;
-            void*               mBinderContextUserData;
-
             String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
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..c7e1e14 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -50,12 +50,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;
 
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index c22be9f..7898928 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -36,6 +36,37 @@
 #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
+
+
 #ifdef __cplusplus
 }   // namespace android
 #endif
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index b37db43..4fd0657 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -36,6 +36,7 @@
     host_supported: true,
 
     export_include_dirs: [
+        "include_cpp",
         "include_ndk",
         "include_platform",
     ],
@@ -101,6 +102,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",
@@ -111,6 +123,7 @@
     name: "libbinder_ndk",
     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..7d9fd51 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
 #include "ibinder_internal.h"
 
 #include <android/binder_stability.h>
@@ -99,8 +100,14 @@
 
     String8 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.c_str()
+                       << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+        } else {
+            // b/155793159
+            LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str()
+                       << "' to dead binder.";
+        }
         return false;
     }
 
@@ -161,7 +168,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();
@@ -676,3 +683,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..902fe79 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -110,13 +110,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 96%
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..f59bb75 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -189,6 +189,7 @@
     explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {}
     ~ScopedAParcel() {}
     ScopedAParcel(ScopedAParcel&&) = default;
+    ScopedAParcel& operator=(ScopedAParcel&&) = default;
 };
 
 /**
@@ -273,6 +274,7 @@
         : ScopedAResource(a) {}
     ~ScopedAIBinder_DeathRecipient() {}
     ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default;
+    ScopedAIBinder_DeathRecipient& operator=(ScopedAIBinder_DeathRecipient&&) = default;
 };
 
 /**
@@ -287,6 +289,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.
@@ -305,6 +308,7 @@
     explicit ScopedFileDescriptor(int a = -1) : 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..f44ce0c 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
+   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 98%
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 df5df13..09949ea 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -831,34 +831,34 @@
 }
 
 /**
- * Writes a vector of int8_t to the next location in a non-null parcel.
+ * 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<int8_t>& vec) {
-    return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+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());
 }
 
 /**
- * Writes an optional vector of int8_t to the next location in a non-null parcel.
+ * Writes an optional vector of uint8_t to the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_writeVector(AParcel* parcel,
-                                           const std::optional<std::vector<int8_t>>& vec) {
+                                           const std::optional<std::vector<uint8_t>>& vec) {
     if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1);
     return AParcel_writeVector(parcel, *vec);
 }
 
 /**
- * Reads a vector of int8_t from the next location in a non-null parcel.
+ * Reads a vector of uint8_t from the next location in a non-null parcel.
  */
-inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) {
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint8_t>* vec) {
     void* vectorData = static_cast<void*>(vec);
     return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
 }
 
 /**
- * Reads an optional vector of int8_t from the next location in a non-null parcel.
+ * Reads an optional vector of uint8_t from the next location in a non-null parcel.
  */
 inline binder_status_t AParcel_readVector(const AParcel* parcel,
-                                          std::optional<std::vector<int8_t>>* vec) {
+                                          std::optional<std::vector<uint8_t>>* vec) {
     void* vectorData = static_cast<void*>(vec);
     return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<int8_t>);
 }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4560f22..33763d5 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>
@@ -407,6 +408,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..a031e29 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>
 
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..5811760
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -0,0 +1,81 @@
+/*
+ * 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>
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+#include <binder/IBinder.h>
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * 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
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+/**
+ * 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..114a781 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,4 @@
  */
 bool AParcel_getAllowFds(const AParcel*);
 
-__END_DECLS
\ No newline at end of file
+__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..1701fb5 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,26 @@
     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
 };
 
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;
+    extern "C++" {
+        AIBinder_fromPlatformBinder*;
+        AIBinder_toPlatformBinder*;
+    };
+  local:
+    *;
 };
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..d889593 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -123,8 +123,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 +155,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/test/Android.bp b/libs/binder/ndk/tests/Android.bp
similarity index 91%
rename from libs/binder/ndk/test/Android.bp
rename to libs/binder/ndk/tests/Android.bp
index 5f5265c..46e6270 100644
--- a/libs/binder/ndk/test/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/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
similarity index 93%
rename from libs/binder/ndk/test/IBinderNdkUnitTest.aidl
rename to libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
index 6e8e463..dc77467d 100644
--- a/libs/binder/ndk/test/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/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl
rename to libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/tests/IEmpty.aidl
similarity index 100%
rename from libs/binder/ndk/test/IEmpty.aidl
rename to libs/binder/ndk/tests/IEmpty.aidl
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
similarity index 100%
rename from libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
rename to libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/tests/iface.cpp
similarity index 94%
rename from libs/binder/ndk/test/iface.cpp
rename to libs/binder/ndk/tests/iface.cpp
index 64832f3..a588985 100644
--- a/libs/binder/ndk/test/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/test/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
similarity index 94%
rename from libs/binder/ndk/test/include/iface/iface.h
rename to libs/binder/ndk/tests/include/iface/iface.h
index cdf5493..d9dd64b 100644
--- a/libs/binder/ndk/test/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/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
similarity index 65%
rename from libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
rename to libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index fd30d87..2bd5248 100644
--- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -19,10 +19,12 @@
 #include <aidl/BnEmpty.h>
 #include <android-base/logging.h>
 #include <android/binder_ibinder_jni.h>
+#include <android/binder_ibinder_platform.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 +36,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 +60,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 +80,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 +110,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 +166,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 +184,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 +213,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 +323,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 +383,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 +432,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 +547,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/rust/Android.bp b/libs/binder/rust/Android.bp
new file mode 100644
index 0000000..0234820
--- /dev/null
+++ b/libs/binder/rust/Android.bp
@@ -0,0 +1,86 @@
+rust_library {
+    name: "libbinder_rs",
+    crate_name: "binder",
+    srcs: ["src/lib.rs"],
+    shared_libs: [
+        "libutils",
+    ],
+    rustlibs: [
+        "liblibc",
+        "libbinder_ndk_sys",
+    ],
+    host_supported: true,
+}
+
+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,
+}
+
+rust_bindgen {
+    name: "libbinder_ndk_bindgen",
+    crate_name: "binder_ndk_bindgen",
+    wrapper_src: "sys/BinderBindings.h",
+    source_stem: "bindings",
+    cflags: [
+        "-x c++",
+    ],
+    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",
+            ],
+        },
+    },
+}
+
+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..6bf9cd5
--- /dev/null
+++ b/libs/binder/rust/src/binder.rs
@@ -0,0 +1,636 @@
+/*
+ * 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.
+///
+/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
+/// one-way RPC.
+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;
+
+    /// Is this object still alive?
+    fn is_binder_alive(&self) -> bool;
+
+    /// Send a ping transaction to this object
+    fn ping_binder(&mut self) -> Result<()>;
+
+    /// 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<()> {
+                $on_transact(&*self.0, code, data, reply)
+            }
+
+            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))
+            }
+        }
+    };
+}
+
+/// 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..a248f5c
--- /dev/null
+++ b/libs/binder/rust/src/parcel.rs
@@ -0,0 +1,542 @@
+/*
+ * 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 {
+    /// 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;
+    use std::ffi::CString;
+
+    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<CString>>(), 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(&["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..78b3d2c
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -0,0 +1,922 @@
+/*
+ * 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, CStr, CString};
+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 CStr {
+    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
+                // null-terminated C string pointer with no nulls in the middle
+                // of the string. Rust guarantees exactly that for a valid CStr
+                // instance.
+                status_result(sys::AParcel_writeString(
+                    parcel.as_native_mut(),
+                    s.as_ptr(),
+                    s.to_bytes()
+                        .len()
+                        .try_into()
+                        .or(Err(StatusCode::BAD_VALUE))?,
+                ))
+            },
+        }
+    }
+}
+
+impl SerializeArray for Option<&CStr> {}
+
+impl Serialize for CStr {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Some(self).serialize(parcel)
+    }
+}
+
+impl Serialize for CString {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Some(self.as_c_str()).serialize(parcel)
+    }
+}
+
+impl SerializeArray for CString {}
+
+impl SerializeOption for CString {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        SerializeOption::serialize_option(this.map(CString::as_c_str), parcel)
+    }
+}
+
+impl SerializeArray for Option<CString> {}
+
+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<CString> {
+    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 CString::new requires
+            // no nulls, including terminating.
+            s.pop();
+            CString::new(s).or(Err(StatusCode::BAD_VALUE))
+        })
+        .transpose()
+    }
+}
+
+impl DeserializeArray for Option<CString> {}
+
+impl DeserializeOption for String {
+    fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+        let c_str = <Option<CString>>::deserialize(parcel)?;
+        c_str
+            .map(|s| s.into_string().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 SerializeOption for str {
+    fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+        match this {
+            None => parcel.write(&-1i32),
+            Some(s) => {
+                let c_str = CString::new(s).or(Err(StatusCode::BAD_VALUE))?;
+                parcel.write(&c_str)
+            }
+        }
+    }
+}
+
+impl Serialize for str {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        Some(self).serialize(parcel)
+    }
+}
+
+impl SerializeArray for &str {}
+
+impl SerializeArray for Option<&str> {}
+
+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 strs = [s1, s2, s3];
+
+    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..5602c96
--- /dev/null
+++ b/libs/binder/rust/src/proxy.rs
@@ -0,0 +1,539 @@
+/*
+ * 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 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()
+    }
+}
+
+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..992f074
--- /dev/null
+++ b/libs/binder/rust/src/state.rs
@@ -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.
+ */
+
+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()
+        }
+    }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.h
new file mode 100644
index 0000000..c7a06d9
--- /dev/null
+++ b/libs/binder/rust/sys/BinderBindings.h
@@ -0,0 +1,86 @@
+/*
+ * 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_manager.h>
+#include <android/binder_parcel.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,
+};
+
+} // 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..622604f
--- /dev/null
+++ b/libs/binder/rust/tests/Android.bp
@@ -0,0 +1,28 @@
+rust_test {
+    name: "rustBinderTest",
+    srcs: ["integration.rs"],
+    rustlibs: [
+        "libbinder_rs",
+    ],
+    // 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/test/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml
similarity index 61%
copy from libs/binder/ndk/test/AndroidTest.xml
copy to libs/binder/rust/tests/AndroidTest.xml
index 89646f7..d8d735d 100644
--- a/libs/binder/ndk/test/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..fe59416
--- /dev/null
+++ b/libs/binder/rust/tests/integration.rs
@@ -0,0 +1,371 @@
+/*
+ * 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, TransactionCode};
+
+/// 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(),
+        })));
+        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,
+}
+
+impl Interface for TestService {}
+
+impl ITest for TestService {
+    fn test(&self) -> binder::Result<String> {
+        Ok(self.s.clone())
+    }
+}
+
+/// Trivial testing binder interface
+pub trait ITest: Interface {
+    /// Returns a test string
+    fn test(&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<()> {
+    reply.write(&service.test()?)?;
+    Ok(())
+}
+
+impl ITest for BpTest {
+    fn test(&self) -> binder::Result<String> {
+        let reply = self
+            .binder
+            .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+        reply.read()
+    }
+}
+
+impl ITest for Binder<BnTest> {
+    fn test(&self) -> binder::Result<String> {
+        self.0.test()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::fs::File;
+    use std::process::{Child, Command};
+    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");
+    }
+
+    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 c0da2cd..a03835b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -155,6 +155,7 @@
         "binderStabilityTest.cpp",
     ],
 
+    // critical that libbinder/libbinder_ndk are shared for VTS
     shared_libs: [
         "libbinder_ndk",
         "libbinder",
@@ -166,6 +167,21 @@
         "binderStabilityTestIface-ndk_platform",
     ],
 
+    test_suites: ["device-tests", "vts"],
+    require_root: true,
+}
+
+cc_test {
+    name: "binderAllocationLimits",
+    defaults: ["binder_test_defaults"],
+    srcs: ["binderAllocationLimits.cpp"],
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+        "libutilscallstack",
+        "libbase",
+    ],
     test_suites: ["device-tests"],
     require_root: true,
 }
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
new file mode 100644
index 0000000..e1f5ed5
--- /dev/null
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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/logging.h>
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <utils/CallStack.h>
+
+#include <malloc.h>
+#include <functional>
+#include <vector>
+
+struct DestructionAction {
+    DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
+    ~DestructionAction() { mF(); };
+private:
+    std::function<void()> mF;
+};
+
+// Group of hooks
+struct MallocHooks {
+    decltype(__malloc_hook) malloc_hook;
+    decltype(__realloc_hook) realloc_hook;
+
+    static MallocHooks save() {
+        return {
+            .malloc_hook = __malloc_hook,
+            .realloc_hook = __realloc_hook,
+        };
+    }
+
+    void overwrite() const {
+        __malloc_hook = malloc_hook;
+        __realloc_hook = realloc_hook;
+    }
+};
+
+static const MallocHooks orig_malloc_hooks = MallocHooks::save();
+
+// When malloc is hit, executes lambda.
+namespace LambdaHooks {
+    using AllocationHook = std::function<void(size_t)>;
+    static std::vector<AllocationHook> lambdas = {};
+
+    static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg);
+    static void* lambda_malloc_hook(size_t bytes, const void* arg);
+
+    static const MallocHooks lambda_malloc_hooks = {
+        .malloc_hook = lambda_malloc_hook,
+        .realloc_hook = lambda_realloc_hook,
+    };
+
+    static void* lambda_malloc_hook(size_t bytes, const void* arg) {
+        {
+            orig_malloc_hooks.overwrite();
+            lambdas.at(lambdas.size() - 1)(bytes);
+            lambda_malloc_hooks.overwrite();
+        }
+        return orig_malloc_hooks.malloc_hook(bytes, arg);
+    }
+
+    static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) {
+        {
+            orig_malloc_hooks.overwrite();
+            lambdas.at(lambdas.size() - 1)(bytes);
+            lambda_malloc_hooks.overwrite();
+        }
+        return orig_malloc_hooks.realloc_hook(ptr, bytes, arg);
+    }
+
+}
+
+// Action to execute when malloc is hit. Supports nesting. Malloc is not
+// restricted when the allocation hook is being processed.
+__attribute__((warn_unused_result))
+DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
+    MallocHooks before = MallocHooks::save();
+    LambdaHooks::lambdas.emplace_back(std::move(f));
+    LambdaHooks::lambda_malloc_hooks.overwrite();
+    return DestructionAction([before]() {
+        before.overwrite();
+        LambdaHooks::lambdas.pop_back();
+    });
+}
+
+// exported symbol, to force compiler not to optimize away pointers we set here
+const void* imaginary_use;
+
+TEST(TestTheTest, OnMalloc) {
+    size_t mallocs = 0;
+    {
+        const auto on_malloc = OnMalloc([&](size_t bytes) {
+            mallocs++;
+            EXPECT_EQ(bytes, 40);
+        });
+
+        imaginary_use = new int[10];
+    }
+    EXPECT_EQ(mallocs, 1);
+}
+
+
+__attribute__((warn_unused_result))
+DestructionAction ScopeDisallowMalloc() {
+    return OnMalloc([&](size_t bytes) {
+        ADD_FAILURE() << "Unexpected allocation: " << bytes;
+        using android::CallStack;
+        std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
+                  << std::endl;
+    });
+}
+
+using android::IBinder;
+using android::Parcel;
+using android::String16;
+using android::defaultServiceManager;
+using android::sp;
+using android::IServiceManager;
+
+static sp<IBinder> GetRemoteBinder() {
+    // This gets binder representing the service manager
+    // the current IServiceManager API doesn't expose the binder, and
+    // I want to avoid adding usages of the AIDL generated interface it
+    // is using underneath, so to avoid people copying it.
+    sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager"));
+    EXPECT_NE(nullptr, binder);
+    return binder;
+}
+
+TEST(BinderAllocation, ParcelOnStack) {
+    const auto m = ScopeDisallowMalloc();
+    Parcel p;
+    imaginary_use = p.data();
+}
+
+TEST(BinderAllocation, GetServiceManager) {
+    defaultServiceManager(); // first call may alloc
+    const auto m = ScopeDisallowMalloc();
+    defaultServiceManager();
+}
+
+// note, ping does not include interface descriptor
+TEST(BinderAllocation, PingTransaction) {
+    sp<IBinder> a_binder = GetRemoteBinder();
+    const auto m = ScopeDisallowMalloc();
+    a_binder->pingBinder();
+}
+
+TEST(BinderAllocation, SmallTransaction) {
+    String16 empty_descriptor = String16("");
+    sp<IServiceManager> manager = defaultServiceManager();
+
+    size_t mallocs = 0;
+    const auto on_malloc = OnMalloc([&](size_t bytes) {
+        mallocs++;
+        // Parcel should allocate a small amount by default
+        EXPECT_EQ(bytes, 128);
+    });
+    manager->checkService(empty_descriptor);
+
+    EXPECT_EQ(mallocs, 1);
+}
+
+int main(int argc, char** argv) {
+    if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
+        CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
+        execv(argv[0], argv);
+        return 1;
+    }
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 40de2db..145c099 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,40 @@
     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));
+    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 +1056,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 +1215,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 +1395,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 +1441,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/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index b790997..3b1faa8 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -116,7 +116,7 @@
         if (time > max_time_bucket) {
             m_long_transactions++;
         }
-        m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1;
+        m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1;
         m_best = min(time, m_best);
         m_worst = max(time, m_worst);
         m_transactions += 1;
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..5e785b6 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;
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index fd557b7..8f092f6 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -56,6 +56,11 @@
         "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",
+        "android.hardware.automotive.vehicle@2.0::IVehicle",
         "android.hardware.biometrics.face@1.0::IBiometricsFace",
         "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
         "android.hardware.bluetooth@1.0::IBluetoothHci",
@@ -67,16 +72,12 @@
         "android.hardware.media.c2@1.0::IComponentStore",
         "android.hardware.media.omx@1.0::IOmx",
         "android.hardware.media.omx@1.0::IOmxStore",
+        "android.hardware.neuralnetworks@1.0::IDevice",
         "android.hardware.power@1.3::IPower",
         "android.hardware.power.stats@1.0::IPowerStats",
         "android.hardware.sensors@1.0::ISensors",
         "android.hardware.thermal@2.0::IThermal",
         "android.hardware.vr@1.0::IVr",
-        "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
-        "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
-        "android.hardware.automotive.vehicle@2.0::IVehicle",
-        "android.hardware.automotive.evs@1.0::IEvsCamera",
-        "android.hardware.neuralnetworks@1.0::IDevice",
         NULL,
 };
 
@@ -86,7 +87,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 de32ff4..6909637 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -1,5 +1,6 @@
 cc_defaults {
     name: "fakeservicemanager_defaults",
+    host_supported: true,
     srcs: [
         "ServiceManager.cpp",
     ],
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
index 67743cd..4a95778 100644
--- a/libs/gralloc/OWNERS
+++ b/libs/gralloc/OWNERS
@@ -1,2 +1,2 @@
-marissaw@google.com
+chrisforbes@google.com
 vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 00f7484..66fb295 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -27,6 +27,11 @@
         enabled: true,
         support_system_process: true,
     },
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 
     srcs: [
         "Gralloc4.cpp"
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 8d12754..1a7c2c9 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -431,6 +431,12 @@
          static_cast<int64_t>(
                  aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
 
+static const aidl::android::hardware::graphics::common::ExtendableType
+        PlaneLayoutComponentType_RAW =
+                {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+                 static_cast<int64_t>(
+                         aidl::android::hardware::graphics::common::PlaneLayoutComponentType::RAW)};
+
 /*---------------------------------------------------------------------------------------------*/
 
 /**
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4809c1f..119b3e0 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,7 +29,6 @@
 #include <android-base/strings.h>
 #include <android/dlext.h>
 #include <binder/IServiceManager.h>
-#include <cutils/properties.h>
 #include <graphicsenv/IGpuService.h>
 #include <log/log.h>
 #include <nativeloader/dlext_namespaces.h>
@@ -40,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:
@@ -67,7 +73,7 @@
 
 static std::string vndkVersionStr() {
 #ifdef __BIONIC__
-    return android::base::GetProperty("ro.vndk.version", "");
+    return base::GetProperty("ro.vndk.version", "");
 #endif
     return "";
 }
@@ -338,10 +344,8 @@
 }
 
 bool GraphicsEnv::checkAngleRules(void* so) {
-    char manufacturer[PROPERTY_VALUE_MAX];
-    char model[PROPERTY_VALUE_MAX];
-    property_get("ro.product.manufacturer", manufacturer, "UNSET");
-    property_get("ro.product.model", model, "UNSET");
+    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
+    auto model = base::GetProperty("ro.product.model", "UNSET");
 
     auto ANGLEGetFeatureSupportUtilAPIVersion =
             (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
@@ -394,7 +398,8 @@
                 ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
                 break;
             }
-            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
+                                                  systemInfoHandle)) {
                 ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
                 break;
             }
@@ -584,7 +589,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");
@@ -625,8 +651,7 @@
     mAngleNamespace = android_create_namespace("ANGLE",
                                                nullptr,            // ld_library_path
                                                mAnglePath.c_str(), // default_library_path
-                                               ANDROID_NAMESPACE_TYPE_SHARED |
-                                                       ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                               ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
                                                nullptr, // permitted_when_isolated_path
                                                nullptr);
 
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/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 7976ecb..ae265ca 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,13 +55,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",
@@ -92,6 +92,7 @@
 
     export_shared_lib_headers: [
         "libbinder",
+        "libinput",
     ],
 
     // bufferhub is not used when building libgui for vendors
@@ -130,6 +131,11 @@
 cc_library_static {
     name: "libgui_bufferqueue_static",
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 
     cflags: [
         "-DNO_BUFFERHUB",
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/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 8d80833..5023b6b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -75,10 +75,12 @@
     if (NULL != fp) {
         const size_t size = 64;
         char proc_name[size];
-        fgets(proc_name, size, fp);
+        char* result = fgets(proc_name, size, fp);
         fclose(fp);
-        name = proc_name;
-        return NO_ERROR;
+        if (result != nullptr) {
+            name = proc_name;
+            return NO_ERROR;
+        }
     }
     return INVALID_OPERATION;
 }
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index b33bc9e..2cc7c34 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -89,12 +89,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 {
@@ -121,9 +117,8 @@
     PhysicalDisplayId vsyncDisplayId;
     uint32_t vsyncCount;
     if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
-        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
-              ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
-              this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%s, count=%d", this,
+              ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount);
         mWaitingForVsync = false;
         dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
     }
@@ -157,6 +152,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 +166,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 2a27a9a..0ac493d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -66,42 +66,39 @@
         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(
+            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) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
-        data.writeUint32(static_cast<uint32_t>(state.size()));
+        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);
+        return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
     virtual void bootFinished()
@@ -111,95 +108,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(
@@ -327,8 +263,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 +278,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();
     }
@@ -371,10 +310,8 @@
         data.writeStrongBinder(display);
         remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
         const status_t result = reply.readInt32();
-        if (result == NO_ERROR) {
-            memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
-        }
-        return result;
+        if (result != NO_ERROR) return result;
+        return reply.read(*info);
     }
 
     virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
@@ -691,8 +628,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;
         }
@@ -1094,22 +1030,22 @@
         return NO_ERROR;
     }
 
-    virtual status_t notifyPowerHint(int32_t hintId) {
+    virtual status_t notifyPowerBoost(int32_t boostId) {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+            ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
             return error;
         }
-        error = data.writeInt32(hintId);
+        error = data.writeInt32(boostId);
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+            ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
             return error;
         }
-        error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+        error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
                                    IBinder::FLAG_ONEWAY);
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to transact: %d", error);
+            ALOGE("notifyPowerBoost: failed to transact: %d", error);
             return error;
         }
         return NO_ERROR;
@@ -1237,135 +1173,88 @@
         case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
 
-            size_t count = data.readUint32();
-            if (count > data.dataSize()) {
-                return BAD_VALUE;
-            }
+            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;
+            return setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+                                       desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
+                                       listenerCallbacks);
         }
         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);
@@ -1420,7 +1309,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;
@@ -1443,10 +1332,8 @@
             const sp<IBinder> display = data.readStrongBinder();
             const status_t result = getDisplayInfo(display, &info);
             reply->writeInt32(result);
-            if (result == NO_ERROR) {
-                memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
-            }
-            return NO_ERROR;
+            if (result != NO_ERROR) return result;
+            return reply->write(info);
         }
         case GET_DISPLAY_CONFIGS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1824,7 +1711,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);
@@ -1990,15 +1881,15 @@
             }
             return setDisplayBrightness(displayToken, brightness);
         }
-        case NOTIFY_POWER_HINT: {
+        case NOTIFY_POWER_BOOST: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            int32_t hintId;
-            status_t error = data.readInt32(&hintId);
+            int32_t boostId;
+            status_t error = data.readInt32(&boostId);
             if (error != NO_ERROR) {
-                ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+                ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
                 return error;
             }
-            return notifyPowerHint(hintId);
+            return notifyPowerBoost(boostId);
         }
         case SET_GLOBAL_SHADOW_SETTINGS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e43446a..6ff4a3d 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -30,176 +30,189 @@
 
 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.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(output.writeStrongBinder, barrierHandle_legacy);
+    SAFE_PARCEL(output.writeStrongBinder, reparentHandle);
+    SAFE_PARCEL(output.writeUint64, frameNumber_legacy);
+    SAFE_PARCEL(output.writeInt32, overrideScalingMode);
+    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(barrierGbp_legacy));
+    SAFE_PARCEL(output.writeStrongBinder, relativeLayerHandle);
+    SAFE_PARCEL(output.writeStrongBinder, parentHandleForChild);
+    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);
     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.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(input.readNullableStrongBinder, &barrierHandle_legacy);
+    SAFE_PARCEL(input.readNullableStrongBinder, &reparentHandle);
+    SAFE_PARCEL(input.readUint64, &frameNumber_legacy);
+    SAFE_PARCEL(input.readInt32, &overrideScalingMode);
+
+    sp<IBinder> tmpBinder;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(tmpBinder);
+
+    float tmpFloat = 0;
+    SAFE_PARCEL(input.readNullableStrongBinder, &relativeLayerHandle);
+    SAFE_PARCEL(input.readNullableStrongBinder, &parentHandleForChild);
+    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);
+    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);
     return NO_ERROR;
 }
 
@@ -211,39 +224,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 +276,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;
@@ -363,7 +380,7 @@
     }
     if (other.what & eFrameChanged) {
         what |= eFrameChanged;
-        frame = other.frame;
+        orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
     }
     if (other.what & eBufferChanged) {
         what |= eBufferChanged;
@@ -404,7 +421,7 @@
 #ifndef NO_INPUT
     if (other.what & eInputInfoChanged) {
         what |= eInputInfoChanged;
-        inputInfo = other.inputInfo;
+        inputHandle = new InputWindowHandle(*other.inputHandle);
     }
 #endif
 
@@ -446,22 +463,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 +541,107 @@
     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.writeBool, useRGBColorSpace);
+    return NO_ERROR;
+}
+
+status_t CaptureArgs::read(const Parcel& input) {
+    int32_t format = 0;
+    SAFE_PARCEL(input.readInt32, &format);
+    pixelFormat = static_cast<ui::PixelFormat>(format);
+    SAFE_PARCEL(input.read, sourceCrop);
+    SAFE_PARCEL(input.readFloat, &frameScale);
+    SAFE_PARCEL(input.readBool, &captureSecureLayers);
+    SAFE_PARCEL(input.readInt32, &uid);
+    SAFE_PARCEL(input.readBool, &useRGBColorSpace);
+    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 c13401d..ecccf29 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,12 +1,25 @@
 adyabr@google.com
 akrulec@google.com
 alecmouri@google.com
+chaviw@google.com
+chrisforbes@google.com
 jessehall@google.com
-jwcai@google.com
 lpy@google.com
-marissaw@google.com
 mathias@google.com
 racarr@google.com
 steventhomas@google.com
 stoza@google.com
 vhau@google.com
+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 2bf8ff7..e45b3d1 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -110,7 +110,7 @@
     mConnectedToCpu = false;
     mProducerControlledByApp = controlledByApp;
     mSwapIntervalZero = false;
-    mMaxBufferCount = 0;
+    mMaxBufferCount = NUM_BUFFER_SLOTS;
 }
 
 Surface::~Surface() {
@@ -732,6 +732,8 @@
         mSharedBufferHasBeenQueued = false;
     }
 
+    mDequeuedSlots.insert(buf);
+
     return OK;
 }
 
@@ -760,6 +762,8 @@
         mSharedBufferHasBeenQueued = true;
     }
 
+    mDequeuedSlots.erase(i);
+
     return OK;
 }
 
@@ -895,6 +899,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.
@@ -1493,7 +1499,7 @@
     int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
 
     if (graphicBuffer != nullptr) {
-        *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+        *buffer = graphicBuffer->toAHardwareBuffer();
         AHardwareBuffer_acquire(*buffer);
     } else {
         *buffer = nullptr;
@@ -1513,7 +1519,7 @@
 }
 
 int Surface::connect(int api) {
-    static sp<IProducerListener> listener = new DummyProducerListener();
+    static sp<IProducerListener> listener = new StubProducerListener();
     return connect(api, listener);
 }
 
@@ -1585,6 +1591,7 @@
         mStickyTransform = 0;
         mAutoPrerotation = false;
         mEnableFrameTimestamps = false;
+        mMaxBufferCount = NUM_BUFFER_SLOTS;
 
         if (api == NATIVE_WINDOW_API_CPU) {
             mConnectedToCpu = false;
@@ -1659,6 +1666,7 @@
         mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
     }
     mSlots[attachedSlot].buffer = graphicBuffer;
+    mDequeuedSlots.insert(attachedSlot);
 
     return NO_ERROR;
 }
@@ -1925,6 +1933,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 +1958,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;
     }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5922f3a..ce2e5ed 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -353,6 +353,8 @@
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeup(other.mEarlyWakeup),
+        mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+        mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime) {
     mDisplayStates = other.mDisplayStates;
@@ -375,6 +377,8 @@
     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();
 
@@ -443,6 +447,8 @@
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
     mEarlyWakeup = earlyWakeup;
+    mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+    mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
     mDisplayStates = displayStates;
@@ -470,6 +476,8 @@
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
     parcel->writeBool(mEarlyWakeup);
+    parcel->writeBool(mExplicitEarlyWakeupStart);
+    parcel->writeBool(mExplicitEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -545,6 +553,8 @@
 
     mContainsBuffer |= other.mContainsBuffer;
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
+    mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+    mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
     other.clear();
     return *this;
 }
@@ -559,6 +569,8 @@
     mTransactionNestCount = 0;
     mAnimation = false;
     mEarlyWakeup = false;
+    mExplicitEarlyWakeupStart = false;
+    mExplicitEarlyWakeupEnd = false;
     mDesiredPresentTime = -1;
 }
 
@@ -682,9 +694,20 @@
         flags |= ISurfaceComposer::eEarlyWakeup;
     }
 
+    // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+    // it is equivalent for none
+    if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+        flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+    }
+    if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+        flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+    }
+
     mForceSynchronous = false;
     mAnimation = false;
     mEarlyWakeup = false;
+    mExplicitEarlyWakeupStart = false;
+    mExplicitEarlyWakeupEnd = false;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
     sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
@@ -731,6 +754,14 @@
     mEarlyWakeup = true;
 }
 
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+    mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+    mExplicitEarlyWakeupEnd = true;
+}
+
 layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
     if (mComposerStates.count(handle) == 0) {
         // we don't have it, add an initialized layer_state to our list
@@ -1106,7 +1137,7 @@
         return *this;
     }
     s->what |= layer_state_t::eFrameChanged;
-    s->frame = frame;
+    s->orientedDisplaySpaceRect = frame;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1328,11 +1359,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;
@@ -1499,8 +1547,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?
 }
@@ -1862,8 +1910,8 @@
     return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
 }
 
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
-    return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+    return ComposerService::getComposerService()->notifyPowerBoost(boostId);
 }
 
 status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
@@ -1875,60 +1923,57 @@
 }
 
 // ----------------------------------------------------------------------------
+status_t SyncScreenCaptureListener::onScreenCaptureComplete(
+        const ScreenCaptureResults& captureResults) {
+    resultsPromise.set_value(captureResults);
+    return NO_ERROR;
+}
 
-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) {
+ScreenCaptureResults SyncScreenCaptureListener::waitForResults() {
+    std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
+    return resultsFuture.get();
+}
+
+status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
+                                          ScreenCaptureResults& captureResults) {
     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;
+
+    sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+    status_t status = s->captureDisplay(captureArgs, captureListener);
+    if (status != NO_ERROR) {
+        return status;
     }
-    return ret;
+    captureResults = captureListener->waitForResults();
+    return captureResults.result;
 }
 
-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,
+                                          ScreenCaptureResults& captureResults) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
+
+    sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+    status_t status = s->captureDisplay(displayOrLayerStack, captureListener);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    captureResults = captureListener->waitForResults();
+    return captureResults.result;
 }
 
-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,
+                                         ScreenCaptureResults& captureResults) {
     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;
+    sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+    status_t status = s->captureLayers(captureArgs, captureListener);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    captureResults = captureListener->waitForResults();
+    return captureResults.result;
 }
 
 } // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a332a1f..8dcb71b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -178,17 +178,28 @@
 }
 
 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;
+    bool invalidParcel = false;
+    status_t status;
+    sp<IBinder> client;
+    if ((status = parcel->readStrongBinder(&client)) != OK) {
+        ALOGE("Failed to read client: %s", statusToString(status).c_str());
+        invalidParcel = true;
+    }
+    sp<IBinder> handle;
+    if ((status = parcel->readStrongBinder(&handle)) != OK) {
+        ALOGE("Failed to read handle: %s", statusToString(status).c_str());
+        invalidParcel = true;
     }
     sp<IBinder> gbp;
-    parcel->readNullableStrongBinder(&gbp);
-
+    if ((status = parcel->readNullableStrongBinder(&gbp)) != OK) {
+        ALOGE("Failed to read gbp: %s", statusToString(status).c_str());
+        invalidParcel = true;
+    }
     uint32_t transformHint = parcel->readUint32();
+
+    if (invalidParcel) {
+        return nullptr;
+    }
     // We aren't the original owner of the surface.
     return new SurfaceControl(new SurfaceComposerClient(
                                       interface_cast<ISurfaceComposerClient>(client)),
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/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/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f210c34..eb5b004 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,7 +31,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);
 
@@ -48,6 +48,9 @@
                                  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);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 8d49184..0d0d102 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -53,19 +53,26 @@
         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)));
         };
 
         struct Hotplug {
@@ -74,7 +81,7 @@
 
         struct Config {
             int32_t configId;
-            nsecs_t vsyncPeriod;
+            nsecs_t vsyncPeriod __attribute__((aligned(8)));
         };
 
         Header header;
@@ -130,6 +137,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 +154,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 0d33b3f..e057b68 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,16 +22,17 @@
 #include <binder/IBinder.h>
 #include <binder/IInterface.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 +49,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 +85,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 {
@@ -139,13 +151,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(
+            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;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -238,65 +249,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 +268,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 +301,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;
 
@@ -488,14 +435,14 @@
     virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
 
     /*
-     * Sends a power hint to the composer. This function is asynchronous.
+     * Sends a power boost to the composer. This function is asynchronous.
      *
-     * hintId
-     *      hint id according to android::hardware::power::V1_0::PowerHint
+     * boostId
+     *      boost id according to android::hardware::power::Boost
      *
      * Returns NO_ERROR upon success.
      */
-    virtual status_t notifyPowerHint(int32_t hintId) = 0;
+    virtual status_t notifyPowerBoost(int32_t boostId) = 0;
 
     /*
      * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -554,7 +501,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,8 +529,8 @@
         GET_DESIRED_DISPLAY_CONFIG_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
-        CAPTURE_SCREEN_BY_ID,
-        NOTIFY_POWER_HINT,
+        CAPTURE_DISPLAY_BY_ID,
+        NOTIFY_POWER_BOOST,
         SET_GLOBAL_SHADOW_SETTINGS,
         GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
         SET_AUTO_LOW_LATENCY_MODE,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 6366529..3afbabf 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -33,7 +33,7 @@
     DECLARE_META_INTERFACE(SurfaceComposerClient)
 
     // flags for createSurface()
-    enum { // (keep in sync with Surface.java)
+    enum { // (keep in sync with SurfaceControl.java)
         eHidden = 0x00000004,
         eDestroyBackbuffer = 0x00000020,
         eSecure = 0x00000080,
@@ -42,6 +42,7 @@
         eProtectedByApp = 0x00000800,
         eProtectedByDRM = 0x00001000,
         eCursorWindow = 0x00002000,
+        eNoColorFill = 0x00004000,
 
         eFXSurfaceBufferQueue = 0x00000000,
         eFXSurfaceEffect = 0x00020000,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e60f677..a763d1d 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,9 +43,11 @@
 #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 <math/vec3.h>
 #include <ui/GraphicTypes.h>
@@ -127,7 +146,7 @@
             transform(0),
             transformToDisplayInverse(false),
             crop(Rect::INVALID_RECT),
-            frame(Rect::INVALID_RECT),
+            orientedDisplaySpaceRect(Rect::INVALID_RECT),
             dataspace(ui::Dataspace::UNKNOWN),
             surfaceDamageRegion(),
             api(-1),
@@ -154,6 +173,8 @@
         float dtdx{0};
         float dtdy{0};
         float dsdy{0};
+        status_t write(Parcel& output) const;
+        status_t read(const Parcel& input);
     };
     sp<IBinder> surface;
     uint64_t what;
@@ -190,7 +211,7 @@
     uint32_t transform;
     bool transformToDisplayInverse;
     Rect crop;
-    Rect frame;
+    Rect orientedDisplaySpaceRect;
     sp<GraphicBuffer> buffer;
     sp<Fence> acquireFence;
     ui::Dataspace dataspace;
@@ -201,7 +222,7 @@
     mat4 colorTransform;
 
 #ifndef NO_INPUT
-    InputWindowInfo inputInfo;
+    sp<InputWindowHandle> inputHandle = new InputWindowHandle();
 #endif
 
     client_cache_t cachedBuffer;
@@ -263,18 +284,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 +304,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 +333,55 @@
 // 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};
+    // True to force using RGB color as the capture result.
+    // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+    // different from RGB (byte per color), and failed when checking colors.
+    // NOTE: This should only be used for testing since in normal cases, we want the screen
+    // capture's colorspace to match the display's colorspace
+    bool useRGBColorSpace{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 = NO_ERROR;
+
+    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..55b4101 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 {
 
@@ -543,8 +544,15 @@
     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 e981a39..af37468 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -18,6 +18,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <future>
 #include <set>
 #include <unordered_map>
 #include <unordered_set>
@@ -217,14 +218,14 @@
     static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
 
     /*
-     * Sends a power hint to the composer. This function is asynchronous.
+     * Sends a power boost to the composer. This function is asynchronous.
      *
-     * hintId
-     *      hint id according to android::hardware::power::V1_0::PowerHint
+     * boostId
+     *      boost id according to android::hardware::power::Boost
      *
      * Returns NO_ERROR upon success.
      */
-    static status_t notifyPowerHint(int32_t hintId);
+    static status_t notifyPowerBoost(int32_t boostId);
 
     /*
      * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -345,10 +346,12 @@
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
 
-        uint32_t                    mForceSynchronous = 0;
-        uint32_t                    mTransactionNestCount = 0;
-        bool                        mAnimation = false;
-        bool                        mEarlyWakeup = false;
+        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;
@@ -505,6 +508,9 @@
 
 #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
 
@@ -547,6 +553,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;
@@ -586,30 +594,23 @@
 
 // ---------------------------------------------------------------------------
 
+class SyncScreenCaptureListener : public BnScreenCaptureListener {
+public:
+    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override;
+    ScreenCaptureResults waitForResults();
+
+private:
+    std::promise<ScreenCaptureResults> resultsPromise;
+};
+
 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,
+                                   ScreenCaptureResults& captureResults);
+    static status_t captureDisplay(uint64_t displayOrLayerStack,
+                                   ScreenCaptureResults& captureResults);
+    static status_t captureLayers(const LayerCaptureArgs& captureArgs,
+                                  ScreenCaptureResults& captureResults);
 };
 
 // ---------------------------------------------------------------------------
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/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..4a186b1 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -120,6 +120,8 @@
                 .show(mSurfaceControl)
                 .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
                 .apply();
+
+        mCaptureArgs.displayToken = mDisplayToken;
     }
 
     void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
@@ -128,7 +130,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,14 +167,15 @@
 
     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++) {
@@ -196,20 +199,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 +320,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 +397,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 +454,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}));
@@ -489,12 +495,8 @@
         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..4bcb795
--- /dev/null
+++ b/libs/gui/tests/DisplayEventStructLayout_test.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.
+ */
+
+#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::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..4d306e7 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,18 @@
 
 // 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);
+        std::unique_ptr<InputChannel> clientChannel;
+        InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
+        mClientChannel = std::move(clientChannel);
 
         mInputFlinger = getInputFlinger();
-        mInputFlinger->registerInputChannel(mServerChannel);
+        mInputFlinger->registerInputChannel(*mServerChannel);
 
         populateInputInfo(width, height);
 
@@ -104,6 +106,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,17 +145,17 @@
         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);
-    }
+    ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel->getConnectionToken()); }
 
     void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
                     const sp<SurfaceControl>&)> transactionBody) {
@@ -164,6 +175,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;
@@ -176,12 +194,11 @@
     void populateInputInfo(int width, int height) {
         mInputInfo.token = mServerChannel->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 +207,20 @@
         // 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::unique_ptr<InputChannel> mServerChannel;
+    std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
     InputWindowInfo mInputInfo;
@@ -269,7 +287,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 +302,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 +331,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 +343,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 +350,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 +362,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,12 +378,9 @@
     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);
@@ -400,12 +399,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 +418,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 +434,6 @@
         t.setTransparentRegionHint(sc, transparentRegion);
     });
     surface->showAt(100, 100);
-    surface->assertFocusChange(true);
     injectTap(101, 101);
     surface->expectTap(1, 1);
 }
@@ -454,10 +448,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 +465,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 +481,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 +498,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 +512,28 @@
 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);
 }
+} // 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 a1d12a5..aedba2a 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>
 
@@ -30,6 +30,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <inttypes.h>
 #include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 
@@ -55,12 +56,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 +123,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 +197,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 +258,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 +294,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 +394,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 +410,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 +441,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 +465,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 +510,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 +536,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,
@@ -681,13 +695,13 @@
     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(
+            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 {
+        return NO_ERROR;
     }
 
     void bootFinished() override {}
@@ -742,12 +756,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 +770,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 +791,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(
@@ -849,7 +855,7 @@
                                           float* /*outAppRequestRefreshRateMax*/) override {
         return NO_ERROR;
     };
-    status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+    status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
 
     status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
                                      float /*lightPosY*/, float /*lightPosZ*/,
@@ -1909,8 +1915,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 +1980,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/input/Android.bp b/libs/input/Android.bp
index 8efaf3d..8f575a8 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,6 +14,13 @@
 
 // libinput is partially built for the host (used by build time keymap validation tool)
 
+filegroup {
+    name: "inputconstants_aidl",
+    srcs: [
+        "android/os/IInputConstants.aidl",
+    ],
+}
+
 cc_library {
     name: "libinput",
     host_supported: true,
@@ -25,32 +32,47 @@
     srcs: [
         "Input.cpp",
         "InputDevice.cpp",
+        "InputEventLabels.cpp",
         "Keyboard.cpp",
         "KeyCharacterMap.cpp",
         "KeyLayoutMap.cpp",
+        "PropertyMap.cpp",
         "TouchVideoFrame.cpp",
         "VirtualKeyMap.cpp",
     ],
 
     clang: true,
 
+    header_libs: ["jni_headers"],
+    export_header_lib_headers: ["jni_headers"],
+
     shared_libs: [
         "libbase",
         "liblog",
         "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/IInputFlinger.aidl",
+                "android/os/ISetInputWindowsListener.aidl",
             ],
 
             shared_libs: [
@@ -69,6 +91,30 @@
             },
         },
     },
+
+    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..fb2f186 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "Input"
 //#define LOG_NDEBUG 0
 
+#include <attestation/HmacKeyManager.h>
 #include <cutils/compiler.h>
 #include <limits.h>
 #include <string.h>
@@ -135,11 +136,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 +170,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 ---
 
@@ -289,6 +302,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 +326,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 +340,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 +363,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 +376,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 +388,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 +418,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 +433,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 +463,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 +488,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 +515,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__
+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 +585,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 +599,7 @@
     mPointerProperties.clear();
     mPointerProperties.setCapacity(pointerCount);
     mSampleEventTimes.clear();
-    mSampleEventTimes.setCapacity(sampleCount);
+    mSampleEventTimes.reserve(sampleCount);
     mSamplePointerCoords.clear();
     mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
 
@@ -591,7 +612,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 +644,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 +663,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 +693,30 @@
 }
 
 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);
+}
+
+const char* 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_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 "UNKNOWN";
 }
 
 // --- 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..dee240c
--- /dev/null
+++ b/libs/input/InputEventLabels.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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), \
+    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..79e15c1 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,10 @@
     return OK;
 }
 
-sp<InputChannel> InputChannel::dup() const {
+std::unique_ptr<InputChannel> InputChannel::dup() 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
@@ -394,34 +390,25 @@
                             getName().c_str());
         return nullptr;
     }
-    return InputChannel::create(mName, std::move(newFd), mToken);
+    return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
 }
 
-status_t InputChannel::write(Parcel& out) const {
-    status_t s = out.writeCString(getName().c_str());
-    if (s != OK) {
-        return s;
+status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
     }
-
-    s = out.writeStrongBinder(mToken);
-    if (s != OK) {
-        return s;
-    }
-
-    s = out.writeUniqueFileDescriptor(mFd);
-    return s;
+    return parcel->writeStrongBinder(mToken)
+            ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
 }
 
-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;
+status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
     }
-
-    return InputChannel::create(name, std::move(rawFd), token);
+    mToken = parcel->readStrongBinder();
+    return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
 }
 
 sp<IBinder> InputChannel::getConnectionToken() const {
@@ -430,9 +417,7 @@
 
 // --- InputPublisher ---
 
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
-        mChannel(channel) {
-}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
 
 InputPublisher::~InputPublisher() {
 }
@@ -463,7 +448,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 +469,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 +480,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 +507,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 +520,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 +553,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 +577,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 +636,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 +648,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 +661,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 +688,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 +703,7 @@
 
                 updateTouchState(mMsg);
                 initializeMotionEvent(motionEvent, &mMsg);
-                *outSeq = mMsg.body.motion.seq;
+                *outSeq = mMsg.header.seq;
                 *outEvent = motionEvent;
 
                 if (DEBUG_TRANSPORT_ACTIONS) {
@@ -738,7 +724,7 @@
                 if (!focusEvent) return NO_MEMORY;
 
                 initializeFocusEvent(focusEvent, &mMsg);
-                *outSeq = mMsg.body.focus.seq;
+                *outSeq = mMsg.header.seq;
                 *outEvent = focusEvent;
                 break;
             }
@@ -752,10 +738,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 +756,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 +778,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 +813,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 +825,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 +835,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 +845,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 +855,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 +865,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 +924,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 +1070,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 +1088,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 +1103,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 +1113,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 +1139,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 +1169,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 +1195,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 +1213,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 85a2015..885dc9b 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
+#include <type_traits>
 #define LOG_TAG "InputWindow"
 #define LOG_NDEBUG 0
 
+#include <android-base/stringprintf.h>
 #include <binder/Parcel.h>
-#include <input/InputWindow.h>
 #include <input/InputTransport.h>
+#include <input/InputWindow.h>
 
 #include <log/log.h>
 
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
 namespace android {
 
+
 // --- InputWindowInfo ---
 void InputWindowInfo::addTouchableRegion(const Rect& region) {
     touchableRegion.orSelf(region);
@@ -42,22 +42,8 @@
             && y >= frameTop && y < frameBottom;
 }
 
-// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
-bool InputWindowInfo::isTrustedOverlay() const {
-    return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
-            layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
-            layoutParamsType == TYPE_NOTIFICATION_SHADE ||
-            layoutParamsType == TYPE_NAVIGATION_BAR ||
-            layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
-            layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
-            layoutParamsType == TYPE_DOCK_DIVIDER ||
-            layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
-            layoutParamsType == TYPE_INPUT_CONSUMER ||
-            layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
-}
-
 bool InputWindowInfo::supportsSplitTouch() const {
-    return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+    return flags.test(Flag::SPLIT_TOUCH);
 }
 
 bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
@@ -65,94 +51,148 @@
             && 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.hasWallpaper == hasWallpaper &&
+            info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+            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.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(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(ownerPid) ?:
+        parcel->writeInt32(ownerUid) ?:
+        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.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;
+    // clang-format off
+    status = parcel->readInt32(&frameLeft) ?:
+        parcel->readInt32(&frameTop) ?:
+        parcel->readInt32(&frameRight) ?:
+        parcel->readInt32(&frameBottom) ?:
+        parcel->readInt32(&surfaceInset) ?:
+        parcel->readFloat(&globalScaleFactor) ?:
+        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(&ownerPid) ?:
+        parcel->readInt32(&ownerUid);
+    // clang-format on
 
-InputWindowInfo::InputWindowInfo(const Parcel& from) {
-    *this = read(from);
+    if (status != OK) {
+        return status;
+    }
+
+    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() {
@@ -166,5 +206,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..7ac8a2e 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -24,9 +24,10 @@
 #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 +86,14 @@
 
 // --- KeyCharacterMap ---
 
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
-
 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 +106,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;
-}
-
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
-    return sEmpty;
+    mLoadFileName = overlay.mLoadFileName;
 }
 
 int32_t 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;
@@ -600,8 +591,12 @@
 }
 
 #ifdef __ANDROID__
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
-    sp<KeyCharacterMap> map = new KeyCharacterMap();
+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 = parcel->readInt32();
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
@@ -656,6 +651,10 @@
 }
 
 void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return;
+    }
     parcel->writeInt32(mType);
 
     size_t numKeys = mKeys.size();
@@ -880,7 +879,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 +896,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 +1016,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 +1033,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..38a68b3 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;
 }
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
new file mode 100644
index 0000000..4833eb9
--- /dev/null
+++ b/libs/input/PropertyMap.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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));
+    }
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+    *outMap = nullptr;
+
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::open(filename, &tokenizer);
+    if (status) {
+        ALOGE("Error %d opening property file %s.", status, filename.string());
+    } else {
+        PropertyMap* map = new PropertyMap();
+        if (!map) {
+            ALOGE("Error allocating property map.");
+            status = NO_MEMORY;
+        } else {
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+            Parser parser(map, tokenizer);
+            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) {
+                delete map;
+            } else {
+                *outMap = map;
+            }
+        }
+        delete tokenizer;
+    }
+    return status;
+}
+
+// --- 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..23ead0e
--- /dev/null
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 propertyMap) -> 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* mapPtr = &propertyMap;
+                    android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+                },
+                [](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 proprtyMap = 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, proprtyMap);
+    }
+    return 0;
+}
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..7c28ac5 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
 
 // --- VelocityTracker ---
 
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use.  Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
-        mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
-    char value[PROPERTY_VALUE_MAX];
-
-    // Allow the default strategy to be overridden using a system property for debugging.
-    if (!strategy) {
-        int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
-        if (length > 0) {
-            strategy = value;
-        } else {
-            strategy = DEFAULT_STRATEGY;
-        }
-    }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+      : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
     // Configure the strategy.
     if (!configureStrategy(strategy)) {
-        ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
-        if (!configureStrategy(DEFAULT_STRATEGY)) {
-            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
-                    strategy);
+        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+        if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+                             "'!",
+                             strategy);
         }
     }
 }
 
 VelocityTracker::~VelocityTracker() {
-    delete mStrategy;
 }
 
-bool VelocityTracker::configureStrategy(const char* strategy) {
-    mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+    if (strategy == VelocityTracker::Strategy::DEFAULT) {
+        mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+    } else {
+        mStrategy = createStrategy(strategy);
+    }
     return mStrategy != nullptr;
 }
 
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
-    if (!strcmp("impulse", strategy)) {
-        // Physical model of pushing an object.  Quality: VERY GOOD.
-        // Works with duplicate coordinates, unclean finger liftoff.
-        return new ImpulseVelocityTrackerStrategy();
-    }
-    if (!strcmp("lsq1", strategy)) {
-        // 1st order least squares.  Quality: POOR.
-        // Frequently underfits the touch data especially when the finger accelerates
-        // or changes direction.  Often underestimates velocity.  The direction
-        // is overly influenced by historical touch points.
-        return new LeastSquaresVelocityTrackerStrategy(1);
-    }
-    if (!strcmp("lsq2", strategy)) {
-        // 2nd order least squares.  Quality: VERY GOOD.
-        // Pretty much ideal, but can be confused by certain kinds of touch data,
-        // particularly if the panel has a tendency to generate delayed,
-        // duplicate or jittery touch coordinates when the finger is released.
-        return new LeastSquaresVelocityTrackerStrategy(2);
-    }
-    if (!strcmp("lsq3", strategy)) {
-        // 3rd order least squares.  Quality: UNUSABLE.
-        // Frequently overfits the touch data yielding wildly divergent estimates
-        // of the velocity when the finger is released.
-        return new LeastSquaresVelocityTrackerStrategy(3);
-    }
-    if (!strcmp("wlsq2-delta", strategy)) {
-        // 2nd order weighted least squares, delta weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
-    }
-    if (!strcmp("wlsq2-central", strategy)) {
-        // 2nd order weighted least squares, central weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
-    }
-    if (!strcmp("wlsq2-recent", strategy)) {
-        // 2nd order weighted least squares, recent weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
-    }
-    if (!strcmp("int1", strategy)) {
-        // 1st order integrating filter.  Quality: GOOD.
-        // Not as good as 'lsq2' because it cannot estimate acceleration but it is
-        // more tolerant of errors.  Like 'lsq1', this strategy tends to underestimate
-        // the velocity of a fling but this strategy tends to respond to changes in
-        // direction more quickly and accurately.
-        return new IntegratingVelocityTrackerStrategy(1);
-    }
-    if (!strcmp("int2", strategy)) {
-        // 2nd order integrating filter.  Quality: EXPERIMENTAL.
-        // For comparison purposes only.  Unlike 'int1' this strategy can compensate
-        // for acceleration but it typically overestimates the effect.
-        return new IntegratingVelocityTrackerStrategy(2);
-    }
-    if (!strcmp("legacy", strategy)) {
-        // Legacy velocity tracker algorithm.  Quality: POOR.
-        // For comparison purposes only.  This algorithm is strongly influenced by
-        // old data points, consistently underestimates velocity and takes a very long
-        // time to adjust to changes in direction.
-        return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+        VelocityTracker::Strategy strategy) {
+    switch (strategy) {
+        case VelocityTracker::Strategy::IMPULSE:
+            return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+        case VelocityTracker::Strategy::LSQ1:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+        case VelocityTracker::Strategy::LSQ2:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+        case VelocityTracker::Strategy::LSQ3:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+        case VelocityTracker::Strategy::WLSQ2_DELTA:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_DELTA);
+        case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_CENTRAL);
+        case VelocityTracker::Strategy::WLSQ2_RECENT:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_RECENT);
+
+        case VelocityTracker::Strategy::INT1:
+            return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+        case VelocityTracker::Strategy::INT2:
+            return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+        case VelocityTracker::Strategy::LEGACY:
+            return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+        default:
+            break;
     }
     return nullptr;
 }
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/ui/UiConfig.cpp b/libs/input/android/InputApplicationInfo.aidl
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to libs/input/android/InputApplicationInfo.aidl
index 0ac863d..9336039 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/input/android/InputApplicationInfo.aidl
@@ -1,11 +1,11 @@
-/*
- * 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.
  * 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,15 +14,10 @@
  * limitations under the License.
  */
 
-#include <ui/UiConfig.h>
+package android;
 
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
-    static const char* config =
-            " [libui]";
-    configStr.append(config);
+parcelable InputApplicationInfo {
+    @nullable IBinder token;
+    @utf8InCpp String name;
+    long dispatchingTimeoutMillis;
 }
-
-
-}; // namespace android
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/ui/UiConfig.cpp b/libs/input/android/os/IInputConstants.aidl
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to libs/input/android/os/IInputConstants.aidl
index 0ac863d..82c220f 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -1,11 +1,11 @@
-/*
- * 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.
  * 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,15 +14,11 @@
  * 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 */
+interface IInputConstants
+{
+    const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
 }
-
-
-}; // namespace android
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
new file mode 100644
index 0000000..0cdf0bb
--- /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);
+    void registerInputChannel(in InputChannel channel);
+    void unregisterInputChannel(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/ui/UiConfig.cpp b/libs/input/android/os/ISetInputWindowsListener.aidl
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to libs/input/android/os/ISetInputWindowsListener.aidl
index 0ac863d..bb58fb6 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/input/android/os/ISetInputWindowsListener.aidl
@@ -1,11 +1,11 @@
-/*
- * 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.
  * 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,15 +14,10 @@
  * 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 */
+oneway interface ISetInputWindowsListener
+{
+    void onSetInputWindowsFinished();
 }
-
-
-}; // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 3b57146..9782c1a 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,7 @@
 cc_test {
     name: "libinput_tests",
     srcs: [
+        "Flags_test.cpp",
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
         "InputDevice_test.cpp",
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..1452745 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,8 @@
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
-    EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
-    EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+    EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
+    EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
@@ -185,12 +174,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 +208,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 +327,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 +343,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 +364,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..65a7761 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,41 @@
     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.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.ownerPid = 19;
     i.ownerUid = 24;
-    i.inputFeatures = 29;
+    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,11 +86,9 @@
     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.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.ownerPid, i2.ownerPid);
@@ -97,6 +98,21 @@
     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/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 bf452c0..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);
 
@@ -191,8 +192,9 @@
     return events;
 }
 
-static void computeAndCheckVelocity(const char* strategy,
-        const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+                                    const std::vector<MotionEventEntry>& motions, int32_t axis,
+                                    float targetVelocity) {
     VelocityTracker vt(strategy);
     float Vx, Vy;
 
@@ -217,7 +219,7 @@
 
 static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
         const std::array<float, 3>& coefficients) {
-    VelocityTracker vt("lsq2");
+    VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
     std::vector<MotionEvent> events = createMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
@@ -238,36 +240,34 @@
     // 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("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            1600);
 }
 
 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("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
 }
 
 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("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
 }
 
 
@@ -297,8 +297,10 @@
         { 96948871ns, {{274.79245, 428.113159}} },
         { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            623.577637);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            5970.7309);
 }
 
 // --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +341,14 @@
         { 235089162955851ns, {{560.66, 843.82}} },
         { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
-    computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            872.794617);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            951.698181);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -3604.819336);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3044.966064);
 }
 
 
@@ -368,8 +374,10 @@
         { 235110660368000ns, {{530.00, 980.00}} },
         { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -4096.583008);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3455.094238);
 }
 
 
@@ -396,10 +404,14 @@
         { 792629200000ns, {{619.00, 1115.00}} },
         { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            574.33429);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            617.40564);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -2361.982666);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -2500.055664);
 }
 
 
@@ -426,10 +438,14 @@
         { 235160520366000ns, {{679.00, 814.00}} },
         { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            1274.141724);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            1438.53186);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -3001.4348);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3695.859619);
 }
 
 
@@ -452,8 +468,10 @@
         { 847237986000ns, {{610.00, 1095.00}} },
         { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -4280.07959);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -4241.004395);
 }
 
 
@@ -476,8 +494,10 @@
         { 235200616933000ns, {{590.00, 844.00}} },
         { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -8715.686523);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -7639.026367);
 }
 
 
@@ -499,10 +519,14 @@
         { 920989261000ns, {{715.00, 903.00}} },
         { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            5670.329102);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            5991.866699);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -13021.101562);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -15093.995117);
 }
 
 
@@ -522,8 +546,10 @@
         { 235247220736000ns, {{620.00, 641.00}} },
         { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -20286.958984);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -20494.587891);
 }
 
 
@@ -541,8 +567,10 @@
         { 235302613019881ns, {{679.26, 526.73}} },
         { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -39295.941406);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -36461.421875);
 }
 
 
@@ -569,10 +597,14 @@
         { 235655842893000ns, {{563.00, 649.00}} },
         { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -419.749695);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -398.303894);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            3309.016357);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            3969.099854);
 }
 
 
@@ -599,10 +631,14 @@
         { 235671246532000ns, {{470.00, 799.00}} },
         { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -262.80426);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -243.665344);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4215.682129);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4587.986816);
 }
 
 
@@ -622,10 +658,14 @@
         { 171051052000ns, {{536.00, 586.00}} },
         { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -723.413513);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -651.038452);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            2091.502441);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            1934.517456);
 }
 
 
@@ -652,8 +692,10 @@
         { 235695373403000ns, {{564.00, 744.00}} },
         { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4254.639648);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4698.415039);
 }
 
 
@@ -677,10 +719,14 @@
         { 235709710626776ns, {{511.72, 741.85}} },
         { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -430.440247);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -447.600311);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            3953.859375);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4316.155273);
 }
 
 
@@ -706,8 +752,10 @@
         { 235727721580000ns, {{516.00, 658.00}} },
         { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4484.617676);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4927.92627);
 }
 
 
@@ -725,8 +773,10 @@
         { 235762396429369ns, {{404.37, 680.67}} },
         { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            14227.0224);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            16064.685547);
 }
 
 
@@ -744,8 +794,10 @@
         { 235772537635000ns, {{484.00, 589.00}} },
         { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            18660.048828);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            16918.439453);
 }
 
 
@@ -764,10 +816,14 @@
         { 507703352649ns, {{443.71, 857.77}} },
         { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -4111.8173);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -6388.48877);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            29765.908203);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            28354.796875);
 }
 
 /**
@@ -789,10 +845,10 @@
 
     // Velocity should actually be zero, but we expect 0.016 here instead.
     // This is close enough to zero, and is likely caused by division by a very small number.
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
 }
 
 /**
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/math/Android.bp b/libs/math/Android.bp
index 693bace..22d4712 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -16,7 +16,20 @@
     name: "libmath",
     host_supported: true,
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+        "com.android.neuralnetworks",
+    ],
+    min_sdk_version: "29",
+
     export_include_dirs: ["include"],
+    target:  {
+        windows: {
+            enabled: true,
+        }
+    }
 }
 
 subdirs = ["tests"]
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 7682973..617a0ab 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -82,6 +82,7 @@
     };
 
 public:
+    CONSTEXPR half() noexcept { }
     CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
     CONSTEXPR operator float() const noexcept { return htof(mBits); }
 
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 496a7ef..604072e 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -35,6 +35,7 @@
     EXPECT_EQ(2UL, sizeof(half));
 
     // test +/- zero
+    EXPECT_EQ(0x0000, half().getBits());
     EXPECT_EQ(0x0000, half( 0.0f).getBits());
     EXPECT_EQ(0x8000, half(-0.0f).getBits());
 
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..ff1b5e6 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -136,6 +136,7 @@
     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();
 
@@ -170,7 +171,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 +295,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);
     }
 }
 
@@ -363,9 +370,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 +380,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) {
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/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..52d73e0 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",
 }
 
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 25130e2..36aad2e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -236,7 +236,11 @@
 /** Compatibility value for ANativeWindow_setFrameRate. */
 enum ANativeWindow_FrameRateCompatibility {
     /**
-     * There are no inherent restrictions on the frame rate of this window.
+     * There are no inherent restrictions on the frame rate of this window. When
+     * the system selects a frame rate other than what the app requested, the
+     * app will be able to run at the system frame rate without requiring pull
+     * down. This value should be used when displaying game content, UIs, and
+     * anything that isn't video.
      */
     ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
     /**
@@ -246,7 +250,7 @@
      * to do pull down or use some other technique to adapt to the system's
      * frame rate. The user experience is likely to be worse (e.g. more frame
      * stuttering) than it would be if the system had chosen the app's requested
-     * frame rate.
+     * frame rate. This value should be used for video content.
      */
     ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
 };
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb6080f..9264bb6 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -64,13 +64,16 @@
     ],
 }
 
+filegroup {
+    name: "librenderengine_threaded_sources",
+    srcs: [
+        "threaded/RenderEngineThreaded.cpp",
+    ],
+}
+
 cc_library_static {
     name: "librenderengine",
     defaults: ["librenderengine_defaults"],
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
     double_loadable: true,
     clang: true,
     cflags: [
@@ -80,6 +83,7 @@
     srcs: [
         ":librenderengine_sources",
         ":librenderengine_gl_sources",
+        ":librenderengine_threaded_sources",
     ],
     lto: {
         thin: true,
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 0fdf093..eb0074b 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -20,19 +20,34 @@
 #include <log/log.h>
 #include <private/gui/SyncFeatures.h>
 #include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
 
 namespace android {
 namespace renderengine {
 
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+    RenderEngineType renderEngineType = args.renderEngineType;
+
+    // Keep the ability to override by PROPERTIES:
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
     if (strcmp(prop, "gles") == 0) {
-        ALOGD("RenderEngine GLES Backend");
-        return renderengine::gl::GLESRenderEngine::create(args);
+        renderEngineType = RenderEngineType::GLES;
     }
-    ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
-    return renderengine::gl::GLESRenderEngine::create(args);
+    if (strcmp(prop, "threaded") == 0) {
+        renderEngineType = RenderEngineType::THREADED;
+    }
+
+    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::GLES:
+        default:
+            ALOGD("RenderEngine with GLES Backend");
+            return renderengine::gl::GLESRenderEngine::create(args);
+    }
 }
 
 RenderEngine::~RenderEngine() = default;
@@ -47,10 +62,6 @@
     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 92e7e71..42e74df 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,15 +354,15 @@
 }
 
 GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
-                                   EGLConfig config, EGLContext ctxt, EGLSurface dummy,
-                                   EGLContext protectedContext, EGLSurface protectedDummy)
+                                   EGLConfig config, EGLContext ctxt, EGLSurface stub,
+                                   EGLContext protectedContext, EGLSurface protectedStub)
       : renderengine::impl::RenderEngine(args),
         mEGLDisplay(display),
         mEGLConfig(config),
         mEGLContext(ctxt),
-        mDummySurface(dummy),
+        mStubSurface(stub),
         mProtectedEGLContext(protectedContext),
-        mProtectedDummySurface(protectedDummy),
+        mProtectedStubSurface(protectedStub),
         mVpWidth(0),
         mVpHeight(0),
         mFramebufferImageCacheSize(args.imageCacheSize),
@@ -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);
@@ -589,6 +622,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);
 }
 
@@ -646,6 +682,7 @@
         }
 
         bindExternalTextureImage(texName, *cachedImage->second);
+        mTextureView.insert_or_assign(texName, buffer->getId());
     }
 
     // Wait for the new buffer to be ready.
@@ -887,7 +924,7 @@
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
     ATRACE_CALL();
 
     if (mPriorResourcesCleaned ||
@@ -896,9 +933,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 +968,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 +1010,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) {
@@ -982,7 +1058,7 @@
 
 status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
                                       const std::vector<const LayerSettings*>& layers,
-                                      ANativeWindowBuffer* const buffer,
+                                      const sp<GraphicBuffer>& buffer,
                                       const bool useFramebufferCache, base::unique_fd&& bufferFence,
                                       base::unique_fd* drawFence) {
     ATRACE_CALL();
@@ -1019,7 +1095,9 @@
     const auto blurLayersSize = blurLayers.size();
 
     if (blurLayersSize == 0) {
-        fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+        fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+                                                              buffer.get()->getNativeBuffer(),
+                                                              useFramebufferCache);
         if (fbo->getStatus() != NO_ERROR) {
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                   buffer->handle);
@@ -1076,7 +1154,9 @@
 
             if (blurLayers.size() == 0) {
                 // Done blurring, time to bind the native FBO and render our blur onto it.
-                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+                                                                      buffer.get()
+                                                                              ->getNativeBuffer(),
                                                                       useFramebufferCache);
                 status = fbo->getStatus();
                 setViewportAndProjection(display.physicalDisplay, display.clip);
@@ -1228,7 +1308,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);
     }
@@ -1562,11 +1643,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);
@@ -1580,7 +1661,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 {
@@ -1616,6 +1697,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 42b8537..8f0df2c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -53,29 +53,26 @@
     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;
     bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+                        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 +83,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 +99,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 +120,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 +132,10 @@
     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 cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
 
     // A data space is considered HDR data space if it has BT2020 color space
     // with PQ or HLG transfer function.
@@ -175,9 +184,9 @@
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
     EGLContext mEGLContext;
-    EGLSurface mDummySurface;
+    EGLSurface mStubSurface;
     EGLContext mProtectedEGLContext;
-    EGLSurface mProtectedDummySurface;
+    EGLSurface mProtectedStubSurface;
     GLint mMaxViewportDims[2];
     GLint mMaxTextureSize;
     GLuint mVpWidth;
@@ -224,6 +233,8 @@
 
     // 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 +248,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/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e06e128..a0e7ab7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -44,12 +44,15 @@
 
 namespace renderengine {
 
-class BindNativeBufferAsFramebuffer;
 class Image;
 class Mesh;
 class Texture;
 struct RenderEngineCreationArgs;
 
+namespace threaded {
+class RenderEngineThreaded;
+}
+
 namespace impl {
 class RenderEngine;
 }
@@ -67,7 +70,12 @@
         HIGH = 3,
     };
 
-    static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
+    enum class RenderEngineType {
+        GLES = 1,
+        THREADED = 2,
+    };
+
+    static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
 
     virtual ~RenderEngine() = 0;
 
@@ -81,10 +89,8 @@
     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.
@@ -107,18 +113,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;
@@ -163,17 +176,12 @@
     // now, this always returns NO_ERROR.
     virtual status_t drawLayers(const DisplaySettings& display,
                                 const std::vector<const LayerSettings*>& layers,
-                                ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+                                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;
 };
 
 struct RenderEngineCreationArgs {
@@ -184,26 +192,25 @@
     bool precacheToneMapperShaderOnly;
     bool supportsBackgroundBlur;
     RenderEngine::ContextPriority contextPriority;
+    RenderEngine::RenderEngineType renderEngineType;
 
     struct Builder;
 
 private:
     // must be created by Builder via constructor with full argument list
-    RenderEngineCreationArgs(
-            int _pixelFormat,
-            uint32_t _imageCacheSize,
-            bool _useColorManagement,
-            bool _enableProtectedContext,
-            bool _precacheToneMapperShaderOnly,
-            bool _supportsBackgroundBlur,
-            RenderEngine::ContextPriority _contextPriority)
-        : pixelFormat(_pixelFormat)
-        , imageCacheSize(_imageCacheSize)
-        , useColorManagement(_useColorManagement)
-        , enableProtectedContext(_enableProtectedContext)
-        , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
-        , supportsBackgroundBlur(_supportsBackgroundBlur)
-        , contextPriority(_contextPriority) {}
+    RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+                             bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+                             bool _supportsBackgroundBlur,
+                             RenderEngine::ContextPriority _contextPriority,
+                             RenderEngine::RenderEngineType _renderEngineType)
+          : pixelFormat(_pixelFormat),
+            imageCacheSize(_imageCacheSize),
+            useColorManagement(_useColorManagement),
+            enableProtectedContext(_enableProtectedContext),
+            precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+            supportsBackgroundBlur(_supportsBackgroundBlur),
+            contextPriority(_contextPriority),
+            renderEngineType(_renderEngineType) {}
     RenderEngineCreationArgs() = delete;
 };
 
@@ -238,10 +245,14 @@
         this->contextPriority = contextPriority;
         return *this;
     }
+    Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+        this->renderEngineType = renderEngineType;
+        return *this;
+    }
     RenderEngineCreationArgs build() const {
         return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
                                         enableProtectedContext, precacheToneMapperShaderOnly,
-                                        supportsBackgroundBlur, contextPriority);
+                                        supportsBackgroundBlur, contextPriority, renderEngineType);
     }
 
 private:
@@ -253,28 +264,7 @@
     bool precacheToneMapperShaderOnly = false;
     bool supportsBackgroundBlur = false;
     RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
-};
-
-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;
+    RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
 };
 
 namespace impl {
@@ -285,7 +275,6 @@
     virtual ~RenderEngine() = 0;
 
     bool useNativeFenceSync() const override;
-    bool useWaitSync() const override;
 
 protected:
     RenderEngine(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index df0f17a..0b80d88 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -43,7 +43,6 @@
     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>&));
@@ -56,10 +55,12 @@
     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*>&,
-                          ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+                          const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                          base::unique_fd*));
+    MOCK_METHOD0(cleanFramebufferCache, void());
 };
 
 } // namespace mock
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index e98babc..bcf389b 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -18,10 +18,12 @@
     test_suites: ["device-tests"],
     srcs: [
         "RenderEngineTest.cpp",
+        "RenderEngineThreadedTest.cpp",
     ],
     static_libs: [
         "libgmock",
         "librenderengine",
+        "librenderengine_mocks",
     ],
     shared_libs: [
         "libbase",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 16a8a0d..c92e817 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,12 +22,13 @@
 #include <condition_variable>
 #include <fstream>
 
-#include <gtest/gtest.h>
 #include <cutils/properties.h>
+#include <gtest/gtest.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
 #include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
 
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -40,14 +41,15 @@
     static void SetUpTestSuite() {
         sRE = renderengine::gl::GLESRenderEngine::create(
                 renderengine::RenderEngineCreationArgs::Builder()
-                    .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
-                    .setImageCacheSize(1)
-                    .setUseColorManagerment(false)
-                    .setEnableProtectedContext(false)
-                    .setPrecacheToneMapperShaderOnly(false)
-                    .setSupportsBackgroundBlur(true)
-                    .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
-        .build());
+                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                        .setImageCacheSize(1)
+                        .setUseColorManagerment(false)
+                        .setEnableProtectedContext(false)
+                        .setPrecacheToneMapperShaderOnly(false)
+                        .setSupportsBackgroundBlur(true)
+                        .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+                        .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+                        .build());
     }
 
     static void TearDownTestSuite() {
@@ -81,6 +83,7 @@
         }
         for (uint32_t texName : mTexNames) {
             sRE->deleteTextures(1, &texName);
+            EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
         }
     }
 
@@ -252,8 +255,8 @@
                     std::vector<const renderengine::LayerSettings*> layers,
                     sp<GraphicBuffer> buffer) {
         base::unique_fd fence;
-        status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
-                                          base::unique_fd(), &fence);
+        status_t status =
+                sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
         sCurrentBuffer = buffer;
 
         int fd = fence.release();
@@ -916,7 +919,7 @@
 
 void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
     fillRedBufferWithoutPremultiplyAlpha();
-    expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+    expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
 }
 
 void RenderEngineTest::clearLeftRegion() {
@@ -926,7 +929,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);
@@ -1004,8 +1007,7 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
-                                      base::unique_fd(), nullptr);
+    status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
     sCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
@@ -1023,8 +1025,7 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
-                                      base::unique_fd(), nullptr);
+    status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
     sCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
     ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
@@ -1414,20 +1415,52 @@
     layers.push_back(&layer);
 
     base::unique_fd fenceOne;
-    sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
-                    &fenceOne);
+    sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
     base::unique_fd fenceTwo;
-    sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
-                    &fenceTwo);
+    sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
 
     const int fd = fenceTwo.get();
     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
new file mode 100644
index 0000000..78fec29
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+    ~RenderEngineThreadedTest() {}
+
+    void SetUp() override {
+        mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+                [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+    }
+
+    std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+    std::string testString = "XYZ";
+    EXPECT_CALL(*mRenderEngine, dump(_));
+    mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+    EXPECT_CALL(*mRenderEngine, primeCache());
+    mThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+    uint32_t texName;
+    EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+    mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+    uint32_t texName;
+    EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+    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);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+    mThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+    EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+    mThreadedRE->unbindExternalTextureBuffer(0x0);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+    size_t size = 20;
+    EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+    size_t result = mThreadedRE->getMaxTextureSize();
+    ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+    size_t size = 0;
+    EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+    size_t result = mThreadedRE->getMaxTextureSize();
+    ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+    size_t dims = 20;
+    EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+    size_t result = mThreadedRE->getMaxViewportDims();
+    ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+    size_t dims = 0;
+    EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+    size_t result = mThreadedRE->getMaxViewportDims();
+    ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+    status_t result = mThreadedRE->isProtected();
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+    size_t result = mThreadedRE->isProtected();
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+    status_t result = mThreadedRE->supportsProtectedContent();
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+    status_t result = mThreadedRE->supportsProtectedContent();
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+    status_t result = mThreadedRE->useProtectedContext(false);
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+    status_t result = mThreadedRE->useProtectedContext(false);
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+    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(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+            .WillOnce(Return(true));
+    status_t result =
+            mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+    renderengine::DisplaySettings settings;
+    std::vector<const renderengine::LayerSettings*> layers;
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    base::unique_fd bufferFence;
+    base::unique_fd drawFence;
+
+    EXPECT_CALL(*mRenderEngine, drawLayers)
+            .WillOnce([](const renderengine::DisplaySettings&,
+                         const std::vector<const renderengine::LayerSettings*>&,
+                         const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                         base::unique_fd*) -> status_t { return NO_ERROR; });
+
+    status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+                                              std::move(bufferFence), &drawFence);
+    ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..9b79943
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+    return std::make_unique<RenderEngineThreaded>(std::move(factory));
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+    ATRACE_CALL();
+
+    std::lock_guard lockThread(mThreadMutex);
+    mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+    {
+        std::lock_guard lock(mThreadMutex);
+        mRunning = false;
+        mCondition.notify_one();
+    }
+
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
+
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO");
+    }
+
+    mRenderEngine = factory();
+
+    std::unique_lock<std::mutex> lock(mThreadMutex);
+    pthread_setname_np(pthread_self(), mThreadName);
+
+    while (mRunning) {
+        if (!mFunctionCalls.empty()) {
+            auto task = mFunctionCalls.front();
+            mFunctionCalls.pop();
+            task(*mRenderEngine);
+        }
+        mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+            return !mRunning || !mFunctionCalls.empty();
+        });
+    }
+}
+
+void RenderEngineThreaded::primeCache() const {
+    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::primeCache");
+            instance.primeCache();
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+    std::promise<std::string> resultPromise;
+    std::future<std::string> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::dump");
+            std::string localResult = result;
+            instance.dump(localResult);
+            resultPromise.set_value(std::move(localResult));
+        });
+    }
+    mCondition.notify_one();
+    // Note: This is an rvalue.
+    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();
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::genTextures");
+            instance.genTextures(count, names);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::deleteTextures");
+            instance.deleteTextures(count, names);
+            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();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+            instance.cacheExternalTextureBuffer(buffer);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+            instance.unbindExternalTextureBuffer(bufferId);
+            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();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getMaxTextureSize");
+            size_t size = instance.getMaxTextureSize();
+            resultPromise.set_value(size);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+    std::promise<size_t> resultPromise;
+    std::future<size_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getMaxViewportDims");
+            size_t size = instance.getMaxViewportDims();
+            resultPromise.set_value(size);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() 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::isProtected");
+            bool returnValue = instance.isProtected();
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() 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::supportsProtectedContent");
+            bool returnValue = instance.supportsProtectedContent();
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push(
+                [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+                    ATRACE_NAME("REThreaded::useProtectedContext");
+                    bool returnValue = instance.useProtectedContext(useProtectedContext);
+                    resultPromise.set_value(returnValue);
+                });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::cleanupPostRender");
+            bool returnValue = instance.cleanupPostRender(mode);
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::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) {
+    std::promise<status_t> resultPromise;
+    std::future<status_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+                             &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::drawLayers");
+            status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+                                                  std::move(bufferFence), drawFence);
+            resultPromise.set_value(status);
+        });
+    }
+    mCondition.notify_one();
+    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
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..b02cdd9
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,92 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+    static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+
+    RenderEngineThreaded(CreateInstanceFactory factory);
+    ~RenderEngineThreaded() override;
+    void primeCache() const override;
+
+    void dump(std::string& result) override;
+
+    bool useNativeFenceSync() const override;
+    void genTextures(size_t count, uint32_t* names) override;
+    void deleteTextures(size_t count, uint32_t const* names) 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;
+    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(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;
+
+    void cleanFramebufferCache() override;
+
+private:
+    void threadMain(CreateInstanceFactory factory);
+
+    /* ------------------------------------------------------------------------
+     * Threading
+     */
+    const char* const mThreadName = "RenderEngineThread";
+    // Protects the creation and destruction of mThread.
+    mutable std::mutex mThreadMutex;
+    std::thread mThread GUARDED_BY(mThreadMutex);
+    bool mRunning GUARDED_BY(mThreadMutex) = true;
+    mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+            GUARDED_BY(mThreadMutex);
+    mutable std::condition_variable mCondition;
+
+    /* ------------------------------------------------------------------------
+     * Render Engine
+     */
+    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // 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 1ee8c71..3fa2e53 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -12,6 +12,74 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+cc_defaults {
+    name: "libui-defaults",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    cppflags: [
+        "-Wextra",
+    ],
+
+    sanitize: {
+        integer_overflow: true,
+        misc_undefined: ["bounds"],
+    },
+
+}
+
+cc_library_static {
+    name: "libui-types",
+    vendor_available: true,
+    host_supported: true,
+    target: {
+        windows: {
+            enabled: true,
+        }
+    },
+
+    defaults: [
+        "libui-defaults",
+    ],
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+    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",
+    ],
+
+}
+
 cc_library_shared {
     name: "libui",
     vendor_available: true,
@@ -35,8 +103,9 @@
     },
 
     srcs: [
-        "ColorSpace.cpp",
         "DebugUtils.cpp",
+        "DeviceProductInfo.cpp",
+        "DisplayInfo.cpp",
         "Fence.cpp",
         "FenceTime.cpp",
         "FrameStats.cpp",
@@ -50,11 +119,7 @@
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
-        "Rect.cpp",
-        "Region.cpp",
         "Size.cpp",
-        "Transform.cpp",
-        "UiConfig.cpp",
     ],
 
     include_dirs: [
@@ -65,8 +130,11 @@
         "include_private",
     ],
 
-    // Uncomment the following line to enable VALIDATE_REGIONS traces
-    //defaults: ["libui-validate-regions-defaults"],
+    defaults: [
+        "libui-defaults",
+        // Uncomment the following line to enable VALIDATE_REGIONS traces
+        //defaults: ["libui-validate-regions-defaults"],
+    ],
 
     shared_libs: [
         "android.hardware.graphics.allocator@2.0",
@@ -100,6 +168,10 @@
         "libmath",
     ],
 
+    whole_static_libs: [
+        "libui-types",
+    ],
+
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
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/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
new file mode 100644
index 0000000..4d6ce43
--- /dev/null
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -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.
+ */
+
+#include <ui/DeviceProductInfo.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
+
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+using base::StringAppendF;
+
+size_t DeviceProductInfo::getFlattenedSize() const {
+    return FlattenableHelpers::getFlattenedSize(name) +
+            FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+            FlattenableHelpers::getFlattenedSize(productId) +
+            FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+            FlattenableHelpers::getFlattenedSize(relativeAddress);
+}
+
+status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+    return OK;
+}
+
+status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+    return OK;
+}
+
+void DeviceProductInfo::dump(std::string& result) const {
+    StringAppendF(&result, "{name=%s, ", name.c_str());
+    StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+    StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+    if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "modelYear=%u, ", model->year);
+    } else if (const auto* manufactureWeekAndYear =
+                       std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+        StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+    } else if (const auto* manufactureYear =
+                       std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+    } else {
+        ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+    }
+
+    result.append("relativeAddress=[");
+    for (size_t i = 0; i < relativeAddress.size(); i++) {
+        if (i != 0) {
+            result.append(", ");
+        }
+        StringAppendF(&result, "%u", relativeAddress[i]);
+    }
+    result.append("]}");
+}
+
+} // namespace android
diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp
new file mode 100644
index 0000000..73a78af
--- /dev/null
+++ b/libs/ui/DisplayInfo.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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/DisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+size_t DisplayInfo::getFlattenedSize() const {
+    return FlattenableHelpers::getFlattenedSize(connectionType) +
+            FlattenableHelpers::getFlattenedSize(density) +
+            FlattenableHelpers::getFlattenedSize(secure) +
+            FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+}
+
+status_t DisplayInfo::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+    return OK;
+}
+
+status_t DisplayInfo::unflatten(void const* buffer, size_t size) {
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+    return OK;
+}
+
+} // namespace android
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 97ead21..b1317b1 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,7 +1,7 @@
+alecmouri@google.com
+chrisforbes@google.com
+jreck@google.com
 lpy@google.com
-marissaw@google.com
 mathias@google.com
 romainguy@google.com
 stoza@google.com
-jwcai@google.com
-tianyuj@google.com
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..ec8a78a 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -22,8 +22,7 @@
 #include <ui/Transform.h>
 #include <utils/String8.h>
 
-namespace android {
-namespace ui {
+namespace android::ui {
 
 Transform::Transform() {
     reset();
@@ -55,11 +54,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 +84,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 +115,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 +148,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 +160,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 +168,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 +210,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 +236,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 +271,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 +291,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 +311,7 @@
     return out;
 }
 
-uint32_t Transform::type() const
-{
+uint32_t Transform::type() const {
     if (mType & UNKNOWN_TYPE) {
         // recompute what this transform is
 
@@ -380,16 +406,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 +449,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 +493,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/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/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index af00342..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -19,7 +19,12 @@
 #include <array>
 #include <cstdint>
 #include <optional>
+#include <string>
+#include <type_traits>
 #include <variant>
+#include <vector>
+
+#include <utils/Flattenable.h>
 
 namespace android {
 
@@ -29,13 +34,7 @@
 // Product-specific information about the display or the directly connected device on the
 // display chain. For example, if the display is transitively connected, this field may contain
 // product information about the intermediate device.
-struct DeviceProductInfo {
-    static constexpr size_t TEXT_BUFFER_SIZE = 20;
-    static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
-
-    using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
-    static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
-
+struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> {
     struct ModelYear {
         uint32_t year;
     };
@@ -48,21 +47,29 @@
     };
 
     // Display name.
-    std::array<char, TEXT_BUFFER_SIZE> name;
+    std::string name;
 
     // Manufacturer Plug and Play ID.
     PnpId manufacturerPnpId;
 
     // Manufacturer product ID.
-    std::array<char, TEXT_BUFFER_SIZE> productId;
+    std::string productId;
 
     using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+    static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
     ManufactureOrModelDate manufactureOrModelDate;
 
-    // Relative address in the display network. Unavailable address is indicated
-    // by all elements equal to 255.
+    // Relative address in the display network. Empty vector indicates that the
+    // address is unavailable.
     // For example, for HDMI connected device this will be the physical address.
-    RelativeAddress relativeAddress;
+    std::vector<uint8_t> relativeAddress;
+
+    bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
+
+    void dump(std::string& result) const;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
new file mode 100644
index 0000000..9eb5483
--- /dev/null
+++ b/libs/ui/include/ui/DisplayId.h
@@ -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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <string>
+
+namespace android {
+
+// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+struct DisplayId {
+    // 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 {
+    // Flag indicating that the ID is stable across reboots.
+    static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
+    // 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;
+    explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
+    explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other.value) {}
+
+    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) {}
+};
+
+static_assert(sizeof(PhysicalDisplayId) == 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> {};
+
+} // namespace std
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 897060c..03e0a38 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -20,19 +20,23 @@
 #include <type_traits>
 
 #include <ui/DeviceProductInfo.h>
+#include <utils/Flattenable.h>
 
 namespace android {
 
 enum class DisplayConnectionType { Internal, External };
 
 // Immutable information about physical display.
-struct DisplayInfo {
+struct DisplayInfo : LightFlattenable<DisplayInfo> {
     DisplayConnectionType connectionType = DisplayConnectionType::Internal;
     float density = 0.f;
     bool secure = false;
     std::optional<DeviceProductInfo> deviceProductInfo;
-};
 
-static_assert(std::is_trivially_copyable_v<DisplayInfo>);
+    bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
+};
 
 } // namespace android
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/PhysicalDisplayId.h b/libs/ui/include/ui/PhysicalDisplayId.h
deleted file mode 100644
index 1a345ac..0000000
--- a/libs/ui/include/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1,32 +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 <cinttypes>
-#include <cstdint>
-
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
-namespace android {
-
-using PhysicalDisplayId = uint64_t;
-
-constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
-    return static_cast<uint8_t>(displayId);
-}
-
-} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2f2229e..6670dc0 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>
@@ -216,11 +216,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..9a434e5 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,8 +108,8 @@
     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);
 
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_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
new file mode 100644
index 0000000..8e316d8
--- /dev/null
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -0,0 +1,160 @@
+/*
+ * 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 <numeric>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+#include <utils/Flattenable.h>
+
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+struct FlattenableHelpers {
+    // Helpers for reading and writing POD structures
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static constexpr size_t getFlattenedSize(const T&) {
+        return sizeof(T);
+    }
+
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static status_t flatten(void** buffer, size_t* size, const T& value) {
+        if (*size < sizeof(T)) return NO_MEMORY;
+        FlattenableUtils::write(*buffer, *size, value);
+        return OK;
+    }
+
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static status_t unflatten(const void** buffer, size_t* size, T* value) {
+        if (*size < sizeof(T)) return NO_MEMORY;
+        FlattenableUtils::read(*buffer, *size, *value);
+        return OK;
+    }
+
+    // Helpers for reading and writing std::string
+    static size_t getFlattenedSize(const std::string& str) {
+        return sizeof(uint64_t) + str.length();
+    }
+
+    static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+        if (*size < getFlattenedSize(str)) return NO_MEMORY;
+        flatten(buffer, size, (uint64_t)str.length());
+        memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+        FlattenableUtils::advance(*buffer, *size, str.length());
+        return OK;
+    }
+
+    static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+        uint64_t length;
+        RETURN_IF_ERROR(unflatten(buffer, size, &length));
+        if (*size < length) return NO_MEMORY;
+        str->assign(reinterpret_cast<const char*>(*buffer), length);
+        FlattenableUtils::advance(*buffer, *size, length);
+        return OK;
+    }
+
+    // Helpers for reading and writing LightFlattenable
+    template <class T>
+    static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+        return value.getFlattenedSize();
+    }
+
+    template <class T>
+    static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+        RETURN_IF_ERROR(value.flatten(*buffer, *size));
+        FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+        return OK;
+    }
+
+    template <class T>
+    static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+        RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+        FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+        return OK;
+    }
+
+    // Helpers for reading and writing std::optional
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+    static size_t getFlattenedSize(const std::optional<T>& value) {
+        return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
+    }
+
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+    static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
+        if (value) {
+            RETURN_IF_ERROR(flatten(buffer, size, true));
+            RETURN_IF_ERROR(flatten(buffer, size, *value));
+        } else {
+            RETURN_IF_ERROR(flatten(buffer, size, false));
+        }
+        return OK;
+    }
+
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+    static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
+        bool isPresent;
+        RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
+        if (isPresent) {
+            *value = T();
+            RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
+        } else {
+            value->reset();
+        }
+        return OK;
+    }
+
+    // Helpers for reading and writing std::vector
+    template <class T>
+    static size_t getFlattenedSize(const std::vector<T>& value) {
+        return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
+                               [](size_t sum, const T& element) {
+                                   return sum + getFlattenedSize(element);
+                               });
+    }
+
+    template <class T>
+    static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+        RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
+        for (const auto& element : value) {
+            RETURN_IF_ERROR(flatten(buffer, size, element));
+        }
+        return OK;
+    }
+
+    template <class T>
+    static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+        uint64_t numElements;
+        RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+        // We don't need an extra size check since each iteration of the loop does that
+        std::vector<T> elements;
+        for (size_t i = 0; i < numElements; i++) {
+            T element;
+            RETURN_IF_ERROR(unflatten(buffer, size, &element));
+            elements.push_back(element);
+        }
+        *value = std::move(elements);
+        return OK;
+    }
+};
+
+} // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h
similarity index 100%
rename from libs/ui/include/ui/ColorSpace.h
rename to libs/ui/include_types/ui/ColorSpace.h
diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h
index ddf70d5..7d2a6d3 120000
--- a/libs/ui/include_vndk/ui/ColorSpace.h
+++ b/libs/ui/include_vndk/ui/ColorSpace.h
@@ -1 +1 @@
-../../include/ui/ColorSpace.h
\ No newline at end of file
+../../include_types/ui/ColorSpace.h
\ No newline at end of file
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 b53342c..28ef77a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
 }
 
 cc_test {
+    name: "FlattenableHelpers_test",
+    shared_libs: ["libui"],
+    srcs: ["FlattenableHelpers_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "GraphicBufferAllocator_test",
     header_libs: [
         "libnativewindow_headers",
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..db32bc7
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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_TAG "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+    std::unique_ptr<int32_t> ptr;
+
+    bool isFixedSize() const { return true; }
+    size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+    status_t flatten(void* buffer, size_t size) const {
+        FlattenableUtils::write(buffer, size, *ptr);
+        return OK;
+    }
+
+    status_t unflatten(void const* buffer, size_t size) {
+        int value;
+        FlattenableUtils::read(buffer, size, value);
+        ptr = std::make_unique<int32_t>(value);
+        return OK;
+    }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+    template <class T>
+    void testWriteThenRead(const T& value, size_t bufferSize) {
+        std::vector<int8_t> buffer(bufferSize);
+        auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+        auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+        size = buffer.size();
+        T valueRead;
+        ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+        EXPECT_EQ(value, valueRead);
+    }
+
+    template <class T>
+    void testTriviallyCopyable(const T& value) {
+        testWriteThenRead(value, sizeof(T));
+    }
+
+    template <class T>
+    void testWriteThenRead(const T& value) {
+        testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+    }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+    testTriviallyCopyable(42);
+    testTriviallyCopyable(1LL << 63);
+    testTriviallyCopyable(false);
+    testTriviallyCopyable(true);
+    testTriviallyCopyable(std::optional<int>());
+    testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+    testWriteThenRead(std::string("Android"));
+    testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+    testWriteThenRead(std::vector<int>({1, 2, 3}));
+    testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+    std::vector<size_t> buffer;
+    constexpr int kInternalValue = 16;
+    {
+        std::optional<TestLightFlattenable> value =
+                TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+        buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+        void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+    }
+
+    const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+    size_t size = buffer.size();
+    std::optional<TestLightFlattenable> valueRead;
+    ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+    ASSERT_TRUE(valueRead.has_value());
+    EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+    std::vector<size_t> buffer;
+    {
+        std::optional<TestLightFlattenable> value;
+        buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+        void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+    }
+
+    const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+    size_t size = buffer.size();
+    std::optional<TestLightFlattenable> valueRead;
+    ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+    ASSERT_FALSE(valueRead.has_value());
+}
+
+} // namespace
+} // namespace android
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..afde5f7
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -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.
+ */
+// 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.
+extern "C" int LLVMFuzzerTestOneInput(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);
+
+  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/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..1f1bcb3 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
 // found in the LICENSE file.
 //
 // eglext_angle.h: ANGLE modifications to the eglext.h header file.
-//   Currently we don't include this file directly, we patch eglext.h
-//   to include it implicitly so it is visible throughout our code.
 
 #ifndef INCLUDE_EGL_EGLEXT_ANGLE_
 #define INCLUDE_EGL_EGLEXT_ANGLE_
 
+#include <EGL/eglext.h>
+
 // clang-format off
 
 #ifndef EGL_ANGLE_robust_resource_initialization
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 3997134..3c76c62 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -100,12 +100,8 @@
         "libgraphicsenv",
         "libnativewindow",
         "libbacktrace",
+        "libbase",
     ],
-    target: {
-        vendor: {
-            exclude_shared_libs: ["libgraphicsenv"],
-        },
-    },
 }
 
 cc_library_static {
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 74c4d7d..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 <cutils/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;
 }
@@ -165,8 +165,9 @@
 }
 
 size_t BlobCache::getFlattenedSize() const {
-    size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
-    for (const CacheEntry& e :  mCacheEntries) {
+    auto buildId = base::GetProperty("ro.build.id", "");
+    size_t size = align4(sizeof(Header) + buildId.size());
+    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());
@@ -185,14 +186,14 @@
     header->mBlobCacheVersion = blobCacheVersion;
     header->mDeviceVersion = blobCacheDeviceVersion;
     header->mNumEntries = mCacheEntries.size();
-    char buildId[PROPERTY_VALUE_MAX];
-    header->mBuildIdLength = property_get("ro.build.id", buildId, "");
-    memcpy(header->mBuildId, buildId, header->mBuildIdLength);
+    auto buildId = base::GetProperty("ro.build.id", "");
+    header->mBuildIdLength = buildId.size();
+    memcpy(header->mBuildId, buildId.c_str(), header->mBuildIdLength);
 
     // 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();
@@ -238,12 +239,11 @@
         ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
         return -EINVAL;
     }
-    char buildId[PROPERTY_VALUE_MAX];
-    int len = property_get("ro.build.id", buildId, "");
+    auto buildId = base::GetProperty("ro.build.id", "");
     if (header->mBlobCacheVersion != blobCacheVersion ||
-            header->mDeviceVersion != blobCacheDeviceVersion ||
-            len != header->mBuildIdLength ||
-            strncmp(buildId, header->mBuildId, len)) {
+        header->mDeviceVersion != blobCacheDeviceVersion ||
+        buildId.size() != header->mBuildIdLength ||
+        strncmp(buildId.c_str(), header->mBuildId, buildId.size())) {
         // We treat version mismatches as an empty cache.
         return 0;
     }
@@ -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 cc42ac7..751f3be 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -17,11 +17,13 @@
 #include "FileBlobCache.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
-#include <log/log.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
+#include <log/log.h>
 
 // Cache file header
 static const char* cacheFileMagic = "EGL$";
@@ -68,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 d664e4d..1afc693 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <EGL/Loader.h>
+#include "EGL/Loader.h"
+
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <utils/Timers.h>
+#include <vndksupport/linker.h>
 
 #include <string>
 
-#include <dirent.h>
-#include <dlfcn.h>
-
-#include <android/dlext.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
-#include <vndksupport/linker.h>
-
+#include "EGL/eglext_angle.h"
 #include "egl_platform_entries.h"
 #include "egl_trace.h"
 #include "egldefs.h"
-#include <EGL/eglext_angle.h>
 
 namespace android {
 
@@ -159,13 +155,11 @@
         return true;
     }
 
-#ifndef __ANDROID_VNDK__
     // Return true if updated driver namespace is set.
     ns = android::GraphicsEnv::getInstance().getDriverNamespace();
     if (ns) {
         return true;
     }
-#endif
 
     return false;
 }
@@ -241,12 +235,12 @@
         // i.e.:
         //      libGLES_${prop}.so, or:
         //      libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
-        char prop[PROPERTY_VALUE_MAX + 1];
         for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
-            if (property_get(key, prop, nullptr) <= 0) {
+            auto prop = base::GetProperty(key, "");
+            if (prop.empty()) {
                 continue;
             }
-            hnd = attempt_to_load_system_driver(cnx, prop, true);
+            hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
             if (hnd) {
                 break;
             } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
@@ -276,7 +270,7 @@
         // will set cnx->useAngle appropriately.
         // Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
         // not just loading ANGLE as option.
-        init_angle_backend(hnd->dso[0], cnx);
+        init_angle_backend(hnd->dso[2], cnx);
     }
 
     LOG_ALWAYS_FATAL_IF(!hnd,
@@ -370,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.
              */
@@ -510,9 +504,9 @@
         .library_namespace = ns,
     };
     void* so = nullptr;
-    char prop[PROPERTY_VALUE_MAX + 1];
     for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
-        if (property_get(key, prop, nullptr) <= 0) {
+        auto prop = base::GetProperty(key, "");
+        if (prop.empty()) {
             continue;
         }
         std::string name = std::string("lib") + kind + "_" + prop + ".so";
@@ -557,12 +551,8 @@
 }
 
 void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
-    void* eglCreateDeviceANGLE = nullptr;
-
-    ALOGV("dso: %p", dso);
-    eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE");
-    ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE);
-    if (eglCreateDeviceANGLE) {
+    void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+    if (pANGLEGetDisplayPlatform) {
         ALOGV("ANGLE GLES library in use");
         cnx->useAngle = true;
     } else {
@@ -573,7 +563,7 @@
 
 Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
     ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
     android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
     if (!ns) {
         return nullptr;
@@ -603,9 +593,6 @@
         hnd->set(dso, GLESv2);
     }
     return hnd;
-#else
-    return nullptr;
-#endif
 }
 
 Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
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 d5d57d7..e5b9e14 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,63 +14,50 @@
  ** 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 <cutils/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 {
             LOG_ALWAYS_FATAL(error);
         }
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.egl.callstack", value, "0");
-        if (atoi(value)) {
+        if (base::GetBoolProperty("debug.egl.callstack", false)) {
             CallStack::log(LOG_TAG);
         }
     }
     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);
     }
@@ -78,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() {
@@ -154,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;
 
@@ -168,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) {
@@ -196,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;
@@ -224,21 +175,16 @@
     pthread_mutex_unlock(&sLogPrintMutex);
     if (printLog) {
         ALOGE("called unimplemented OpenGL ES API");
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.egl.callstack", value, "0");
-        if (atoi(value)) {
+        if (base::GetBoolProperty("debug.egl.callstack", false)) {
             CallStack::log(LOG_TAG);
         }
     }
 }
 
-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;
 }
 
@@ -274,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 00caff2..dc8e587 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,23 +16,26 @@
 
 #if defined(__ANDROID__)
 
-#include <cutils/properties.h>
-#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;
 
@@ -102,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"));
 
@@ -115,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 6af7cd2..a288c21 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,49 +14,41 @@
  ** limitations under the License.
  */
 
-#define __STDC_LIMIT_MACROS 1
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "egl_display.h"
 
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
 #include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
-#include <cutils/properties.h>
+#include "EGL/eglext_angle.h"
 #include "Loader.h"
 #include "egl_angle_platform.h"
 #include "egl_cache.h"
 #include "egl_object.h"
 #include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
 
 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) {
@@ -73,7 +65,7 @@
 }
 
 bool needsAndroidPEglMitigation() {
-    static const int32_t vndk_version = property_get_int32("ro.vndk.version", -1);
+    static const int32_t vndk_version = base::GetIntProperty("ro.vndk.version", -1);
     return vndk_version <= 28;
 }
 
@@ -82,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;
@@ -98,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) {
@@ -128,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,
@@ -151,10 +147,8 @@
         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
 
-        char prop[PROPERTY_VALUE_MAX];
-        property_get("debug.angle.validation", prop, "0");
         attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
-        attrs.push_back(atoi(prop));
+        attrs.push_back(base::GetBoolProperty("debug.angle.validation", false));
 
         attrs.push_back(EGL_NONE);
 
@@ -178,7 +172,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
@@ -206,7 +199,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");
                 }
@@ -214,17 +207,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++;
@@ -232,7 +231,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;
@@ -245,7 +244,7 @@
             if (minor != nullptr) *minor = cnx->minor;
             return EGL_TRUE;
         }
-        while(eglIsInitialized) {
+        while (eglIsInitialized) {
             refCond.wait(_l);
         }
     }
@@ -265,40 +264,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;
@@ -372,16 +362,8 @@
 
         egl_cache_t::get()->initialize(this);
 
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.egl.finish", value, "0");
-        if (atoi(value)) {
-            finishOnSwap = true;
-        }
-
-        property_get("debug.egl.traceGpuCompletion", value, "0");
-        if (atoi(value)) {
-            traceGpuCompletion = true;
-        }
+        finishOnSwap = base::GetBoolProperty("debug.egl.finish", false);
+        traceGpuCompletion = base::GetBoolProperty("debug.egl.traceGpuCompletion", false);
 
         // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
         // changing the behavior from the past where we always advertise
@@ -401,7 +383,6 @@
 }
 
 EGLBoolean egl_display_t::terminate() {
-
     { // scope for refLock
         std::unique_lock<std::mutex> _rl(refLock);
         if (refs == 0) {
@@ -434,7 +415,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;
@@ -467,8 +448,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) {
@@ -477,8 +457,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);
@@ -488,7 +467,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
@@ -499,10 +477,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)
@@ -514,14 +491,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();
             }
@@ -547,6 +522,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 44a1c0b..9752e38 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -18,9 +18,9 @@
 
 #include <EGL/egl.h>
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/dlext.h>
-#include <cutils/properties.h>
 #include <dlfcn.h>
 #include <graphicsenv/GraphicsEnv.h>
 #include <log/log.h>
@@ -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++;
@@ -157,9 +169,7 @@
 
     if (debug_layers.empty()) {
         // Only check system properties if Java settings are empty
-        char prop[PROPERTY_VALUE_MAX];
-        property_get("debug.gles.layers", prop, "");
-        debug_layers = prop;
+        debug_layers = base::GetProperty("debug.gles.layers", "");
     }
 
     return debug_layers;
@@ -382,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 c976c60..398efc0 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
 
 #include "egl_platform_entries.h"
 
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware_buffer.h>
 #include <ctype.h>
+#include <cutils/compiler.h>
 #include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
-#include <android/hardware_buffer.h>
-#include <android-base/strings.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
 #include <condition_variable>
 #include <deque>
 #include <mutex>
-#include <unordered_map>
 #include <string>
 #include <thread>
+#include <unordered_map>
 
 #include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
 #include "egl_display.h"
-#include "egl_object.h"
 #include "egl_layers.h"
+#include "egl_object.h"
 #include "egl_tls.h"
 #include "egl_trace.h"
 
@@ -80,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
 
 /*
@@ -154,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;
@@ -261,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;
         }
@@ -273,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();
+}
 
 // ----------------------------------------------------------------------------
 
@@ -313,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);
@@ -323,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();
@@ -341,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);
     }
 
@@ -357,99 +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) {
-            char value[PROPERTY_VALUE_MAX];
-            property_get("debug.egl.force_msaa", value, "false");
+    if (!cnx->dso) return EGL_FALSE;
 
-            if (!strcmp(value, "true")) {
-                size_t attribCount = 0;
-                EGLint attrib = attrib_list[0];
+    if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false))
+        return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size,
+                                        num_config);
 
-                // 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;
+    // Force 4x MSAA
+    size_t attribCount = 0;
+    EGLint attrib = attrib_list[0];
 
-                // 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++;
-                }
+    // 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 (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;
-                    }
-                }
-            }
+    // 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);
 }
 
 // ----------------------------------------------------------------------------
@@ -487,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.
@@ -512,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;
@@ -522,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) {
@@ -702,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;
@@ -771,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;
     }
@@ -792,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,
@@ -804,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) {
@@ -845,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);
     }
@@ -856,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);
     }
@@ -866,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);
@@ -908,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);
@@ -926,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);
     }
@@ -941,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);
@@ -970,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;
@@ -995,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();
@@ -1013,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()) {
@@ -1039,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);
@@ -1081,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) {
@@ -1105,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) {
@@ -1191,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;
 
@@ -1208,8 +1132,7 @@
     return nullptr;
 }
 
-__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) {
     if (FILTER_EXTENSIONS(procname)) {
         return nullptr;
     }
@@ -1256,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;
 
@@ -1271,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
@@ -1303,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];
     }
 
@@ -1313,7 +1233,6 @@
 
 class FrameCompletionThread {
 public:
-
     static void queueSync(EGLSyncKHR sync) {
         static FrameCompletionThread thread;
 
@@ -1330,7 +1249,6 @@
     }
 
 private:
-
     FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
         std::thread thread(&FrameCompletionThread::loop, this);
         thread.detach();
@@ -1385,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);
@@ -1409,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);
         }
     }
 
@@ -1448,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:
@@ -1496,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:
@@ -1516,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()) {
@@ -1556,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;
@@ -1612,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) {
@@ -1632,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;
@@ -1643,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();
@@ -1654,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();
@@ -1668,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);
 }
@@ -1686,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);
     }
@@ -1726,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;
@@ -1735,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 &&
@@ -1749,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;
 }
@@ -1795,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;
@@ -1832,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;
@@ -1871,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;
@@ -1900,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;
@@ -1913,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;
@@ -1945,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;
@@ -1990,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;
         }
@@ -2097,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;
 }
@@ -2180,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;
@@ -2217,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;
@@ -2231,42 +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) {
-    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
-    // this function cannot be implemented when this libEGL is built for
-    // vendors.
-#ifndef __ANDROID_VNDK__
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) {
     if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
-    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
+    return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
 }
 
 // ----------------------------------------------------------------------------
 // NVIDIA extensions
 // ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl() {
     EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
 
@@ -2277,8 +2125,7 @@
     return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
-EGLuint64NV eglGetSystemTimeNVImpl()
-{
+EGLuint64NV eglGetSystemTimeNVImpl() {
     EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
 
@@ -2292,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);
@@ -2349,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);
@@ -2387,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) {
@@ -2434,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);
@@ -2497,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:
@@ -2519,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) {
@@ -2554,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:
@@ -2563,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) {
@@ -2590,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) {
@@ -2603,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) {
@@ -2616,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) {
@@ -2629,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);
 }
 
@@ -2638,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 },
@@ -2726,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) {
@@ -2744,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 aaecb62..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 <android-base/properties.h>
+#include <log/log.h>
 #include <stdlib.h>
 
-#include <cutils/properties.h>
-#include <log/log.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,17 +101,13 @@
              "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));
-            char value[PROPERTY_VALUE_MAX];
-            property_get("debug.egl.callstack", value, "0");
-            if (atoi(value)) {
+            ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
+            if (base::GetBoolProperty("debug.egl.callstack", false)) {
                 CallStack::log(LOG_TAG);
             }
         }
@@ -113,7 +122,6 @@
         return true;
     }
     return false;
-
 }
 
 egl_tls_t* egl_tls_t::getTLS() {
@@ -163,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/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp
index 65f89b1..8d4323f 100644
--- a/opengl/tests/gl2_jni/Android.bp
+++ b/opengl/tests/gl2_jni/Android.bp
@@ -17,6 +17,7 @@
         "-Werror",
         "-Wno-error=unused-parameter",
     ],
+    header_libs: ["jni_headers"],
     srcs: ["jni/gl_code.cpp"],
     shared_libs: [
         "liblog",
diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp
index 5bec336..0cb129a 100644
--- a/opengl/tests/gl_jni/Android.bp
+++ b/opengl/tests/gl_jni/Android.bp
@@ -19,6 +19,7 @@
         "-Werror",
         "-Wno-error=unused-parameter",
     ],
+    header_libs: ["jni_headers"],
     srcs: ["jni/gl_code.cpp"],
     shared_libs: [
         "liblog",
diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp
index cf899ac..66afb6a 100644
--- a/opengl/tests/gl_perfapp/Android.bp
+++ b/opengl/tests/gl_perfapp/Android.bp
@@ -17,6 +17,7 @@
         "-Werror",
         "-Wno-error=unused-parameter",
     ],
+    header_libs: ["jni_headers"],
     srcs: ["jni/gl_code.cpp"],
     shared_libs: [
         "liblog",
diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp
index 2432566..1006d44 100644
--- a/opengl/tests/gldual/Android.bp
+++ b/opengl/tests/gldual/Android.bp
@@ -20,6 +20,7 @@
         "-Werror",
         "-Wno-error=unused-parameter",
     ],
+    header_libs: ["jni_headers"],
     srcs: ["jni/gl_code.cpp"],
     shared_libs: [
         "liblog",
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 41fcf1b..7fd9c3a 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -1,11 +1,23 @@
 #!/bin/bash
 set -u
 set -e
+
+if [ -z "$ANDROID_BUILD_TOP" ] ; then
+    echo "ANDROID_BUILD_TOP is not set, did you run lunch?"
+    exit 1
+fi
+
+# Avoid spewing files in any location other than the intended one.
+if [ ! -x "$PWD/gen" ] ; then
+    echo "Run this script from its parent directory".
+    exit 1
+fi
+
 rm -rf out generated
 
 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
@@ -92,7 +104,7 @@
 
 # Add UnsupportedAppUsage.java to known sources.
 mkdir -p out/android/compat/annotation
-cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
+cp ${ANDROID_BUILD_TOP}/tools/platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
 
 pushd out > /dev/null
 mkdir classes
@@ -153,23 +165,23 @@
     fi
 }
 
-compareGenerated ../../../../base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
-compareGenerated ../../../../base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
 
 for x in GL.java GL10.java GL10Ext.java GL11.java GL11Ext.java GL11ExtensionPack.java
 do
-    compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
+    compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
 done
 
 for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
 do
-    compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
-    compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
+    compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
+    compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C android_opengl_${x}.cpp
 done
 
 for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
 do
-    compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
+    compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
 done
 
 if [ $KEEP_GENERATED == "0" ] ; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 9932556..951ecff 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -21,8 +21,8 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.SurfaceTexture;
 import android.view.Surface;
-import android.view.SurfaceView;
 import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 
 /**
  * EGL 1.4
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/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
index afcc3eb..32c9d7d 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
+++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
@@ -19,6 +19,7 @@
 package com.google.android.gles_jni;
 
 import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.os.Build;
@@ -26,6 +27,7 @@
 import android.util.Log;
 
 import java.nio.Buffer;
+
 import javax.microedition.khronos.opengles.GL10;
 import javax.microedition.khronos.opengles.GL10Ext;
 import javax.microedition.khronos.opengles.GL11;
@@ -55,6 +57,7 @@
     private boolean have_OES_framebuffer_object;
     private boolean have_OES_texture_cube_map;
 
+    @UnsupportedAppUsage
     public GLImpl() {
     }
 
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 6eed24a..1bcaab4 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,6 +21,8 @@
         "libbinder",
         "libcutils",
         "libgfxstats",
+        "libgpumem",
+        "libgpumemtracer",
         "libgraphicsenv",
         "liblog",
         "libutils",
@@ -85,6 +87,10 @@
     name: "gpuservice",
     defaults: ["libgpuservice_binary"],
     init_rc: ["gpuservice.rc"],
+    required: [
+        "bpfloader",
+        "gpu_mem.o",
+    ],
     srcs: [":gpuservice_binary_sources"],
     shared_libs: [
         "libgpuservice",
diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk
new file mode 100644
index 0000000..482fc6d
--- /dev/null
+++ b/services/gpuservice/CleanSpec.mk
@@ -0,0 +1,52 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Remove gpu_mem.o
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp)
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 7bb0e4a..52d5d4f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,13 +24,16 @@
 #include <binder/Parcel.h>
 #include <binder/PermissionCache.h>
 #include <cutils/properties.h>
+#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;
@@ -45,7 +48,16 @@
 
 const char* const GpuService::SERVICE_NAME = "gpu";
 
-GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
+GpuService::GpuService()
+      : 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,
                              const std::string& driverVersionName, uint64_t driverVersionCode,
@@ -62,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();
 
@@ -90,24 +122,31 @@
     } else {
         bool dumpAll = true;
         bool dumpDriverInfo = false;
+        bool dumpMem = false;
         bool dumpStats = false;
         size_t numArgs = args.size();
 
         if (numArgs) {
-            dumpAll = false;
             for (size_t index = 0; index < numArgs; ++index) {
                 if (args[index] == String16("--gpustats")) {
                     dumpStats = true;
                 } else if (args[index] == String16("--gpudriverinfo")) {
                     dumpDriverInfo = true;
+                } else if (args[index] == String16("--gpumem")) {
+                    dumpMem = true;
                 }
             }
+            dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
         }
 
         if (dumpAll || dumpDriverInfo) {
             dumpGameDriverInfo(&result);
             result.append("\n");
         }
+        if (dumpAll || dumpMem) {
+            mGpuMem->dump(args, &result);
+            result.append("\n");
+        }
         if (dumpAll || dumpStats) {
             mGpuStats->dump(args, &result);
             result.append("\n");
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index b3e34d5..409084b 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,7 +28,9 @@
 
 namespace android {
 
+class GpuMem;
 class GpuStats;
+class GpuMemTracer;
 
 class GpuService : public BnGpuService, public PriorityDumper {
 public:
@@ -50,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
@@ -72,7 +76,11 @@
     /*
      * Attributes
      */
+    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/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+bpf {
+    name: "gpu_mem.o",
+    srcs: ["gpu_mem.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -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 <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+                   AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+    uint64_t ignore;
+    /* Actual fields start at offset 8 */
+    uint32_t gpu_id;
+    uint32_t pid;
+    uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+    uint64_t key = 0;
+    uint64_t cur_val = 0;
+    uint64_t* prev_val = NULL;
+
+    /* The upper 32 bits are for gpu_id while the lower is the pid */
+    key = ((uint64_t)args->gpu_id << 32) | args->pid;
+    cur_val = args->size;
+
+    if (!cur_val) {
+        bpf_gpu_mem_total_map_delete_elem(&key);
+        return 0;
+    }
+
+    prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+    if (prev_val) {
+        *prev_val = cur_val;
+    } else {
+        bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+    }
+    return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/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: "libgpumem",
+    srcs: [
+        "GpuMem.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbpf",
+        "libbpf_android",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libbase",
+        "libbpf_android",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..c78322e
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#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>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+    bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+    // Make sure bpf programs are loaded
+    bpf::waitForProgsLoaded();
+
+    int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+    if (fd < 0) {
+        ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+        return;
+    }
+
+    // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+    int count = 0;
+    while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+        if (++count > kGpuWaitTimeout) {
+            ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
+                  kGpuMemTotalTracepoint);
+            return;
+        }
+        // Retry until GPU driver loaded or timeout.
+        sleep(1);
+    }
+
+    // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+    auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+    if (!map.isValid()) {
+        ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+        return;
+    }
+    setGpuMemTotalMap(map);
+
+    mInitialized.store(true);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+    mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+    ATRACE_CALL();
+
+    if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
+        result->append("Failed to initialize GPU memory eBPF\n");
+        return;
+    }
+
+    auto res = mGpuMemTotalMap.getFirstKey();
+    if (!res.ok()) {
+        result->append("GPU memory total usage map is empty\n");
+        return;
+    }
+    uint64_t key = res.value();
+    // unordered_map<gpu_id, vector<pair<pid, size>>>
+    std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+    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();
+
+        dumpMap[gpu_id].emplace_back(pid, size);
+
+        res = mGpuMemTotalMap.getNextKey(key);
+        if (!res.ok()) break;
+        key = res.value();
+    }
+
+    for (auto& gpu : dumpMap) {
+        if (gpu.second.empty()) continue;
+        StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+        std::sort(gpu.second.begin(), gpu.second.end(),
+                  [](auto& l, auto& r) { return l.first < r.first; });
+
+        int i = 0;
+        if (gpu.second[0].first != 0) {
+            StringAppendF(result, "Global total: N/A\n");
+        } else {
+            StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+            i++;
+        }
+        for (; i < gpu.second.size(); i++) {
+            StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+                          gpu.second[i].second);
+        }
+    }
+}
+
+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
new file mode 100644
index 0000000..de691e2
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.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 <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <functional>
+
+namespace android {
+
+class GpuMem {
+public:
+    GpuMem() = default;
+    ~GpuMem();
+
+    // initialize eBPF program and map
+    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.
+    friend class TestableGpuMem;
+
+    // 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;
+
+    // gpu memory tracepoint event category
+    static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+    // gpu memory total tracepoint
+    static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+    // pinned gpu memory total bpf c program path in bpf sysfs
+    static constexpr char kGpuMemTotalProgPath[] =
+            "/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 538506d..db81f5d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -19,11 +19,16 @@
         address: true,
     },
     srcs: [
+        "GpuMemTest.cpp",
         "GpuStatsTest.cpp",
     ],
     shared_libs: [
+        "libbase",
+        "libbpf",
+        "libbpf_android",
         "libcutils",
         "libgfxstats",
+        "libgpumem",
         "libgraphicsenv",
         "liblog",
         "libstatslog",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..45e8367
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTest.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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <android-base/stringprintf.h>
+#include <bpf/BpfMap.h>
+#include <gmock/gmock.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+namespace {
+
+using base::StringPrintf;
+using testing::HasSubstr;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+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:
+    GpuMemTest() {
+        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());
+    }
+
+    ~GpuMemTest() {
+        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 {
+        SKIP_IF_BPF_NOT_SUPPORTED;
+        ASSERT_EQ(0, 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());
+    }
+
+    std::string dumpsys() {
+        std::string result;
+        Vector<String16> args;
+        mGpuMem->dump(args, &result);
+        return result;
+    }
+
+    std::unique_ptr<GpuMem> mGpuMem;
+    TestableGpuMem mTestableGpuMem;
+    bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+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(),
+              "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total");
+    EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map");
+}
+
+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);
+
+    EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL)));
+}
+
+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);
+
+    EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A"));
+}
+
+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);
+
+    EXPECT_THAT(dumpsys(),
+                HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+                                       (uint32_t)(TEST_PROC_KEY_1 >> 32))));
+    EXPECT_THAT(dumpsys(),
+                HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1,
+                                       TEST_PROC_VAL_1)));
+    EXPECT_THAT(dumpsys(),
+                HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+                                       (uint32_t)(TEST_PROC_KEY_2 >> 32))));
+    EXPECT_THAT(dumpsys(),
+                HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2,
+                                       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
new file mode 100644
index 0000000..6c8becb
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+
+namespace android {
+
+class TestableGpuMem {
+public:
+    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);
+    }
+
+    std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; }
+
+    std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; }
+
+    std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; }
+
+    std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; }
+
+private:
+    GpuMem *mGpuMem;
+};
+
+} // namespace android
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..8af9bcb 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -93,16 +93,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 +115,40 @@
     if (setInputWindowsListener) {
         setInputWindowsListener->onSetInputWindowsFinished();
     }
+    return binder::Status::ok();
 }
 
 // Used by tests only.
-void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
     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);
+
+    mDispatcher->registerInputChannel(channel.dup());
+    return binder::Status::ok();
 }
 
-void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
-    mDispatcher->unregisterInputChannel(channel);
+binder::Status InputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+    mDispatcher->unregisterInputChannel(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..35d2d58 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 registerInputChannel(const InputChannel& channel) override;
+    binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status setFocusedWindow(const FocusRequest&) override;
 
 private:
     sp<InputReaderInterface> mReader;
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..b645d69 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -45,49 +45,45 @@
     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 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 +94,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 +103,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 +129,17 @@
 protected:
     explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
           : mDispatcher(dispatcher) {
-        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+        std::unique_ptr<InputChannel> serverChannel, clientChannel;
+        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+        mServerChannel = std::move(serverChannel);
+        mClientChannel = std::move(clientChannel);
         mConsumer = std::make_unique<InputConsumer>(mClientChannel);
     }
 
     virtual ~FakeInputReceiver() {}
 
     sp<InputDispatcher> mDispatcher;
-    sp<InputChannel> mServerChannel, mClientChannel;
+    std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
     std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
 };
@@ -149,7 +149,7 @@
     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);
@@ -161,9 +161,8 @@
     virtual bool updateInfo() override {
         mInfo.token = mServerChannel->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}}});
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 49630ad..4328e03 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,35 +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_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},
@@ -99,6 +70,12 @@
     releaseInjectionState();
 }
 
+std::string EventEntry::getDescription() const {
+    std::string result;
+    appendDescription(result);
+    return result;
+}
+
 void EventEntry::release() {
     refCount -= 1;
     if (refCount == 0) {
@@ -140,10 +117,12 @@
 // --- 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() {}
 
@@ -182,7 +161,7 @@
     msg += StringPrintf("(deviceId=%d, 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,
+                        deviceId, source, displayId, KeyEvent::actionToString(action), flags,
                         keyCode, scanCode, metaState, repeatCount, policyFlags);
 }
 
@@ -206,7 +185,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),
@@ -244,7 +222,7 @@
                         "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(),
+                        deviceId, source, displayId, MotionEvent::actionToString(action),
                         actionButton, flags, metaState, buttonState,
                         motionClassificationToString(classification), edgeFlags, xPrecision,
                         yPrecision, xCursorPosition, yCursorPosition);
@@ -263,17 +241,13 @@
 
 volatile int32_t DispatchEntry::sNextSeqAtomic;
 
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
-                             float yOffset, float globalScaleFactor, float windowXScale,
-                             float windowYScale)
+DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+                             float globalScaleFactor)
       : seq(nextSeq()),
         eventEntry(eventEntry),
         targetFlags(targetFlags),
-        xOffset(xOffset),
-        yOffset(yOffset),
+        transform(transform),
         globalScaleFactor(globalScaleFactor),
-        windowXScale(windowXScale),
-        windowYScale(windowYScale),
         deliveryTime(0),
         resolvedAction(0),
         resolvedFlags(0) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ab481bd..d5b589e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -83,6 +83,8 @@
 
     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();
@@ -110,8 +112,10 @@
 struct FocusEntry : EventEntry {
     sp<IBinder> connectionToken;
     bool hasFocus;
+    std::string_view reason;
 
-    FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
+    FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+               std::string_view reason);
     virtual void appendDescription(std::string& msg) const;
 
 protected:
@@ -152,7 +156,6 @@
 };
 
 struct MotionEntry : EventEntry {
-    nsecs_t eventTime;
     int32_t deviceId;
     uint32_t source;
     int32_t displayId;
@@ -191,20 +194,21 @@
 
     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(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+                  float globalScaleFactor);
     ~DispatchEntry();
 
     inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -251,12 +255,12 @@
     sp<Connection> connection;
     nsecs_t eventTime;
     KeyEntry* keyEntry;
-    sp<InputApplicationHandle> inputApplicationHandle;
+    std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
     std::string reason;
     int32_t userActivityEventType;
     uint32_t seq;
     bool handled;
-    sp<InputChannel> inputChannel;
+    std::shared_ptr<InputChannel> inputChannel;
     sp<IBinder> oldToken;
     sp<IBinder> newToken;
 };
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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 1dbcf98..6460fe9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -45,27 +45,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 "    "
@@ -78,7 +79,8 @@
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
-constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+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 +91,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 +165,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)) {
@@ -243,6 +252,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;
@@ -262,12 +280,11 @@
 static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
                                                           EventEntry* eventEntry,
                                                           int32_t inputTargetFlags) {
-    if (inputTarget.useDefaultPointerInfo()) {
-        const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+    if (inputTarget.useDefaultPointerTransform()) {
+        const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, // increments ref
-                                               inputTargetFlags, pointerInfo.xOffset,
-                                               pointerInfo.yOffset, inputTarget.globalScaleFactor,
-                                               pointerInfo.windowXScale, pointerInfo.windowYScale);
+                                               inputTargetFlags, transform,
+                                               inputTarget.globalScaleFactor);
     }
 
     ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -277,28 +294,24 @@
 
     // 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 =
@@ -320,59 +333,35 @@
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
-                                            inputTargetFlags, firstPointerInfo.xOffset,
-                                            firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
-                                            firstPointerInfo.windowXScale,
-                                            firstPointerInfo.windowYScale);
+                                            inputTargetFlags, firstPointerTransform,
+                                            inputTarget.globalScaleFactor);
     combinedMotionEntry->release();
     return dispatchEntry;
 }
 
-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");
+static void addGestureMonitors(const std::vector<Monitor>& monitors,
+                               std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+                               float yOffset = 0) {
+    if (monitors.empty()) {
+        return;
     }
-    return key;
+    outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+    for (const Monitor& monitor : monitors) {
+        outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+    }
 }
 
-// --- 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;
-    }
-
-    if (hashLen != hash.size()) {
-        ALOGE("HMAC-SHA256 has unexpected length");
-        return INVALID_HMAC;
-    }
-
-    return hash;
 }
 
 // --- InputDispatcher ---
@@ -392,8 +381,7 @@
         // 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) {
+        mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
 
@@ -413,7 +401,7 @@
 
     while (!mConnectionsByFd.empty()) {
         sp<Connection> connection = mConnectionsByFd.begin()->second;
-        unregisterInputChannel(connection->inputChannel);
+        unregisterInputChannel(connection->inputChannel->getConnectionToken());
     }
 }
 
@@ -453,6 +441,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) {
@@ -466,6 +459,55 @@
     mLooper->pollOnce(timeoutMillis);
 }
 
+/**
+ * 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) {
+            onAnrLocked(mAwaitedFocusedApplication);
+            mAwaitedFocusedApplication.reset();
+            return LONG_LONG_MIN;
+        } else {
+            // Keep waiting
+            const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
+            ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+            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();
 
@@ -529,9 +571,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.
@@ -620,6 +659,66 @@
     }
 }
 
+/**
+ * Return true if the events preceding this incoming motion event should be dropped
+ * Return false otherwise (the default behaviour)
+ */
+bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
+    const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+            (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+
+    // Optimize case where the current application is unresponsive and the user
+    // decides to touch a window in a different application.
+    // If the application takes too long to catch up then we drop all events preceding
+    // the touch into the other window.
+    if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
+        int32_t displayId = motionEntry.displayId;
+        int32_t x = static_cast<int32_t>(
+                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+        int32_t y = static_cast<int32_t>(
+                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        sp<InputWindowHandle> touchedWindowHandle =
+                findTouchedWindowAtLocked(displayId, x, y, nullptr);
+        if (touchedWindowHandle != nullptr &&
+            touchedWindowHandle->getApplicationToken() !=
+                    mAwaitedFocusedApplication->getApplicationToken()) {
+            // User touched a different application than the one we are waiting on.
+            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 needWake = mInboundQueue.empty();
     mInboundQueue.push_back(entry);
@@ -649,36 +748,18 @@
         }
 
         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.
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-            if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
-                (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
-                mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
-                mInputTargetWaitApplicationToken != nullptr) {
-                int32_t displayId = motionEntry->displayId;
-                int32_t x =
-                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-                int32_t y =
-                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-                sp<InputWindowHandle> touchedWindowHandle =
-                        findTouchedWindowAtLocked(displayId, x, y);
-                if (touchedWindowHandle != nullptr &&
-                    touchedWindowHandle->getApplicationToken() !=
-                            mInputTargetWaitApplicationToken) {
-                    // User touched a different application than the one we are waiting on.
-                    // Flag the event, and start pruning the input queue.
-                    mNextUnblockedEvent = motionEntry;
-                    needWake = true;
-                }
+            if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
+                mNextUnblockedEvent = entry;
+                needWake = true;
             }
             break;
         }
-        case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::FOCUS: {
+            LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
+            break;
+        }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
             // nothing to do
             break;
         }
@@ -697,29 +778,33 @@
 }
 
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                 int32_t y, bool addOutsideTargets,
+                                                                 int32_t y, TouchState* touchState,
+                                                                 bool addOutsideTargets,
                                                                  bool addPortalWindows) {
+    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
+        LOG_ALWAYS_FATAL(
+                "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 &&
                             portalToDisplayId != displayId) {
                             if (addPortalWindows) {
                                 // For the monitoring channels of the display.
-                                mTempTouchState.addPortalWindow(windowHandle);
+                                touchState->addPortalWindow(windowHandle);
                             }
-                            return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
                                                              addOutsideTargets, addPortalWindows);
                         }
                         // Found window.
@@ -727,10 +812,10 @@
                     }
                 }
 
-                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(windowHandle,
-                                                      InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
-                                                      BitSet32(0));
+                if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+                    touchState->addOrUpdateWindow(windowHandle,
+                                                  InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+                                                  BitSet32(0));
                 }
             }
         }
@@ -739,7 +824,7 @@
 }
 
 std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
     std::vector<TouchedMonitor> touchedMonitors;
 
     std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
@@ -753,18 +838,6 @@
     return touchedMonitors;
 }
 
-void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
-                                         std::vector<TouchedMonitor>& outTouchedMonitors,
-                                         float xOffset, float yOffset) {
-    if (monitors.empty()) {
-        return;
-    }
-    outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
-    for (const Monitor& monitor : monitors) {
-        outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
-    }
-}
-
 void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
     const char* reason;
     switch (dropReason) {
@@ -888,7 +961,6 @@
 
 void InputDispatcher::releasePendingEventLocked() {
     if (mPendingEvent) {
-        resetAnrTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
         mPendingEvent = nullptr;
     }
@@ -979,14 +1051,30 @@
     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
+        mInboundQueue.push_front(mPendingEvent);
+        mPendingEvent = nullptr;
+    }
+
     FocusEntry* focusEntry =
-            new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
-    enqueueInboundEventLocked(focusEntry);
+            new 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::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+                         [](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);
 }
 
 void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
-    sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+    std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
     if (channel == nullptr) {
         return; // Window has gone away
     }
@@ -994,7 +1082,10 @@
     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});
 }
 
@@ -1053,10 +1144,10 @@
         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));
+            if (focusedWindowToken != nullptr) {
+                commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken);
             }
             commandEntry->keyEntry = entry;
             postCommandLocked(std::move(commandEntry));
@@ -1152,14 +1243,16 @@
     }
 
     setInjectionResult(entry, injectionResult);
+    if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+        ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
+        return true;
+    }
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-        if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
-            CancelationOptions::Mode mode(isPointerEvent
-                                                  ? CancelationOptions::CANCEL_POINTER_EVENTS
-                                                  : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
-            CancelationOptions options(mode, "input event injection failed");
-            synthesizeCancelationEventsForMonitorsLocked(options);
-        }
+        CancelationOptions::Mode mode(isPointerEvent
+                                              ? CancelationOptions::CANCEL_POINTER_EVENTS
+                                              : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+        CancelationOptions options(mode, "input event injection failed");
+        synthesizeCancelationEventsForMonitorsLocked(options);
         return true;
     }
 
@@ -1167,9 +1260,10 @@
     addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
     if (isPointerEvent) {
-        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
-        if (stateIndex >= 0) {
-            const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+        std::unordered_map<int32_t, TouchState>::iterator it =
+                mTouchStatesByDisplay.find(entry->displayId);
+        if (it != mTouchStatesByDisplay.end()) {
+            const TouchState& state = it->second;
             if (!state.portalWindows.empty()) {
                 // The event has gone through these portal windows, so we add monitoring targets of
                 // the corresponding displays as well.
@@ -1229,6 +1323,8 @@
     ALOGD("dispatchEventToCurrentInputTargets");
 #endif
 
+    updateInteractionTokensLocked(*eventEntry, inputTargets);
+
     ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
     pokeUserActivityLocked(*eventEntry);
@@ -1248,118 +1344,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) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
-                      getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
-            }
-            nsecs_t 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;
-            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 (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-        TouchState& state = mTouchStatesByDisplay.editValueAt(d);
-        state.removeWindowByToken(token);
-    }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
-        nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
-    if (newTimeout > 0) {
-        // Extend the timeout.
-        mInputTargetWaitTimeoutTime = now() + newTimeout;
-    } 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);
-            }
-        }
-    }
-}
-
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
-    if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-        return currentTime - mInputTargetWaitStartTime;
-    }
-    return 0;
-}
-
-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();
 }
 
 /**
@@ -1390,71 +1397,150 @@
     return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
 }
 
+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;
+}
+
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
                                                         const EventEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime) {
-    int32_t injectionResult;
     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) {
-            injectionResult =
-                    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.");
-            goto Unresponsive;
-        }
-
-        ALOGI("Dropping event because there is no focused window or focused application in display "
-              "%" PRId32 ".",
-              displayId);
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-        goto 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 INPUT_EVENT_INJECTION_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;
+            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 INPUT_EVENT_INJECTION_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 INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            // Still waiting for the focused window
+            return INPUT_EVENT_INJECTION_PENDING;
+        }
+    }
+
+    // we have a valid, non-null focused window
+    resetNoFocusedWindowTimeoutLocked();
+
     // Check permissions.
     if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
-        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-        goto Failed;
+        return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
     }
 
-    // Check whether the window is ready for more input.
-    reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
-    if (!reason.empty()) {
-        injectionResult =
-                handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
-                                            focusedWindowHandle, nextWakeupTime, reason.c_str());
-        goto Unresponsive;
+    if (focusedWindowHandle->getInfo()->paused) {
+        ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+        return INPUT_EVENT_INJECTION_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 INPUT_EVENT_INJECTION_PENDING;
+        }
     }
 
     // Success!  Output targets.
-    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
     addWindowTargetLocked(focusedWindowHandle,
                           InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
                           BitSet32(0), inputTargets);
 
     // Done.
-Failed:
-Unresponsive:
-    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
-    updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-    if (DEBUG_FOCUS) {
-        ALOGD("findFocusedWindow finished: injectionResult=%d, "
-              "timeSpentWaitingForApplication=%0.1fms",
-              injectionResult, timeSpentWaitingForApplication / 1000000.0);
-    }
-    return injectionResult;
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+/**
+ * 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;
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
@@ -1478,22 +1564,25 @@
     // Update the touch state as needed based on the properties of the touch event.
     int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
     InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-    sp<InputWindowHandle> newHoverWindowHandle;
+    sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+    sp<InputWindowHandle> newTouchedWindowHandle;
 
-    // Copy current touch state into mTempTouchState.
-    // This state is always reset at the end of this function, so if we don't find state
-    // for the specified display then our initial state will be empty.
+    // Copy current touch state into tempTouchState.
+    // This state will be used to update mTouchStatesByDisplay at the end of this function.
+    // If no state for the specified display exists, then our initial state will be empty.
     const TouchState* oldState = nullptr;
-    ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-    if (oldStateIndex >= 0) {
-        oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
-        mTempTouchState.copyFrom(*oldState);
+    TouchState tempTouchState;
+    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
+            mTouchStatesByDisplay.find(displayId);
+    if (oldStateIt != mTouchStatesByDisplay.end()) {
+        oldState = &(oldStateIt->second);
+        tempTouchState.copyFrom(*oldState);
     }
 
-    bool isSplit = mTempTouchState.split;
-    bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
-            (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
-             mTempTouchState.displayId != displayId);
+    bool isSplit = tempTouchState.split;
+    bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+            (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
+             tempTouchState.displayId != displayId);
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
@@ -1503,30 +1592,26 @@
     bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Dropping event because a pointer for a different device is already down "
-                      "in display %" PRId32,
-                      displayId);
-            }
+        if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+            ALOGI("Dropping event because a pointer for a different device is already down "
+                  "in display %" PRId32,
+                  displayId);
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             switchedDevice = false;
             wrongDevice = true;
             goto Failed;
         }
-        mTempTouchState.reset();
-        mTempTouchState.down = down;
-        mTempTouchState.deviceId = entry.deviceId;
-        mTempTouchState.source = entry.source;
-        mTempTouchState.displayId = displayId;
+        tempTouchState.reset();
+        tempTouchState.down = down;
+        tempTouchState.deviceId = entry.deviceId;
+        tempTouchState.source = entry.source;
+        tempTouchState.displayId = displayId;
         isSplit = false;
     } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
-        if (DEBUG_FOCUS) {
-            ALOGI("Dropping move event because a pointer for a different device is already active "
-                  "in display %" PRId32,
-                  displayId);
-        }
+        ALOGI("Dropping move event because a pointer for a different device is already active "
+              "in display %" PRId32,
+              displayId);
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         switchedDevice = false;
@@ -1549,12 +1634,12 @@
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        sp<InputWindowHandle> newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
-                                          true /*addPortalWindows*/);
+        newTouchedWindowHandle =
+                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
 
         std::vector<TouchedMonitor> newGestureMonitors = isDown
-                ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+                ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
                 : std::vector<TouchedMonitor>{};
 
         // Figure out whether splitting will be allowed for this window.
@@ -1571,9 +1656,28 @@
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
             // Try to assign the pointer to the first foreground window we find, if there is one.
-            newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
+            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;
+            }
+        }
+
+        // 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 ".",
@@ -1595,10 +1699,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.
@@ -1607,15 +1711,15 @@
                 uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                 pointerIds.markBit(pointerId);
             }
-            mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+            tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
         }
 
-        mTempTouchState.addGestureMonitors(newGestureMonitors);
+        tempTouchState.addGestureMonitors(newGestureMonitors);
     } else {
         /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (!mTempTouchState.down) {
+        if (!tempTouchState.down) {
             if (DEBUG_FOCUS) {
                 ALOGD("Dropping event because the pointer is not down or we previously "
                       "dropped the pointer down event in display %" PRId32,
@@ -1627,14 +1731,13 @@
 
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
-            mTempTouchState.isSlippery()) {
+            tempTouchState.isSlippery()) {
             int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
             int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
-                    mTempTouchState.getFirstForegroundWindowHandle();
-            sp<InputWindowHandle> newTouchedWindowHandle =
-                    findTouchedWindowAtLocked(displayId, x, y);
+                    tempTouchState.getFirstForegroundWindowHandle();
+            newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                 oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
                 if (DEBUG_FOCUS) {
@@ -1643,9 +1746,9 @@
                           newTouchedWindowHandle->getName().c_str(), displayId);
                 }
                 // Make a slippery exit from the old window.
-                mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
-                                                  InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
-                                                  BitSet32(0));
+                tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
+                                                 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+                                                 BitSet32(0));
 
                 // Make a slippery entrance into the new window.
                 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -1665,32 +1768,37 @@
                 if (isSplit) {
                     pointerIds.markBit(entry.pointerProperties[0].id);
                 }
-                mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+                tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
             }
         }
     }
 
     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());
 #endif
-            mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
-                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
-                                              BitSet32(0));
+            tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
+                                             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());
 #endif
-            mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
-                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
-                                              BitSet32(0));
+            tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
+                                             InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+                                             BitSet32(0));
         }
     }
 
@@ -1698,7 +1806,7 @@
     // is at least one touched foreground window.
     {
         bool haveForegroundWindow = false;
-        for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+        for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
             if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
                 haveForegroundWindow = true;
                 if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
@@ -1708,13 +1816,11 @@
                 }
             }
         }
-        bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+        bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
         if (!haveForegroundWindow && !hasGestureMonitor) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Dropping event because there is no touched foreground window in display "
-                      "%" PRId32 " or gesture monitor to receive it.",
-                      displayId);
-            }
+            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;
             goto Failed;
         }
@@ -1727,38 +1833,22 @@
     // set the policy flag that we will not reveal coordinate information to this window.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
-                mTempTouchState.getFirstForegroundWindowHandle();
+                tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle) {
             const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
-            for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+            for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
                 if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                     sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
                     if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
-                        mTempTouchState.addOrUpdateWindow(inputWindowHandle,
-                                                          InputTarget::FLAG_ZERO_COORDS,
-                                                          BitSet32(0));
+                        tempTouchState.addOrUpdateWindow(inputWindowHandle,
+                                                         InputTarget::FLAG_ZERO_COORDS,
+                                                         BitSet32(0));
                     }
                 }
             }
         }
     }
 
-    // Ensure all touched foreground windows are ready for new input.
-    for (const TouchedWindow& touchedWindow : mTempTouchState.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()) {
-                injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
-                                                              touchedWindow.windowHandle,
-                                                              nextWakeupTime, reason.c_str());
-                goto Unresponsive;
-            }
-        }
-    }
-
     // 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.
@@ -1767,15 +1857,15 @@
     // to View.onGenericMotionEvent to enable wallpapers to handle these events.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
-                mTempTouchState.getFirstForegroundWindowHandle();
+                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) {
-                    mTempTouchState
+                    windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
+                    tempTouchState
                             .addOrUpdateWindow(windowHandle,
                                                InputTarget::FLAG_WINDOW_IS_OBSCURED |
                                                        InputTarget::
@@ -1790,19 +1880,19 @@
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
 
-    for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
         addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                               touchedWindow.pointerIds, inputTargets);
     }
 
-    for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
         addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                                   touchedMonitor.yOffset, inputTargets);
     }
 
     // Drop the outside or hover touch windows since we will not care about them
     // in the next iteration.
-    mTempTouchState.filterNonAsIsTouchWindows();
+    tempTouchState.filterNonAsIsTouchWindows();
 
 Failed:
     // Check injection permission once and for all.
@@ -1814,98 +1904,81 @@
         }
     }
 
+    if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
+        return injectionResult;
+    }
+
     // Update final pieces of touch state if the injector had permission.
-    if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (!wrongDevice) {
-            if (switchedDevice) {
+    if (!wrongDevice) {
+        if (switchedDevice) {
+            if (DEBUG_FOCUS) {
+                ALOGD("Conflicting pointer actions: Switched to a different device.");
+            }
+            *outConflictingPointerActions = true;
+        }
+
+        if (isHoverAction) {
+            // Started hovering, therefore no longer down.
+            if (oldState && oldState->down) {
                 if (DEBUG_FOCUS) {
-                    ALOGD("Conflicting pointer actions: Switched to a different device.");
+                    ALOGD("Conflicting pointer actions: Hover received while pointer was "
+                          "down.");
                 }
                 *outConflictingPointerActions = true;
             }
+            tempTouchState.reset();
+            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+                maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                tempTouchState.deviceId = entry.deviceId;
+                tempTouchState.source = entry.source;
+                tempTouchState.displayId = displayId;
+            }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+                   maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            // All pointers up or canceled.
+            tempTouchState.reset();
+        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+            // First pointer went down.
+            if (oldState && oldState->down) {
+                if (DEBUG_FOCUS) {
+                    ALOGD("Conflicting pointer actions: Down received while already down.");
+                }
+                *outConflictingPointerActions = true;
+            }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+            // One pointer went up.
+            if (isSplit) {
+                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
 
-            if (isHoverAction) {
-                // Started hovering, therefore no longer down.
-                if (oldState && oldState->down) {
-                    if (DEBUG_FOCUS) {
-                        ALOGD("Conflicting pointer actions: Hover received while pointer was "
-                              "down.");
-                    }
-                    *outConflictingPointerActions = true;
-                }
-                mTempTouchState.reset();
-                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-                    maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                    mTempTouchState.deviceId = entry.deviceId;
-                    mTempTouchState.source = entry.source;
-                    mTempTouchState.displayId = displayId;
-                }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
-                       maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-                // All pointers up or canceled.
-                mTempTouchState.reset();
-            } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-                // First pointer went down.
-                if (oldState && oldState->down) {
-                    if (DEBUG_FOCUS) {
-                        ALOGD("Conflicting pointer actions: Down received while already down.");
-                    }
-                    *outConflictingPointerActions = true;
-                }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
-                // One pointer went up.
-                if (isSplit) {
-                    int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-                    uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
-                    for (size_t i = 0; i < mTempTouchState.windows.size();) {
-                        TouchedWindow& touchedWindow = mTempTouchState.windows[i];
-                        if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
-                            touchedWindow.pointerIds.clearBit(pointerId);
-                            if (touchedWindow.pointerIds.isEmpty()) {
-                                mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
-                                continue;
-                            }
+                for (size_t i = 0; i < tempTouchState.windows.size();) {
+                    TouchedWindow& touchedWindow = tempTouchState.windows[i];
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+                        touchedWindow.pointerIds.clearBit(pointerId);
+                        if (touchedWindow.pointerIds.isEmpty()) {
+                            tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+                            continue;
                         }
-                        i += 1;
                     }
+                    i += 1;
                 }
             }
+        }
 
-            // Save changes unless the action was scroll in which case the temporary touch
-            // state was only valid for this one action.
-            if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
-                if (mTempTouchState.displayId >= 0) {
-                    if (oldStateIndex >= 0) {
-                        mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
-                    } else {
-                        mTouchStatesByDisplay.add(displayId, mTempTouchState);
-                    }
-                } else if (oldStateIndex >= 0) {
-                    mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
-                }
+        // Save changes unless the action was scroll in which case the temporary touch
+        // state was only valid for this one action.
+        if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
+            if (tempTouchState.displayId >= 0) {
+                mTouchStatesByDisplay[displayId] = tempTouchState;
+            } else {
+                mTouchStatesByDisplay.erase(displayId);
             }
+        }
 
-            // Update hover state.
-            mLastHoverWindowHandle = newHoverWindowHandle;
-        }
-    } else {
-        if (DEBUG_FOCUS) {
-            ALOGD("Not updating touch focus because injection was denied.");
-        }
+        // Update hover state.
+        mLastHoverWindowHandle = newHoverWindowHandle;
     }
 
-Unresponsive:
-    // Reset temporary touch state to ensure we release unnecessary references to input channels.
-    mTempTouchState.reset();
-
-    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
-    updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-    if (DEBUG_FOCUS) {
-        ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
-              "timeSpentWaitingForApplication=%0.1fms",
-              injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
-    }
     return injectionResult;
 }
 
@@ -1923,7 +1996,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;
@@ -1938,8 +2012,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,
@@ -1962,7 +2035,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);
 }
 
@@ -2001,16 +2076,11 @@
     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 (info->ownerPid == otherInfo->ownerPid) {
+        // If ownerPid is the same we don't generate occlusion events as there
+        // is no in-process security boundary.
         return false;
-    } else if (otherInfo->isTrustedOverlay()) {
+    } else if (otherInfo->trustedOverlay) {
         return false;
     } else if (otherInfo->displayId != info->displayId) {
         return false;
@@ -2021,13 +2091,13 @@
 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.
         }
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-          if (canBeObscuredBy(windowHandle, otherHandle) &&
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
@@ -2037,13 +2107,12 @@
 
 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) {
             break; // All future windows are below us. Exit early.
         }
-
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->overlaps(windowInfo)) {
@@ -2053,106 +2122,17 @@
     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) {
-            std::string label(applicationHandle->getName());
-            label += " - ";
-            label += windowHandle->getName();
-            return label;
+            return applicationHandle->getName() + " - " + windowHandle->getName();
         } else {
             return applicationHandle->getName();
         }
     } else if (windowHandle != nullptr) {
-        return windowHandle->getName();
+        return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName();
     } else {
         return "<unknown application or window>";
     }
@@ -2164,11 +2144,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
@@ -2432,31 +2411,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));
 }
 
@@ -2474,6 +2514,9 @@
     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;
@@ -2504,15 +2547,9 @@
                 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) &&
                     !(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];
@@ -2547,8 +2584,9 @@
                                                      dispatchEntry->resolvedFlags,
                                                      motionEntry->edgeFlags, motionEntry->metaState,
                                                      motionEntry->buttonState,
-                                                     motionEntry->classification, xScale, yScale,
-                                                     xOffset, yOffset, motionEntry->xPrecision,
+                                                     motionEntry->classification,
+                                                     dispatchEntry->transform,
+                                                     motionEntry->xPrecision,
                                                      motionEntry->yPrecision,
                                                      motionEntry->xCursorPosition,
                                                      motionEntry->yCursorPosition,
@@ -2593,7 +2631,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, "
@@ -2610,10 +2647,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;
@@ -2623,7 +2680,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;
 }
@@ -2633,7 +2690,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,
@@ -2644,8 +2701,6 @@
           connection->getInputChannelName().c_str(), seq, toString(handled));
 #endif
 
-    connection->inputPublisherBlocked = false;
-
     if (connection->status == Connection::STATUS_BROKEN ||
         connection->status == Connection::STATUS_ZOMBIE) {
         return;
@@ -2760,7 +2815,7 @@
         }
 
         // Unregister the channel.
-        d->unregisterInputChannelLocked(connection->inputChannel, notify);
+        d->unregisterInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
         return 0; // remove the callback
     }             // release lock
 }
@@ -2790,7 +2845,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;
@@ -2825,8 +2880,7 @@
             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;
@@ -2891,8 +2945,7 @@
             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;
@@ -3056,7 +3109,7 @@
         if (newKeyCode != AKEYCODE_UNKNOWN) {
             std::scoped_lock _l(mLock);
             struct KeyReplacement replacement = {keyCode, deviceId};
-            mReplacedKeys.add(replacement, newKeyCode);
+            mReplacedKeys[replacement] = newKeyCode;
             keyCode = newKeyCode;
             metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
@@ -3066,10 +3119,10 @@
         // even if the modifier was released between the down and the up events.
         std::scoped_lock _l(mLock);
         struct KeyReplacement replacement = {keyCode, deviceId};
-        ssize_t index = mReplacedKeys.indexOfKey(replacement);
-        if (index >= 0) {
-            keyCode = mReplacedKeys.valueAt(index);
-            mReplacedKeys.removeItemsAt(index);
+        auto replacementIt = mReplacedKeys.find(replacement);
+        if (replacementIt != mReplacedKeys.end()) {
+            keyCode = replacementIt->second;
+            mReplacedKeys.erase(replacementIt);
             metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
     }
@@ -3203,13 +3256,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)) {
@@ -3282,7 +3335,6 @@
           "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;
@@ -3485,7 +3537,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: {
@@ -3493,7 +3545,7 @@
             VerifiedMotionEvent verifiedMotionEvent =
                     verifiedMotionEventFromMotionEvent(motionEvent);
             result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
-            calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+            calculatedHmac = sign(verifiedMotionEvent);
             break;
         }
         default: {
@@ -3565,15 +3617,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;
@@ -3583,11 +3641,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,
@@ -3601,7 +3679,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;
@@ -3636,10 +3738,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());
@@ -3653,7 +3754,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);
@@ -3695,64 +3797,53 @@
         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);
-        }
-        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);
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    if (focusedToken) {
+        FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
+        if (result != FocusResult::OK) {
+            onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
         }
     }
 
-    ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-    if (stateIndex >= 0) {
-        TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+    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);
+        }
+    }
+
+    std::unordered_map<int32_t, TouchState>::iterator stateIt =
+            mTouchStatesByDisplay.find(displayId);
+    if (stateIt != mTouchStatesByDisplay.end()) {
+        TouchState& state = stateIt->second;
         for (size_t i = 0; i < state.windows.size();) {
             TouchedWindow& touchedWindow = state.windows[i];
             if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
@@ -3760,7 +3851,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,
@@ -3789,28 +3880,36 @@
 }
 
 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>");
     }
-    { // acquire lock
+    if (inputApplicationHandle != nullptr &&
+        inputApplicationHandle->getApplicationToken() != nullptr) {
+        // 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;
+
+        // If oldFocusedApplicationHandle already exists
+        if (oldFocusedApplicationHandle != nullptr) {
+            // If a new focused application handle is different from the old one and
+            // old focus application info is awaited focused application info.
+            if (*oldFocusedApplicationHandle != *inputApplicationHandle &&
+                mAwaitedFocusedApplication != nullptr &&
+                *oldFocusedApplicationHandle == *mAwaitedFocusedApplication) {
+                resetNoFocusedWindowTimeoutLocked();
             }
-        } else if (oldFocusedApplicationHandle != nullptr) {
-            resetAnrTimeoutsLocked();
-            oldFocusedApplicationHandle.clear();
+            // Erase the old application from container first
             mFocusedApplicationHandlesByDisplay.erase(displayId);
+            // Should already get freed after removed from container but just double check.
+            oldFocusedApplicationHandle.reset();
         }
+
+        // Set the new application handle.
+        mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3834,11 +3933,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,
@@ -3849,21 +3948,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());
                 }
             }
         }
@@ -3888,7 +3982,7 @@
 
         if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
             if (mDispatchFrozen && !frozen) {
-                resetAnrTimeoutsLocked();
+                resetNoFocusedWindowTimeoutLocked();
             }
 
             if (mDispatchEnabled && !enabled) {
@@ -3967,8 +4061,8 @@
         }
 
         bool found = false;
-        for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-            TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+            TouchState& state = pair.second;
             for (size_t i = 0; i < state.windows.size(); i++) {
                 const TouchedWindow& touchedWindow = state.windows[i];
                 if (touchedWindow.windowHandle == fromWindowHandle) {
@@ -4028,8 +4122,9 @@
     resetKeyRepeatLocked();
     releasePendingEventLocked();
     drainInboundQueueLocked();
-    resetAnrTimeoutsLocked();
+    resetNoFocusedWindowTimeoutLocked();
 
+    mAnrTracker.clear();
     mTouchStatesByDisplay.clear();
     mLastHoverWindowHandle.clear();
     mReplacedKeys.clear();
@@ -4047,6 +4142,28 @@
     }
 }
 
+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;
+}
+
 void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
     dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
     dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4057,34 +4174,23 @@
         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=%0.3fms\n",
-                                 displayId, applicationHandle->getName().c_str(),
-                                 applicationHandle->getDispatchingTimeout(
-                                         DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
-                                         1000000.0);
+                                         ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
+                                 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();
 
-    if (!mTouchStatesByDisplay.isEmpty()) {
+    if (!mTouchStatesByDisplay.empty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
-        for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
-            const TouchState& state = mTouchStatesByDisplay.valueAt(i);
+        for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+            const TouchState& state = pair.second;
             dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
                                  state.displayId, toString(state.down), toString(state.split),
                                  state.deviceId, state.source);
@@ -4124,28 +4230,30 @@
                     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, "
+                                                 "portalToDisplayId=%d, paused=%s, focusable=%s, "
+                                                 "hasWallpaper=%s, visible=%s, "
+                                                 "flags=%s, type=0x%08x, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
-                                                 "windowScale=(%f,%f), touchableRegion=",
+                                                 "touchableRegion=",
                                          i, windowInfo->name.c_str(), 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);
+                                         windowInfo->flags.string().c_str(),
+                                         static_cast<int32_t>(windowInfo->type),
+                                         windowInfo->frameLeft, windowInfo->frameTop,
+                                         windowInfo->frameRight, windowInfo->frameBottom,
+                                         windowInfo->globalScaleFactor);
                     dumpRegion(dump, windowInfo->touchableRegion);
-                    dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
-                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                    dump += StringPrintf(", inputFeatures=%s",
+                                         windowInfo->inputFeatures.string().c_str());
+                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+                                         "ms\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
-                                         windowInfo->dispatchingTimeout / 1000000.0);
+                                         millis(windowInfo->dispatchingTimeout));
+                    windowInfo->transform.dump(dump, "transform", INDENT4);
                 }
             } else {
                 dump += INDENT2 "Windows: <none>\n";
@@ -4178,7 +4286,7 @@
         for (EventEntry* entry : mRecentQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "RecentQueue: <empty>\n";
@@ -4189,8 +4297,8 @@
         dump += INDENT "PendingEvent:\n";
         dump += INDENT2;
         mPendingEvent->appendDescription(dump);
-        dump += StringPrintf(", age=%0.1fms\n",
-                             (currentTime - mPendingEvent->eventTime) * 0.000001f);
+        dump += StringPrintf(", age=%" PRId64 "ms\n",
+                             ns2ms(currentTime - mPendingEvent->eventTime));
     } else {
         dump += INDENT "PendingEvent: <none>\n";
     }
@@ -4201,18 +4309,18 @@
         for (EventEntry* entry : mInboundQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "InboundQueue: <empty>\n";
     }
 
-    if (!mReplacedKeys.isEmpty()) {
+    if (!mReplacedKeys.empty()) {
         dump += INDENT "ReplacedKeys:\n";
-        for (size_t i = 0; i < mReplacedKeys.size(); i++) {
-            const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
-            int32_t newKeyCode = mReplacedKeys.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+        for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
+            const KeyReplacement& replacement = pair.first;
+            int32_t newKeyCode = pair.second;
+            dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
                                  replacement.keyCode, replacement.deviceId, newKeyCode);
         }
     } else {
@@ -4224,11 +4332,10 @@
         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",
@@ -4236,9 +4343,10 @@
                 for (DispatchEntry* entry : connection->outboundQueue) {
                     dump.append(INDENT4);
                     entry->eventEntry->appendDescription(dump);
-                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
+                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
+                                         "ms\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime));
                 }
             } else {
                 dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -4251,10 +4359,10 @@
                     dump += INDENT4;
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
-                                         "age=%0.1fms, wait=%0.1fms\n",
+                                         "age=%" PRId64 "ms, wait=%" PRId64 "ms seq=%" PRIu32 "\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f,
-                                         (currentTime - entry->deliveryTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime),
+                                         ns2ms(currentTime - entry->deliveryTime), entry->seq);
                 }
             } else {
                 dump += INDENT3 "WaitQueue: <empty>\n";
@@ -4265,29 +4373,29 @@
     }
 
     if (isAppSwitchPendingLocked()) {
-        dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
-                             (mAppSwitchDueTime - now()) / 1000000.0);
+        dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
+                             ns2ms(mAppSwitchDueTime - now()));
     } else {
         dump += INDENT "AppSwitch: not pending\n";
     }
 
     dump += INDENT "Configuration:\n";
-    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
-    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
-                         mConfig.keyRepeatTimeout * 0.000001f);
+    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
+    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
+                         ns2ms(mConfig.keyRepeatTimeout));
 }
 
 void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
     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) {
+status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
 #if DEBUG_REGISTRATION
     ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
 #endif
@@ -4315,7 +4423,7 @@
     return OK;
 }
 
-status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
                                                int32_t displayId, bool isGestureMonitor) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -4347,15 +4455,11 @@
     return OK;
 }
 
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
-#endif
-
+status_t InputDispatcher::unregisterInputChannel(const sp<IBinder>& connectionToken) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+        status_t status = unregisterInputChannelLocked(connectionToken, false /*notify*/);
         if (status) {
             return status;
         }
@@ -4367,24 +4471,22 @@
     return OK;
 }
 
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::unregisterInputChannelLocked(const sp<IBinder>& connectionToken,
                                                        bool notify) {
-    sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+    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;
     }
 
-    [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
-    ALOG_ASSERT(removed);
-    mInputChannelsByToken.erase(inputChannel->getConnectionToken());
+    removeConnectionLocked(connection);
+    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);
@@ -4393,19 +4495,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;
             }
@@ -4429,13 +4531,14 @@
         }
         int32_t displayId = foundDisplayId.value();
 
-        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-        if (stateIndex < 0) {
+        std::unordered_map<int32_t, TouchState>::iterator stateIt =
+                mTouchStatesByDisplay.find(displayId);
+        if (stateIt == mTouchStatesByDisplay.end()) {
             ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
             return BAD_VALUE;
         }
 
-        TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+        TouchState& state = stateIt->second;
         std::optional<int32_t> foundDeviceId;
         for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
             if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
@@ -4455,7 +4558,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);
             }
@@ -4479,7 +4583,7 @@
     return std::nullopt;
 }
 
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
     if (inputConnectionToken == nullptr) {
         return nullptr;
     }
@@ -4494,6 +4598,11 @@
     return nullptr;
 }
 
+void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+    removeByValue(mConnectionsByFd, connection);
+}
+
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
                                                     bool handled) {
@@ -4517,10 +4626,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;
@@ -4528,17 +4635,69 @@
     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 sp<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());
 
+    updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+                             reason);
+
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = nullptr;
+    commandEntry->inputChannel = connection->inputChannel;
+    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->inputChannel = nullptr;
+    commandEntry->reason = std::move(reason);
+    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;
@@ -4548,21 +4707,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) {
@@ -4598,18 +4745,55 @@
             commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
     mLock.unlock();
 
-    nsecs_t newTimeout =
+    const std::chrono::nanoseconds timeoutExtension =
             mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, 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::extendAnrTimeoutsLocked(
+        const std::shared_ptr<InputApplicationHandle>& application,
+        const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
+    sp<Connection> connection = getConnectionLocked(connectionToken);
+    if (connection == nullptr) {
+        if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
+            // Maybe ANR happened because there's no focused window?
+            mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
+            mAwaitedFocusedApplication = application;
+        } else {
+            // It's also 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);
 
     mLock.unlock();
@@ -4643,6 +4827,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;
@@ -4655,15 +4853,12 @@
         return;
     }
     DispatchEntry* dispatchEntry = *dispatchEntryIt;
-
-    nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
     if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-        std::string msg =
-                StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                             connection->getWindowName().c_str(), eventDuration * 0.000001f);
-        dispatchEntry->eventEntry->appendDescription(msg);
-        ALOGI("%s", msg.c_str());
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
     }
+    reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
 
     bool restartEvent;
     if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
@@ -4679,13 +4874,18 @@
     }
 
     // Dequeue the event and start the next cycle.
-    // Note that because the lock might have been released, it is possible that the
+    // Because the lock might have been released, it is possible that the
     // contents of the wait queue to have been drained, so we need to double-check
     // a few things.
     dispatchEntryIt = connection->findWaitQueueEntry(seq);
     if (dispatchEntryIt != connection->waitQueue.end()) {
         dispatchEntry = *dispatchEntryIt;
         connection->waitQueue.erase(dispatchEntryIt);
+        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);
@@ -4898,9 +5098,8 @@
     return event;
 }
 
-void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
-                                               int32_t injectionResult,
-                                               nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+                                               const Connection& connection, bool handled) {
     // TODO Write some statistics about how long we spend waiting.
 }
 
@@ -4991,4 +5190,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 670d0e1..f3b3dda 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.
  *
@@ -113,7 +106,8 @@
             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;
@@ -122,12 +116,16 @@
     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 status_t registerInputChannel(
+            const std::shared_ptr<InputChannel>& inputChannel) override;
+    virtual void setFocusedWindow(const FocusRequest&) override;
+    virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+                                          int32_t displayId, bool isGestureMonitor) override;
+    virtual status_t unregisterInputChannel(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 +136,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;
@@ -173,7 +179,8 @@
     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);
@@ -191,26 +198,30 @@
     EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
 
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+                                                    TouchState* touchState,
                                                     bool addOutsideTargets = false,
                                                     bool addPortalWindows = false) REQUIRES(mLock);
 
     // All registered connections mapped by channel file descriptor.
     std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
 
+    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+            REQUIRES(mLock);
+
+    void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+
     struct IBinderHash {
         std::size_t operator()(const sp<IBinder>& b) const {
             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);
 
-    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) 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);
 
@@ -251,12 +262,14 @@
         bool operator==(const KeyReplacement& rhs) const {
             return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
         }
-        bool operator<(const KeyReplacement& rhs) const {
-            return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+    };
+    struct KeyReplacementHash {
+        size_t operator()(const KeyReplacement& key) const {
+            return std::hash<int32_t>()(key.keyCode) ^ (std::hash<int32_t>()(key.deviceId) << 1);
         }
     };
     // Maps the key code replaced, device id tuple to the key code it was replaced with
-    KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+    std::unordered_map<KeyReplacement, int32_t, KeyReplacementHash> mReplacedKeys GUARDED_BY(mLock);
     // Process certain Meta + Key combinations
     void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
                                  int32_t& metaState);
@@ -266,6 +279,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);
@@ -285,13 +301,24 @@
             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.
@@ -300,16 +327,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);
 
-    KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
-    TouchState mTempTouchState 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);
@@ -317,6 +345,11 @@
     // 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)
             REQUIRES(mLock);
@@ -332,36 +365,61 @@
     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,
-    };
+    /**
+     * 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);
 
-    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);
+    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);
+
+    /**
+     * 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,
@@ -372,11 +430,10 @@
                                            nsecs_t* nextWakeupTime,
                                            bool* outConflictingPointerActions) REQUIRES(mLock);
     std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
-            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
             REQUIRES(mLock);
-    void addGestureMonitors(const std::vector<Monitor>& monitors,
-                            std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
-                            float yOffset = 0);
+    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)
@@ -392,13 +449,9 @@
     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 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
@@ -433,8 +486,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)
@@ -453,13 +506,14 @@
     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);
 
     // 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 unregisterInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
             REQUIRES(mLock);
 
     // Interesting events that we might like to log or tell the framework about.
@@ -467,11 +521,18 @@
                                        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 sp<Connection>& connection) REQUIRES(mLock);
+    void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) 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)
@@ -497,8 +558,8 @@
     LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
 
     void reportTouchEventForStatistics(const MotionEntry& entry);
-    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
-                                  int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+    void reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+                                  const Connection& connection, bool handled);
     void traceInboundQueueLengthLocked() REQUIRES(mLock);
     void traceOutboundQueueLength(const sp<Connection>& connection);
     void traceWaitQueueLength(const sp<Connection>& connection);
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..c3d50ea 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,15 +18,15 @@
 #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
 
 #include <InputListener.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/FocusRequest.h>
+#include <android/os/ISetInputWindowsListener.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.
  */
@@ -113,7 +113,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.
      *
@@ -149,11 +150,16 @@
      */
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
 
+    /**
+     * Sets focus on the specified window.
+     */
+    virtual void setFocusedWindow(const FocusRequest&) = 0;
+
     /* Registers input channels 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 status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
 
     /* Registers input channels to be used to monitor input events.
      *
@@ -163,14 +169,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 status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+                                          int32_t displayId, bool gestureMonitor) = 0;
 
     /* Unregister 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 unregisterInputChannel(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..d04d797 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,8 +47,9 @@
 
     /* 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;
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 cbe0190..9e797e4 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -21,7 +21,9 @@
         "InputHost.cpp",
     ],
 
+    header_libs: ["jni_headers"],
     shared_libs: [
+        "libbase",
         "libbinder",
         "libcrypto",
         "libcutils",
@@ -42,6 +44,7 @@
         //-fvisibility=hidden
     ],
 
+    export_header_lib_headers: ["jni_headers"],
     export_include_dirs: ["."],
 }
 
@@ -57,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..cabc782 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -29,8 +29,8 @@
 
 #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 "    "
@@ -217,8 +217,10 @@
     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());
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 973b4f9..577309a 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,13 @@
     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 registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
+    binder::Status unregisterInputChannel(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/InputListener.h b/services/inputflinger/include/InputListener.h
index f8d0150..8317b05 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -22,7 +22,6 @@
 #include <input/Input.h>
 #include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
-#include <utils/Vector.h>
 
 namespace android {
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index f8d5351..573524e 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 ---
@@ -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 264d287..67d3cc2 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 {
+        PropertyMap* propertyMap;
+        status_t status = PropertyMap::load(String8(configurationFile.c_str()), &propertyMap);
+        if (status) {
+            ALOGE("Error loading input device configuration file for device '%s'.  "
+                  "Using default configuration.",
+                  identifier.name.c_str());
+        } else {
+            configuration = std::unique_ptr<PropertyMap>(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(KEY_OK),
-                                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..b224476 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"
@@ -107,7 +108,7 @@
         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 +132,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 +142,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,24 +178,24 @@
     }
 
     // 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));
     }
 
@@ -209,7 +210,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 +225,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 +239,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 +256,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 +425,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 +481,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..feaacb3 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),
@@ -208,7 +209,7 @@
     mDevices.emplace(eventHubId, device);
     bumpGenerationLocked();
 
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
         notifyExternalStylusPresenceChanged();
     }
 }
@@ -237,7 +238,7 @@
 
     device->removeEventHubDevice(eventHubId);
 
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+    if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
         notifyExternalStylusPresenceChanged();
     }
 
@@ -353,6 +354,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 +373,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 +403,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 +415,7 @@
 }
 
 void InputReader::updatePointerDisplayLocked() {
-    sp<PointerControllerInterface> controller = mPointerController.promote();
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
     if (controller == nullptr) {
         return;
     }
@@ -424,9 +438,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);
     }
 }
 
@@ -558,12 +572,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);
     }
 }
 
@@ -709,6 +723,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 +749,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..6b28069 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(); }
 
@@ -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..2d6ccf5 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;
+    void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) 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;
@@ -155,13 +156,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();
 
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 7be4a58..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;
@@ -233,7 +235,7 @@
 }
 
 bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
-    return scanCode < BTN_MOUSE || scanCode >= KEY_OK ||
+    return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
             (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
             (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
 }
@@ -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..e867c6f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+// clang-format off
 #include "../Macros.h"
+// clang-format on
 
 #include "TouchInputMapper.h"
 
@@ -102,6 +104,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 +141,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 +165,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,7 +187,7 @@
 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);
@@ -218,7 +226,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,
@@ -284,12 +292,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 +321,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);
@@ -323,15 +333,15 @@
 
 const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
     switch (deviceMode) {
-        case DEVICE_MODE_DISABLED:
+        case DeviceMode::DISABLED:
             return "disabled";
-        case DEVICE_MODE_DIRECT:
+        case DeviceMode::DIRECT:
             return "direct";
-        case DEVICE_MODE_UNSCALED:
+        case DeviceMode::UNSCALED:
             return "unscaled";
-        case DEVICE_MODE_NAVIGATION:
+        case DeviceMode::NAVIGATION:
             return "navigation";
-        case DEVICE_MODE_POINTER:
+        case DeviceMode::POINTER:
             return "pointer";
     }
     return "unknown";
@@ -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"),
@@ -488,10 +499,10 @@
     dump += INDENT3 "Parameters:\n";
 
     switch (mParameters.gestureMode) {
-        case Parameters::GESTURE_MODE_SINGLE_TOUCH:
+        case Parameters::GestureMode::SINGLE_TOUCH:
             dump += INDENT4 "GestureMode: single-touch\n";
             break;
-        case Parameters::GESTURE_MODE_MULTI_TOUCH:
+        case Parameters::GestureMode::MULTI_TOUCH:
             dump += INDENT4 "GestureMode: multi-touch\n";
             break;
         default:
@@ -499,16 +510,16 @@
     }
 
     switch (mParameters.deviceType) {
-        case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+        case Parameters::DeviceType::TOUCH_SCREEN:
             dump += INDENT4 "DeviceType: touchScreen\n";
             break;
-        case Parameters::DEVICE_TYPE_TOUCH_PAD:
+        case Parameters::DeviceType::TOUCH_PAD:
             dump += INDENT4 "DeviceType: touchPad\n";
             break;
-        case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+        case Parameters::DeviceType::TOUCH_NAVIGATION:
             dump += INDENT4 "DeviceType: touchNavigation\n";
             break;
-        case Parameters::DEVICE_TYPE_POINTER:
+        case Parameters::DeviceType::POINTER:
             dump += INDENT4 "DeviceType: pointer\n";
             break;
         default:
@@ -558,14 +569,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 +594,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 +621,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 +656,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 +667,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 +679,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 +770,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 +809,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 +858,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 +912,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 +939,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 +1002,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 +1123,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 +1146,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 +1164,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 +1180,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 +1195,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 +1212,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 +1257,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 +1291,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 +1310,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 +1325,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 +1340,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 +1381,7 @@
     mCurrentCookedState.clear();
     mLastRawState.clear();
     mLastCookedState.clear();
-    mPointerUsage = POINTER_USAGE_NONE;
+    mPointerUsage = PointerUsage::NONE;
     mSentHoverEnter = false;
     mHavePointerIds = false;
     mCurrentMotionAborted = false;
@@ -1383,7 +1394,7 @@
     resetExternalStylus();
 
     if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
 
@@ -1442,17 +1453,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 +1529,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 +1558,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 +1588,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 +1639,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 +1666,7 @@
 }
 
 bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
-    if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+    if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
         return false;
     }
 
@@ -1697,11 +1709,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 +1782,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 +1903,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 +2037,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 +2054,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 +2099,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 +2132,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 +2153,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 +2180,7 @@
         // Distance
         float distance;
         switch (mCalibration.distanceCalibration) {
-            case Calibration::DISTANCE_CALIBRATION_SCALED:
+            case Calibration::DistanceCalibration::SCALED:
                 distance = in.distance * mDistanceScale;
                 break;
             default:
@@ -2174,7 +2190,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 +2267,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 +2277,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 +2308,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 +2353,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 +2406,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 +2494,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 +2564,7 @@
 
     // Remove any current spots.
     if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
 }
@@ -2553,7 +2580,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 +2593,7 @@
                 *outFinishPreviousGesture = true;
 
                 mPointerGesture.activeGestureId = -1;
-                mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+                mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
                 mPointerGesture.currentGestureIdBits.clear();
 
                 mPointerVelocityControl.reset();
@@ -2598,9 +2625,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 +2660,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 +2689,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 +2718,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 +2772,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 +2785,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 +2809,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 +2852,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 +2862,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 +2882,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 +2906,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 +2915,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 +2961,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 +2981,7 @@
         }
 
         if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
-            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+            mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
             mPointerGesture.activeGestureId = 0;
             mPointerGesture.referenceIdBits.clear();
             mPointerVelocityControl.reset();
@@ -3005,7 +3033,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 +3055,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 +3074,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 +3105,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 +3117,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 +3132,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 +3161,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 +3183,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 +3196,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) {
@@ -3396,12 +3424,12 @@
     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();
 
@@ -3560,7 +3588,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 +3600,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 +3845,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 +3939,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..6465cc9 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -27,14 +27,25 @@
         "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,
 }
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..b5c5c9e
--- /dev/null
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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);
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index eeff757..6e08e1b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,16 +17,19 @@
 #include "../dispatcher/InputDispatcher.h"
 
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
 #include <input/Input.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
 #include <cinttypes>
+#include <thread>
 #include <unordered_set>
 #include <vector>
 
 using android::base::StringPrintf;
+using namespace android::flag_operators;
 
 namespace android::inputdispatcher {
 
@@ -83,9 +86,13 @@
                                         args.displayId);
     }
 
-    void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+    void assertFilterInputEventWasNotCalled() {
+        std::scoped_lock lock(mLock);
+        ASSERT_EQ(nullptr, mFilteredEvent);
+    }
 
     void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mConfigurationChangedTime)
                 << "Timed out waiting for configuration changed call";
         ASSERT_EQ(*mConfigurationChangedTime, when);
@@ -93,6 +100,7 @@
     }
 
     void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mLastNotifySwitch);
         // We do not check id because it is not exposed to the policy
         EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
@@ -103,33 +111,103 @@
     }
 
     void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+        std::scoped_lock lock(mLock);
         ASSERT_EQ(touchedToken, mOnPointerDownToken);
         mOnPointerDownToken.clear();
     }
 
     void assertOnPointerDownWasNotCalled() {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mOnPointerDownToken == nullptr)
                 << "Expected onPointerDownOutsideFocus to not have been called";
     }
 
+    // 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 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
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+
+        // If there is an ANR, Dispatcher won't be idle because there are still events
+        // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+        // before checking if ANR was called.
+        // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
+        // it some time to act. 100ms seems reasonable.
+        mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+            return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+        });
+        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+        if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+            ADD_FAILURE() << "Did not receive ANR callback";
+        }
+        // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+        // the dispatcher started counting before this function was called
+        if (std::chrono::abs(timeout - waited) > 100ms) {
+            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) {
         mConfig.keyRepeatTimeout = timeout;
         mConfig.keyRepeatDelay = delay;
     }
 
+    void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
 private:
-    std::unique_ptr<InputEvent> mFilteredEvent;
-    std::optional<nsecs_t> mConfigurationChangedTime;
-    sp<IBinder> mOnPointerDownToken;
-    std::optional<NotifySwitchArgs> mLastNotifySwitch;
+    std::mutex mLock;
+    std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
+    std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
+    sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
+    std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
+
+    // ANR handling
+    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 {
+        std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
-                              const std::string&) override {
-        return 0;
+    std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application,
+                                       const sp<IBinder>& windowToken,
+                                       const std::string&) override {
+        std::scoped_lock lock(mLock);
+        mAnrApplications.push(application);
+        mAnrWindowTokens.push(windowToken);
+        mNotifyAnr.notify_all();
+        return mAnrTimeout;
     }
 
     virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -141,6 +219,7 @@
     }
 
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+        std::scoped_lock lock(mLock);
         switch (inputEvent->getType()) {
             case AINPUT_EVENT_TYPE_KEY: {
                 const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
@@ -173,6 +252,7 @@
 
     virtual 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.
          */
@@ -186,11 +266,13 @@
     }
 
     virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+        std::scoped_lock lock(mLock);
         mOnPointerDownToken = newToken;
     }
 
     void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
                                          int32_t displayId) {
+        std::scoped_lock lock(mLock);
         ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
         ASSERT_EQ(mFilteredEvent->getType(), type);
 
@@ -212,70 +294,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 {
@@ -296,6 +314,32 @@
         mFakePolicy.clear();
         mDispatcher.clear();
     }
+
+    /**
+     * Used for debugging when writing the test
+     */
+    void dumpDispatcherState() {
+        std::string dump;
+        mDispatcher->dump(dump);
+        std::stringstream ss(dump);
+        std::string to;
+
+        while (std::getline(ss, to, '\n')) {
+            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);
+    }
 };
 
 
@@ -338,10 +382,11 @@
     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,
@@ -353,10 +398,10 @@
     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);
+                     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -365,10 +410,10 @@
     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);
+                     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -378,10 +423,10 @@
     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);
+                     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -390,10 +435,10 @@
     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);
+                     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -402,9 +447,8 @@
     // 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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -413,9 +457,8 @@
 
     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -426,9 +469,8 @@
     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -438,9 +480,8 @@
     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -452,9 +493,8 @@
     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,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -485,27 +525,50 @@
 
 // --- InputDispatcherTest SetInputWindowTest ---
 static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
-static constexpr std::chrono::duration DISPATCHING_TIMEOUT = 5s;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
-    FakeApplicationHandle() {}
+    FakeApplicationHandle() {
+        mInfo.name = "Fake Application";
+        mInfo.token = new BBinder();
+        mInfo.dispatchingTimeoutMillis =
+                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
+    }
     virtual ~FakeApplicationHandle() {}
 
     virtual bool updateInfo() override {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         return true;
     }
+
+    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(const std::shared_ptr<InputChannel>& clientChannel,
+                               const std::string name)
           : mName(name) {
         mConsumer = std::make_unique<InputConsumer>(clientChannel);
     }
 
     InputEvent* consume() {
+        InputEvent* event;
+        std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+        if (!consumeSeq) {
+            return nullptr;
+        }
+        finishEvent(*consumeSeq);
+        return event;
+    }
+
+    /**
+     * Receive an event without acknowledging it.
+     * Return the sequence number that could later be used to send finished signal.
+     */
+    std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         uint32_t consumeSeq;
         InputEvent* event;
 
@@ -522,23 +585,29 @@
 
         if (status == WOULD_BLOCK) {
             // Just means there's no event available.
-            return nullptr;
+            return std::nullopt;
         }
 
         if (status != OK) {
             ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
-            return nullptr;
+            return std::nullopt;
         }
         if (event == nullptr) {
             ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
-            return nullptr;
+            return std::nullopt;
         }
+        if (outEvent != nullptr) {
+            *outEvent = event;
+        }
+        return consumeSeq;
+    }
 
-        status = mConsumer->sendFinishedSignal(consumeSeq, true);
-        if (status != OK) {
-            ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
-        }
-        return event;
+    /**
+     * To be used together with "receiveEvent" to complete the consumption of an event.
+     */
+    void finishEvent(uint32_t consumeSeq) {
+        const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+        ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
@@ -548,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());
@@ -593,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(); }
@@ -612,70 +696,84 @@
     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;
+        if (token == std::nullopt) {
+            std::unique_ptr<InputChannel> serverChannel, clientChannel;
             InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-            mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
-            dispatcher->registerInputChannel(serverChannel);
+            mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
             token = serverChannel->getConnectionToken();
+            dispatcher->registerInputChannel(std::move(serverChannel));
         }
 
         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;
+    }
+
+    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,
@@ -727,6 +825,19 @@
                                      expectedFlags);
     }
 
+    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(outEvent);
+    }
+
+    void finishEvent(uint32_t sequenceNum) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+        mInputReceiver->finishEvent(sequenceNum);
+    }
+
     InputEvent* consume() {
         if (mInputReceiver == nullptr) {
             return nullptr;
@@ -735,8 +846,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();
     }
 
@@ -752,68 +867,184 @@
 
 std::atomic<int32_t> FakeWindowHandle::sId{1};
 
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
-        int32_t displayId = ADISPLAY_ID_NONE) {
+static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+                         int32_t displayId = ADISPLAY_ID_NONE,
+                         int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+                         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // Define a valid key down event.
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
-                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
-                     AMETA_NONE,
-                     /* repeatCount */ 0, currentTime, currentTime);
+                     INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+                     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 injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action,
-                                 int32_t source, int32_t displayId, int32_t x, int32_t y,
-                                 int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                 int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) {
-    MotionEvent event;
-    PointerProperties pointerProperties[1];
-    PointerCoords pointerCoords[1];
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+                             int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
+}
 
-    pointerProperties[0].clear();
-    pointerProperties[0].id = 0;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
+                           int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
 
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+class PointerBuilder {
+public:
+    PointerBuilder(int32_t id, int32_t toolType) {
+        mProperties.clear();
+        mProperties.id = id;
+        mProperties.toolType = toolType;
+        mCoords.clear();
+    }
 
-    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, xCursorPosition, yCursorPosition,
-                     currentTime, currentTime,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    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 int32_t injectMotionEvent(
+        const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT) {
+    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+                                        injectionTimeout,
+                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static int32_t 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},
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_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}) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x,
-                             location.y);
+    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}) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x,
-                             location.y);
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
 }
 
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
@@ -864,7 +1095,7 @@
 }
 
 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);
 
@@ -886,11 +1117,11 @@
  * 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,
@@ -909,11 +1140,11 @@
  * 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}}});
@@ -928,7 +1159,7 @@
 
 // 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",
@@ -944,91 +1175,209 @@
     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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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);
 
@@ -1038,18 +1387,20 @@
     // left window. This event should be dispatched to the left window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
               injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
-                                ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
+                                ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
     windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowRight->assertNoEvents();
 }
 
 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);
@@ -1067,7 +1418,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);
 
@@ -1090,7 +1441,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,
@@ -1125,7 +1476,7 @@
 }
 
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     PointF touchPoint = {10, 10};
 
@@ -1181,21 +1532,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}}});
@@ -1245,12 +1596,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);
 
@@ -1262,7 +1614,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);
 
@@ -1277,7 +1629,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);
 
@@ -1301,10 +1653,10 @@
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
                         int32_t displayId, bool isGestureMonitor = false) {
-        sp<InputChannel> serverChannel, clientChannel;
+        std::unique_ptr<InputChannel> serverChannel, clientChannel;
         InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-        mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
-        dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
+        dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
     }
 
     sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1314,6 +1666,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);
@@ -1332,7 +1688,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}}});
@@ -1348,14 +1704,16 @@
 }
 
 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,
@@ -1368,7 +1726,7 @@
 }
 
 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}}});
@@ -1392,8 +1750,23 @@
     monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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);
 
@@ -1424,52 +1797,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);
@@ -1499,7 +1878,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);
 
@@ -1535,12 +1914,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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_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 {
@@ -1554,12 +2135,12 @@
     }
 
     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);
     }
 
@@ -1641,17 +2222,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.
@@ -1659,24 +2241,25 @@
         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;
 };
 
@@ -1778,6 +2361,23 @@
     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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->assertNoEvents();
+    secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
 class InputFilterTest : public InputDispatcherTest {
 protected:
     static constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -1855,25 +2455,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);
     }
 
@@ -1949,19 +2551,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}}});
@@ -1973,9 +2576,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,
@@ -2005,133 +2607,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) {
@@ -2140,36 +2732,913 @@
     // 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 = 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(30ms);
+        mWindow->setFocusable(true);
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        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);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+        mWindow.clear();
+    }
+
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mWindow;
+    static constexpr PointF WINDOW_LOCATION = {20, 20};
+
+    void tapOnWindow() {
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   WINDOW_LOCATION));
+        ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send an event to the app and have the app not respond right away.
+// 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,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+
+    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));
+    std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(sequenceNum);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    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(INPUT_EVENT_INJECTION_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 int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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 int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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 int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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, INPUT_EVENT_INJECTION_SYNC_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, INPUT_EVENT_INJECTION_SYNC_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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.
+
+    int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   location));
+        ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_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.
+
+    int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_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();
+}
+
+// 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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    mMirror->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    // single window is removed but the window token remains focused
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+    // Both windows are removed
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+    mWindow->consumeFocusEvent(false);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_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..3aef1e4
--- /dev/null
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -0,0 +1,461 @@
+/*
+ * 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(){};
+    void checkFdFlags(const android::base::unique_fd& fd);
+
+    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 registerInputChannel(const InputChannel& channel) override;
+    binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status setFocusedWindow(const FocusRequest&) override;
+
+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;
+
+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 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();
+}
+
+void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
+    const int result = fcntl(fd, F_GETFL);
+    EXPECT_NE(result, -1);
+    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+    AutoMutex _l(mLock);
+    // check Fd flags
+    checkFdFlags(channel.getFd());
+
+    mInputChannels.push_back(channel.dup());
+
+    return binder::Status::ok();
+}
+
+binder::Status TestInputManager::unregisterInputChannel(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 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() {}
+
+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 registerInputChannel
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+    InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+    mService->registerInputChannel(*serverChannel);
+
+    std::vector<::android::InputChannel> channels;
+    mQuery->getInputChannels(&channels);
+    ASSERT_EQ(channels.size(), 1UL);
+    EXPECT_EQ(channels[0], *serverChannel);
+
+    mService->unregisterInputChannel(serverChannel->getConnectionToken());
+    mQuery->getInputChannels(&channels);
+    EXPECT_EQ(channels.size(), 0UL);
+}
+
+/**
+ *  Test InputFlinger service interface registerInputChannel with invalid cases
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
+    std::unique_ptr<InputChannel> serverChannel, clientChannel;
+    InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+
+    std::vector<::android::InputChannel> channels;
+    mQuery->getInputChannels(&channels);
+    EXPECT_EQ(channels.size(), 0UL);
+
+    mService->registerInputChannel(InputChannel());
+    mService->unregisterInputChannel(clientChannel->getConnectionToken());
+
+    mService->registerInputChannel(*serverChannel);
+    mService->registerInputChannel(*clientChannel);
+    mQuery->getInputChannels(&channels);
+    EXPECT_EQ(channels.size(), 2UL);
+
+    mService->unregisterInputChannel(clientChannel->getConnectionToken());
+    mService->unregisterInputChannel(serverChannel->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 109edfe..f56735a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -27,15 +27,17 @@
 #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>
+
 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 +46,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 +86,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 +102,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 +127,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 +135,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 +144,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 +162,7 @@
         mSpotsByDisplay[displayId] = newSpots;
     }
 
-    virtual void clearSpots() {
-    }
+    void clearSpots() override {}
 
     std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
 };
@@ -176,14 +175,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 +255,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 +288,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 +315,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 +360,7 @@
 
     struct Device {
         InputDeviceIdentifier identifier;
-        uint32_t classes;
+        Flags<InputDeviceClass> classes;
         PropertyMap configuration;
         KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
         KeyedVector<int, bool> relativeAxes;
@@ -384,9 +384,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 +404,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 +580,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 +613,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 +621,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 +660,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 +678,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 +688,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 +699,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 +710,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 +721,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 +735,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 +758,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 +767,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 +786,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 +796,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 +850,7 @@
             mResetWasCalled(false),
             mProcessWasCalled(false) {}
 
-    virtual ~FakeInputMapper() { }
+    virtual ~FakeInputMapper() {}
 
     void setKeyboardType(int32_t keyboardType) {
         mKeyboardType = keyboardType;
@@ -1043,11 +919,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 +929,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 +942,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 +1003,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 +1021,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 +1030,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 +1089,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 +1105,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 +1127,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 +1141,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 +1172,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 +1180,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 +1197,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
@@ -1314,7 +1236,7 @@
  * 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 +1280,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 +1289,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,22 +1319,21 @@
     }
 
     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
+    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);
@@ -1435,12 +1356,12 @@
 
 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 +1393,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 +1426,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 +1459,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 +1492,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 +1534,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 +1544,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 +1565,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 +1598,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 +1613,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 +1629,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 +1656,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 +1736,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 +1751,7 @@
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
     }
 
-    virtual void TearDown() override {
+    void TearDown() override {
         ASSERT_EQ(mReader->stop(), OK);
         mTestListener.clear();
         mFakePolicy.clear();
@@ -1848,21 +1838,39 @@
     ASSERT_LE(prevTimestamp, keyArgs.eventTime);
 }
 
+/**
+ * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
+ * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
+ * are passed to the listener.
+ */
+static_assert(BTN_GEAR_DOWN == BTN_WHEEL);
+TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) {
+    std::unique_ptr<UinputSteamController> controller = createUinputDevice<UinputSteamController>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    NotifyKeyArgs keyArgs;
+
+    controller->pressAndReleaseKey(BTN_GEAR_DOWN);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+    ASSERT_EQ(BTN_GEAR_DOWN, keyArgs.scanCode);
+
+    controller->pressAndReleaseKey(BTN_GEAR_UP);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+    ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode);
+}
+
 // --- 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());
@@ -1925,9 +1933,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
@@ -1942,11 +1950,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);
@@ -1955,26 +1965,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 ---
@@ -1985,33 +2000,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();
     }
@@ -2022,14 +2036,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) {
@@ -2198,8 +2212,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());
@@ -2225,33 +2238,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();
     }
@@ -2262,16 +2269,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;
     }
 
@@ -2287,8 +2311,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();
@@ -2296,6 +2319,7 @@
         event.code = code;
         event.value = value;
         mapper.process(&event);
+        mReader->loopOnce();
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -2326,9 +2350,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);
     }
@@ -2339,7 +2363,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 ---
@@ -2399,8 +2424,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,
@@ -2436,10 +2461,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);
@@ -2534,13 +2565,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);
@@ -2548,7 +2583,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);
@@ -2567,7 +2602,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) {
@@ -2705,7 +2740,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);
@@ -2715,7 +2750,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);
@@ -2772,6 +2807,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));
@@ -2827,6 +2865,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);
@@ -2836,15 +2911,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);
@@ -2876,9 +2949,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);
@@ -2907,13 +2980,78 @@
                                                 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);
+
+    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, mapper.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) {
@@ -2999,12 +3137,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);
     }
 
@@ -3013,7 +3151,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);
     }
@@ -3103,7 +3241,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;
 
@@ -3660,7 +3798,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) {
@@ -3688,7 +3826,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);
@@ -3727,14 +3865,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);
@@ -3748,7 +3886,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) {
@@ -3758,8 +3896,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);
 
@@ -3776,7 +3913,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);
 }
 
@@ -3886,8 +4023,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) {
@@ -3896,9 +4033,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() {
@@ -4150,7 +4287,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;
 
@@ -4200,7 +4337,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;
 
@@ -4321,7 +4458,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;
 
@@ -4396,7 +4533,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;
 
@@ -4492,7 +4629,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;
 
@@ -5387,7 +5524,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;
 
@@ -5663,7 +5800,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;
 
@@ -5838,7 +5975,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;
 
@@ -6751,7 +6888,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);
@@ -6784,14 +6921,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);
@@ -6817,15 +6955,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,
@@ -6844,7 +6980,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);
 
@@ -6857,11 +6994,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;
@@ -6968,7 +7105,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);
 
@@ -7035,10 +7172,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);
@@ -7048,7 +7185,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));
@@ -7062,19 +7199,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));
@@ -7083,11 +7220,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);
@@ -7096,8 +7232,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));
@@ -7105,60 +7241,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); }
 };
 
 /**
@@ -7182,7 +7497,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));
@@ -7196,7 +7511,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;
@@ -7314,4 +7629,209 @@
     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());
+
+    // 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 10e7293..7fec2c8 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -125,6 +125,9 @@
     pressAndReleaseKey(KEY_HOME);
 }
 
+// --- UinputSteamController
+UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
 // --- UinputTouchScreen ---
 UinputTouchScreen::UinputTouchScreen(const Rect* size)
       : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
@@ -176,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 ec3cd9f..01a557c 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -108,6 +108,16 @@
     UinputHomeKey();
 };
 
+// A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key.
+class UinputSteamController : public UinputKeyboard {
+public:
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputSteamController();
+};
+
 // --- UinputTouchScreen ---
 // A touch screen device with specific size.
 class UinputTouchScreen : public UinputDevice {
@@ -129,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 b0d3e3b..b5e6ae9 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -2,9 +2,15 @@
     name: "libpowermanager",
 
     srcs: [
-        "IPowerManager.cpp",
-        "Temperature.cpp",
+        "BatterySaverPolicyConfig.cpp",
         "CoolingDevice.cpp",
+        "ParcelDuration.cpp",
+        "PowerHalController.cpp",
+        "PowerHalLoader.cpp",
+        "PowerHalWrapper.cpp",
+        "PowerSaveState.cpp",
+        "Temperature.cpp",
+        "WorkSource.cpp",
         ":libpowermanager_aidl",
     ],
 
@@ -17,9 +23,13 @@
     },
 
     shared_libs: [
-        "libutils",
         "libbinder",
-        "liblog"
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.power-cpp",
     ],
 
     cflags: [
@@ -34,22 +44,3 @@
          "include",
     ],
 }
-
-cc_test {
-    name: "thermalmanager-test",
-    srcs: ["IThermalManagerTest.cpp",
-          ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
-    shared_libs: [
-        "libbase",
-        "libhidlbase",
-        "liblog",
-        "libpowermanager",
-        "libbinder",
-        "libutils",
-    ],
-}
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
new file mode 100644
index 0000000..ee55b6b
--- /dev/null
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "BatterySaverPolicyConfig"
+
+#include <android/BatterySaverPolicyConfig.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) {
+    int32_t num = 0;
+    status_t ret = parcel->readInt32(&num);
+    if (ret != OK) {
+        return ret;
+    }
+    for (int i = 0; i < num; i++) {
+        String16 key, val;
+        ret = parcel->readString16(&key) ?:
+              parcel->readString16(&val);
+        if (ret != OK) {
+           return ret;
+        }
+        mDeviceSpecificSettings.emplace_back(key, val);
+    }
+    return ret;
+}
+
+status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->readFloat(&mAdjustBrightnessFactor)
+        ?: parcel->readBool(&mAdvertiseIsEnabled)
+        ?: parcel->readBool(&mDeferFullBackup)
+        ?: parcel->readBool(&mDeferKeyValueBackup)
+        ?: readDeviceSpecificSettings(parcel)
+        ?: parcel->readBool(&mDisableAnimation)
+        ?: parcel->readBool(&mDisableAod)
+        ?: parcel->readBool(&mDisableLaunchBoost)
+        ?: parcel->readBool(&mDisableOptionalSensors)
+        ?: parcel->readBool(&mDisableSoundTrigger)
+        ?: parcel->readBool(&mDisableVibration)
+        ?: parcel->readBool(&mEnableAdjustBrightness)
+        ?: parcel->readBool(&mEnableDataSaver)
+        ?: parcel->readBool(&mEnableFirewall)
+        ?: parcel->readBool(&mEnableNightMode)
+        ?: parcel->readBool(&mEnableQuickDoze)
+        ?: parcel->readBool(&mForceAllAppsStandby)
+        ?: parcel->readBool(&mForceBackgroundCheck)
+        ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode));
+}
+
+status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
+    status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size());
+    if (ret != OK) {
+        return ret;
+    }
+    for (auto& settings : mDeviceSpecificSettings) {
+        ret = parcel->writeString16(settings.first) ?:
+              parcel->writeString16(settings.second);
+        if (ret != OK) {
+           return ret;
+        }
+    }
+    return ret;
+}
+
+status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->writeFloat(mAdjustBrightnessFactor)
+        ?: parcel->writeBool(mAdvertiseIsEnabled)
+        ?: parcel->writeBool(mDeferFullBackup)
+        ?: parcel->writeBool(mDeferKeyValueBackup)
+        ?: writeDeviceSpecificSettings(parcel)
+        ?: parcel->writeBool(mDisableAnimation)
+        ?: parcel->writeBool(mDisableAod)
+        ?: parcel->writeBool(mDisableLaunchBoost)
+        ?: parcel->writeBool(mDisableOptionalSensors)
+        ?: parcel->writeBool(mDisableSoundTrigger)
+        ?: parcel->writeBool(mDisableVibration)
+        ?: parcel->writeBool(mEnableAdjustBrightness)
+        ?: parcel->writeBool(mEnableDataSaver)
+        ?: parcel->writeBool(mEnableFirewall)
+        ?: parcel->writeBool(mEnableNightMode)
+        ?: parcel->writeBool(mEnableQuickDoze)
+        ?: parcel->writeBool(mForceAllAppsStandby)
+        ?: parcel->writeBool(mForceBackgroundCheck)
+        ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode));
+}
+
+} // namespace android::os
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
deleted file mode 100644
index ea3a831..0000000
--- a/services/powermanager/IPowerManager.cpp
+++ /dev/null
@@ -1,142 +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 "IPowerManager"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-class BpPowerManager : public BpInterface<IPowerManager>
-{
-public:
-    explicit BpPowerManager(const sp<IBinder>& impl)
-        : BpInterface<IPowerManager>(impl)
-    {
-    }
-
-    virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
-            const String16& packageName, bool isOneWay)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
-        data.writeStrongBinder(lock);
-        data.writeInt32(flags);
-        data.writeString16(tag);
-        data.writeString16(packageName);
-        data.writeInt32(0); // no WorkSource
-        data.writeString16(NULL, 0); // no history tag
-        return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply,
-                isOneWay ? IBinder::FLAG_ONEWAY : 0);
-    }
-
-    virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
-            const String16& packageName, int uid, bool isOneWay)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
-        data.writeStrongBinder(lock);
-        data.writeInt32(flags);
-        data.writeString16(tag);
-        data.writeString16(packageName);
-        data.writeInt32(uid); // uid to blame for the work
-        return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply,
-                isOneWay ? IBinder::FLAG_ONEWAY : 0);
-    }
-
-    virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeStrongBinder(lock);
-        data.writeInt32(flags);
-        return remote()->transact(RELEASE_WAKE_LOCK, data, &reply,
-                isOneWay ? IBinder::FLAG_ONEWAY : 0);
-    }
-
-    virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
-            bool isOneWay) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeStrongBinder(lock);
-        data.writeInt32Array(len, uids);
-        return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply,
-                isOneWay ? IBinder::FLAG_ONEWAY : 0);
-    }
-
-    virtual status_t powerHint(int hintId, int param)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeInt32(hintId);
-        data.writeInt32(param);
-        // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it
-        return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeInt64(event_time_ms);
-        data.writeInt32(reason);
-        data.writeInt32(flags);
-        return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
-    }
-
-    virtual status_t reboot(bool confirm, const String16& reason, bool wait)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeInt32(confirm);
-        data.writeString16(reason);
-        data.writeInt32(wait);
-        return remote()->transact(REBOOT, data, &reply, 0);
-    }
-
-    virtual status_t shutdown(bool confirm, const String16& reason, bool wait)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeInt32(confirm);
-        data.writeString16(reason);
-        data.writeInt32(wait);
-        return remote()->transact(SHUTDOWN, data, &reply, 0);
-    }
-
-    virtual status_t crash(const String16& message)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-        data.writeString16(message);
-        return remote()->transact(CRASH, data, &reply, 0);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
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
new file mode 100644
index 0000000..178f545
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -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.
+ */
+
+#define LOG_TAG "PowerHalController"
+#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 namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+    sp<IPower> halAidl = PowerHalLoader::loadAidl();
+    if (halAidl) {
+        return std::make_unique<AidlHalWrapper>(halAidl);
+    }
+    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<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+    }
+    if (halHidlV1_0) {
+        return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
+    }
+    return nullptr;
+}
+
+void HalConnector::reset() {
+    PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+    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();
+        if (mConnectedHal == nullptr) {
+            // Unable to connect to Power HAL service. Fallback to default.
+            return mDefaultHal;
+        }
+    }
+    return mConnectedHal;
+}
+
+// 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.
+        mConnectedHal = nullptr;
+        mHalConnector->reset();
+    }
+    return result;
+}
+
+HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    auto result = handle->setBoost(boost, durationMs);
+    return processHalResult(result, "setBoost");
+}
+
+HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    auto result = handle->setMode(mode, enabled);
+    return processHalResult(result, "setMode");
+}
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..1f1b43a
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.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.
+ */
+
+#define LOG_TAG "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <powermanager/PowerHalLoader.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+    if (!exists) {
+        return nullptr;
+    }
+    if (hal) {
+        return hal;
+    }
+    hal = loadFn();
+    if (hal) {
+        ALOGV("Successfully connected to Power HAL %s service.", halName);
+    } else {
+        ALOGV("Power HAL %s service not available.", halName);
+        exists = false;
+    }
+    return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+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);
+    gHalAidl = nullptr;
+    gHalHidlV1_0 = nullptr;
+    gHalHidlV1_1 = nullptr;
+}
+
+sp<IPower> PowerHalLoader::loadAidl() {
+    std::lock_guard<std::mutex> lock(gHalMutex);
+    static bool gHalExists = true;
+    static auto loadFn = []() { return waitForVintfService<IPower>(); };
+    return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
+    std::lock_guard<std::mutex> lock(gHalMutex);
+    return loadHidlV1_0Locked();
+}
+
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
+    std::lock_guard<std::mutex> lock(gHalMutex);
+    static bool gHalExists = true;
+    static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+    return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
+    static bool gHalExists = true;
+    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
new file mode 100644
index 0000000..4a711ca
--- /dev/null
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "HalWrapper"
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+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 HalResult::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;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+    if (boost == Boost::INTERACTION) {
+        return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+    } else {
+        ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+        return HalResult::UNSUPPORTED;
+    }
+}
+
+HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+    uint32_t data = enabled ? 1 : 0;
+    switch (mode) {
+        case Mode::LAUNCH:
+            return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+        case Mode::LOW_POWER:
+            return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+        case Mode::SUSTAINED_PERFORMANCE:
+            return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+        case Mode::VR:
+            return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+        case Mode::INTERACTIVE:
+            return setInteractive(enabled);
+        case Mode::DOUBLE_TAP_TO_WAKE:
+            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 HalResult::UNSUPPORTED;
+    }
+}
+
+HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    return toHalResult(mHandleV1_0->powerHint(hintId, data));
+}
+
+HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
+    return toHalResult(mHandleV1_0->setInteractive(enabled));
+}
+
+HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+    return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+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 ||
+        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 (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+        bool isSupported = false;
+        auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
+        if (!isSupportedRet.isOk()) {
+            ALOGE("Skipped setBoost %s because check support failed with: %s",
+                  toString(boost).c_str(), isSupportedRet.toString8().c_str());
+            return HalResult::FAILED;
+        }
+
+        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 HalResult::UNSUPPORTED;
+        }
+    }
+    lock.unlock();
+
+    return toHalResult(mHandle->setBoost(boost, durationMs));
+}
+
+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 ||
+        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 (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+        bool isSupported = false;
+        auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
+        if (!isSupportedRet.isOk()) {
+            ALOGE("Skipped setMode %s because check support failed with: %s",
+                  toString(mode).c_str(), isSupportedRet.toString8().c_str());
+            return HalResult::FAILED;
+        }
+
+        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 HalResult::UNSUPPORTED;
+        }
+    }
+    lock.unlock();
+
+    return toHalResult(mHandle->setMode(mode, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
new file mode 100644
index 0000000..6d1830a
--- /dev/null
+++ b/services/powermanager/PowerSaveState.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "PowerSaveState"
+
+#include <android/PowerSaveState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->readBool(&mBatterySaverEnabled)
+        ?: parcel->readBool(&mGlobalBatterySaverEnabled)
+        ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+        ?: parcel->readFloat(&mBrightnessFactor);
+}
+
+status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->writeBool(mBatterySaverEnabled)
+        ?: parcel->writeBool(mGlobalBatterySaverEnabled)
+        ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+        ?: parcel->writeFloat(mBrightnessFactor);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
new file mode 100644
index 0000000..caaec55
--- /dev/null
+++ b/services/powermanager/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libpowermanager_test"
+    }
+  ]
+}
diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
new file mode 100644
index 0000000..1006a06
--- /dev/null
+++ b/services/powermanager/WorkSource.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "WorkSource"
+
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t WorkSource::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+    int32_t num;
+    status_t ret = parcel->readInt32(&num)
+                ?: parcel->readInt32Vector(&mUids)
+                ?: parcel->readString16Vector(&mNames);
+
+    return ret;
+}
+
+status_t WorkSource::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    return parcel->writeInt32(mUids.size())
+        ?: parcel->writeInt32Vector(mUids)
+        ?: parcel->writeString16Vector(mNames);
+}
+
+} // namespace android::os
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/ui/UiConfig.cpp b/services/powermanager/benchmarks/main.cpp
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to services/powermanager/benchmarks/main.cpp
index 0ac863d..15c57bf 100644
--- a/libs/ui/UiConfig.cpp
+++ b/services/powermanager/benchmarks/main.cpp
@@ -1,11 +1,11 @@
 /*
- * 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.
  * 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,15 +14,6 @@
  * limitations under the License.
  */
 
-#include <ui/UiConfig.h>
+#include <benchmark/benchmark.h>
 
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
-    static const char* config =
-            " [libui]";
-    configStr.append(config);
-}
-
-
-}; // namespace android
+BENCHMARK_MAIN();
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
new file mode 100644
index 0000000..728c8a0
--- /dev/null
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -0,0 +1,143 @@
+/*
+ * 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_BATTERY_SAVER_POLICY_CONFIG_H
+#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java
+ */
+struct BatterySaverPolicyConfig : public android::Parcelable {
+
+    BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f,
+                             bool advertiseIsEnabled = false,
+                             bool deferFullBackup = false,
+                             bool deferKeyValueBackup = false,
+                             std::vector<std::pair<String16, String16>> deviceSpecificSettings = {},
+                             bool disableAnimation = false,
+                             bool disableAod = false,
+                             bool disableLaunchBoost = false,
+                             bool disableOptionalSensors = false,
+                             bool disableSoundTrigger = false,
+                             bool disableVibration = false,
+                             bool enableAdjustBrightness = false,
+                             bool enableDataSaver = false,
+                             bool enableFirewall = false,
+                             bool enableNightMode = false,
+                             bool enableQuickDoze = false,
+                             bool forceAllAppsStandby = false,
+                             bool forceBackgroundCheck = false,
+                             LocationMode locationMode = static_cast<LocationMode>(0))
+        : mAdjustBrightnessFactor(adjustBrightnessFactor),
+          mAdvertiseIsEnabled(advertiseIsEnabled),
+          mDeferFullBackup(deferFullBackup),
+          mDeferKeyValueBackup(deferKeyValueBackup),
+          mDeviceSpecificSettings(deviceSpecificSettings),
+          mDisableAnimation(disableAnimation),
+          mDisableAod(disableAod),
+          mDisableLaunchBoost(disableLaunchBoost),
+          mDisableOptionalSensors(disableOptionalSensors),
+          mDisableSoundTrigger(disableSoundTrigger),
+          mDisableVibration(disableVibration),
+          mEnableAdjustBrightness(enableAdjustBrightness),
+          mEnableDataSaver(enableDataSaver),
+          mEnableFirewall(enableFirewall),
+          mEnableNightMode(enableNightMode),
+          mEnableQuickDoze(enableQuickDoze),
+          mForceAllAppsStandby(forceAllAppsStandby),
+          mForceBackgroundCheck(forceBackgroundCheck),
+          mLocationMode(locationMode) {
+    }
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
+    status_t writeToParcel(android::Parcel* parcel) const override;
+    bool operator == (const BatterySaverPolicyConfig &bsp) const {
+        return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f &&
+               mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled &&
+               mDeferFullBackup == bsp.mDeferFullBackup &&
+               mDeferKeyValueBackup == bsp.mDeferKeyValueBackup &&
+               mDeviceSpecificSettings == bsp.mDeviceSpecificSettings &&
+               mDisableAnimation == bsp.mDisableAnimation &&
+               mDisableAod == bsp.mDisableAod &&
+               mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
+               mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
+               mDisableSoundTrigger == bsp.mDisableSoundTrigger &&
+               mDisableVibration == bsp.mDisableVibration &&
+               mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
+               mEnableDataSaver == bsp.mEnableDataSaver &&
+               mEnableFirewall == bsp.mEnableFirewall &&
+               mEnableNightMode == bsp.mEnableNightMode &&
+               mEnableQuickDoze == bsp.mEnableQuickDoze &&
+               mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
+               mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
+               mLocationMode == bsp.mLocationMode;
+    }
+
+private:
+    status_t readDeviceSpecificSettings(const android::Parcel *parcel);
+    status_t writeDeviceSpecificSettings(android::Parcel *parcel) const;
+    /** Adjust screen brightness factor */
+    float mAdjustBrightnessFactor;
+    /** Is advertise enabled */
+    bool mAdvertiseIsEnabled;
+    /** Defer full backup */
+    bool mDeferFullBackup;
+    /** Defer key value backup */
+    bool mDeferKeyValueBackup;
+    /** Device specific settings */
+    std::vector<std::pair<String16, String16>> mDeviceSpecificSettings;
+    /** Disable animation */
+    bool mDisableAnimation;
+    /** Disable Aod */
+    bool mDisableAod;
+    /** Disable launch boost */
+    bool mDisableLaunchBoost;
+    /** Disable optional sensors */
+    bool mDisableOptionalSensors;
+    /** Disable sound trigger */
+    bool mDisableSoundTrigger;
+    /** Disable vibration */
+    bool mDisableVibration;
+    /** Enable adjust brightness */
+    bool mEnableAdjustBrightness;
+    /** Enable data saver */
+    bool mEnableDataSaver;
+    /** Enable firewall */
+    bool mEnableFirewall;
+    /** Enable night mode */
+    bool mEnableNightMode;
+    /** Enable quick doze */
+    bool mEnableQuickDoze;
+    /** Force all Apps standby */
+    bool mForceAllAppsStandby;
+    /** Force Background check */
+    bool mForceBackgroundCheck;
+    /** Location mode */
+    LocationMode mLocationMode;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */
diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h
new file mode 100644
index 0000000..42933d4
--- /dev/null
+++ b/services/powermanager/include/android/LocationMode.h
@@ -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.
+ */
+
+#ifndef ANDROID_OS_LOCATION_MODE_H
+#define ANDROID_OS_LOCATION_MODE_H
+
+namespace android::os {
+
+enum class LocationMode : int32_t {
+    NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE,
+    GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+    ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+    FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY,
+    THROTTLE_REQUESTS_WHEN_SCREEN_OFF =
+                IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+    MIN = IPowerManager::LOCATION_MODE_NO_CHANGE,
+    MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_LOCATION_MODE_H */
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/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
new file mode 100644
index 0000000..b421f6a
--- /dev/null
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -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.
+ */
+
+#ifndef ANDROID_OS_POWER_SAVE_STATE_H
+#define ANDROID_OS_POWER_SAVE_STATE_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * PowerSaveState is a structure to encapsulate PowerSaveState status.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
+ */
+struct PowerSaveState : public android::Parcelable {
+
+    PowerSaveState(bool batterySaverEnabled = false,
+                   bool globalBatterySaverEnabled = false,
+                   LocationMode locationMode = static_cast<LocationMode>(0),
+                   float brightnessFactor = 0.5f)
+            : mBatterySaverEnabled(batterySaverEnabled),
+              mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
+              mLocationMode(locationMode),
+              mBrightnessFactor(brightnessFactor) {
+    }
+
+    bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
+    bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
+    LocationMode getLocationMode() const { return mLocationMode; }
+    float getBrightnessFactor() const { return mBrightnessFactor; }
+    bool operator == (const PowerSaveState &ps) const {
+        return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
+               mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled &&
+               mLocationMode == ps.mLocationMode &&
+               fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f;
+    }
+
+    status_t readFromParcel(const android::Parcel* parcel) override;
+    status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+    /** Whether we should enable battery saver for this service. */
+    bool mBatterySaverEnabled;
+    /** Whether battery saver mode is enabled. */
+    bool mGlobalBatterySaverEnabled;
+    /** Location mode */
+    LocationMode mLocationMode;
+    /** Screen brightness factor. */
+    float mBrightnessFactor;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_POWER_SAVE_STATE_H */
diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h
new file mode 100644
index 0000000..f12847d
--- /dev/null
+++ b/services/powermanager/include/android/WorkSource.h
@@ -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.
+ */
+
+#ifndef ANDROID_OS_WORKSOURCE_H
+#define ANDROID_OS_WORKSOURCE_H
+
+#include <optional>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * WorkSource is a structure to describes the source of some work that may be done by someone else.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java
+ */
+struct WorkSource : public android::Parcelable {
+    WorkSource(
+               std::vector<int32_t> uids = {},
+               std::optional<std::vector<std::optional<String16>>> names = std::nullopt)
+        : mUids(uids),
+          mNames(names) {
+    }
+    std::vector<int32_t> getUids() const { return mUids; }
+    std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; }
+    bool operator == (const WorkSource &ws) const {
+        return mUids == ws.mUids && mNames == ws.mNames;
+    }
+    status_t readFromParcel(const android::Parcel* parcel) override;
+    status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+    /** WorkSource UID array */
+    std::vector<int32_t> mUids = {};
+    /** WorkSource Tag array */
+    std::optional<std::vector<std::optional<String16>>> mNames = {};
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_WORKSOURCE_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
new file mode 100644
index 0000000..49abc11
--- /dev/null
+++ b/services/powermanager/tests/Android.bp
@@ -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.
+
+cc_test {
+    name: "libpowermanager_test",
+    test_suites: ["device-tests"],
+    srcs: [
+        "IThermalManagerTest.cpp",
+        "PowerHalControllerTest.cpp",
+        "PowerHalLoaderTest.cpp",
+        "PowerHalWrapperAidlTest.cpp",
+        "PowerHalWrapperHidlV1_0Test.cpp",
+        "PowerHalWrapperHidlV1_1Test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libpowermanager",
+        "libutils",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.power-cpp",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
similarity index 95%
rename from services/powermanager/IThermalManagerTest.cpp
rename to services/powermanager/tests/IThermalManagerTest.cpp
index 575b9ee..b62be5f 100644
--- a/services/powermanager/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
new file mode 100644
index 0000000..141b244
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "PowerHalControllerTest"
+
+#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/PowerHalController.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+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));
+};
+
+class TestPowerHalConnector : public HalConnector {
+public:
+    TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+    virtual ~TestPowerHalConnector() = default;
+
+    virtual std::unique_ptr<HalWrapper> connect() override {
+        mCountMutex.lock();
+        ++mConnectedCount;
+        mCountMutex.unlock();
+        return std::make_unique<HidlHalWrapperV1_0>(mHal);
+    }
+
+    void reset() override {
+        mCountMutex.lock();
+        ++mResetCount;
+        mCountMutex.unlock();
+    }
+
+    int getConnectCount() { return mConnectedCount; }
+
+    int getResetCount() { return mResetCount; }
+
+private:
+    sp<IPower> mHal = nullptr;
+    std::mutex mCountMutex;
+    int mConnectedCount = 0;
+    int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+    AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+    std::unique_ptr<HalWrapper> connect() override {
+        // Call parent to update counter, but ignore connected HalWrapper.
+        TestPowerHalConnector::connect();
+        return nullptr;
+    }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIPowerV1_0>();
+        std::unique_ptr<TestPowerHalConnector> halConnector =
+                std::make_unique<TestPowerHalConnector>(mMockHal);
+        mHalConnector = halConnector.get();
+        mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+    }
+
+protected:
+    sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+    TestPowerHalConnector* mHalConnector = nullptr;
+    std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    mHalController->init();
+    mHalController->init();
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+    std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+            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.
+    auto result = halController.setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    result = halController.setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+    // PowerHalConnector was called every time to attempt to reconnect with
+    // underlying service.
+    powerHalConnectCount = failingHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 2);
+    // PowerHalConnector was never reset.
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    {
+        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));
+    }
+
+    auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+            .WillByDefault([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
+
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
+
+    auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::FAILED, result);
+    result = mHalController->setMode(Mode::VR, false);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LOW_POWER, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+    // 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.
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    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(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.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+            .WillByDefault([](PowerHint, int32_t) {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
+
+    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(HalResult::SUCCESSFUL, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::LAUNCH, true);
+            ASSERT_EQ(HalResult::FAILED, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::LOW_POWER, false);
+            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::VR, true);
+            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.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..058e1b5
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "PowerHalLoaderTest"
+
+#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>
+
+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;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+    return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+    return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+    return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public Test {
+public:
+    sp<T> load() { return ::loadHal<T>(); }
+    void unload() { PowerHalLoader::unloadAll(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+    sp<TypeParam> firstHal = this->load();
+    if (firstHal == nullptr) {
+        ALOGE("Power HAL not available. Skipping test.");
+        return;
+    }
+    sp<TypeParam> secondHal = this->load();
+    ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+    sp<TypeParam> firstHal = this->load();
+    if (firstHal == nullptr) {
+        ALOGE("Power HAL not available. Skipping test.");
+        return;
+    }
+    this->unload();
+    sp<TypeParam> secondHal = this->load();
+    ASSERT_NE(secondHal, nullptr);
+    ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+    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));
+    }
+
+    futures[0].wait();
+    sp<TypeParam> firstHal = futures[0].get();
+    if (firstHal == nullptr) {
+        ALOGE("Power HAL not available. Skipping test.");
+        return;
+    }
+
+    for (int i = 1; i < 10; i++) {
+        futures[i].wait();
+        sp<TypeParam> currentHal = futures[i].get();
+        ASSERT_EQ(firstHal, currentHal);
+    }
+}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..a765659
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "PowerHalWrapperAidlTest"
+
+#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>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPower : public IPower {
+public:
+    MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+    MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+    MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+    MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperAidlTest : public Test {
+public:
+    void SetUp() override;
+
+protected:
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
+    sp<StrictMock<MockIPower>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperAidlTest::SetUp() {
+    mMockHal = new StrictMock<MockIPower>();
+    mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
+    ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+                .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));
+    }
+
+    auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
+    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())));
+        EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+                .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)));
+    }
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+    ASSERT_EQ(HalResult::FAILED, result);
+    result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
+    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())));
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
+    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));
+    }
+
+    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(HalResult::SUCCESSFUL, result);
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
+                .Times(Exactly(1));
+    }
+
+    auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+    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())));
+        EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
+                .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)));
+    }
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::FAILED, result);
+    result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+    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())));
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+    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));
+    }
+
+    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(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
new file mode 100644
index 0000000..6693d0b
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "PowerHalWrapperHidlV1_0Test"
+
+#include <android/hardware/power/Boost.h>
+#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;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+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));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_0Test : public Test {
+public:
+    void SetUp() override;
+
+protected:
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
+    sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_0Test::SetUp() {
+    mMockHal = new StrictMock<MockIPowerV1_0>();
+    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));
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+    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));
+            });
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+    auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+    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::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));
+        EXPECT_CALL(*mMockHal.get(),
+                    setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+                .Times(Exactly(1));
+    }
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::LOW_POWER, false);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::VR, false);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::INTERACTIVE, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+    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));
+            });
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+    ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+    auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..55bbd6d
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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 "PowerHalWrapperHidlV1_1Test"
+
+#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 <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.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 IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPowerV1_0 {
+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));
+};
+
+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));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_1Test : public Test {
+public:
+    void SetUp() override;
+
+protected:
+    std::unique_ptr<HalWrapper> mWrapper = nullptr;
+    sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
+    sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_1Test::SetUp() {
+    mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
+    mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
+    mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+    ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
+    EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+            .Times(Exactly(1));
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+    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));
+            });
+
+    auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+    auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+    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));
+        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));
+        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));
+        EXPECT_CALL(*mMockHalV1_0.get(),
+                    setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+                .Times(Exactly(1));
+    }
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::LOW_POWER, false);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::VR, false);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::INTERACTIVE, true);
+    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+    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));
+            });
+
+    auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+    ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+    auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 45e67f7..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);
                         }
                     }
 
@@ -820,7 +823,7 @@
 
     status_t err(NO_ERROR);
     // If the min period or min timeout has changed since the last batch call, call batch.
-    if (prevBestBatchParams != info.bestBatchParams) {
+    if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
                  info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
         err = checkReturnAndGetStatus(mSensors->batch(
@@ -890,14 +893,13 @@
         Info& info = mActivationCount.editValueAt(i);
 
         if (info.hasBatchParamsForIdent(ident)) {
-            if (updateBatchParamsLocked(handle, info) != NO_ERROR) {
-                bool enable = info.numActiveClients() == 0 && info.isActive;
-                bool disable = info.numActiveClients() > 0 && !info.isActive;
+            updateBatchParamsLocked(handle, info);
+            bool disable = info.numActiveClients() == 0 && info.isActive;
+            bool enable = info.numActiveClients() > 0 && !info.isActive;
 
-                if ((enable || disable) &&
-                    doActivateHardwareLocked(handle, enable) == NO_ERROR) {
-                    info.isActive = enable;
-                }
+            if ((enable || disable) &&
+                doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+                info.isActive = enable;
             }
         }
     }
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 0dcf8c0..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;
     }
@@ -40,22 +39,12 @@
     switch ((SensorTypeV2_1)event->type) {
         case SensorTypeV2_1::ACCELEROMETER:
         case SensorTypeV2_1::MAGNETIC_FIELD:
-        case SensorTypeV2_1::ORIENTATION:
         case SensorTypeV2_1::GYROSCOPE:
-        case SensorTypeV2_1::GRAVITY:
-        case SensorTypeV2_1::LINEAR_ACCELERATION:
         case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
         case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
         case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED:
             axes = 3;
             break;
-        case SensorTypeV2_1::GAME_ROTATION_VECTOR:
-            axes = 4;
-            break;
-        case SensorTypeV2_1::ROTATION_VECTOR:
-        case SensorTypeV2_1::GEOMAGNETIC_ROTATION_VECTOR:
-            axes = 5;
-            break;
         case SensorTypeV2_1::DEVICE_ORIENTATION:
         case SensorTypeV2_1::LIGHT:
         case SensorTypeV2_1::PRESSURE:
@@ -77,11 +66,8 @@
         case SensorTypeV2_1::HINGE_ANGLE:
             axes = 1;
             break;
-        case SensorTypeV2_1::POSE_6DOF:
-            axes = 15;
-            break;
         default:
-            // No other sensors have data that needs to be rounded.
+            // No other sensors have data that needs to be quantized.
             break;
     }
 
@@ -92,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:
@@ -104,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..d14a301 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -28,6 +28,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 +41,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;
@@ -271,11 +278,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 +451,25 @@
     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 appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
+                                                                mOpPackageName);
+            success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        }
     }
     return success;
 }
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 13cee6f..8f2d5db 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -116,8 +116,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,6 +176,7 @@
     int mEventsDropped;
     String8 mPackageName;
     const String16 mOpPackageName;
+    int mTargetSdk;
 #if DEBUG_CONNECTIONS
     int mEventsReceived, mEventsSent, mEventsSentFromCache;
     int mTotalAcksNeeded, mTotalAcksReceived;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index ffcd0a0..2969839 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"
@@ -699,6 +701,9 @@
     if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
         return PERMISSION_DENIED;
     }
+    if (args.size() == 0) {
+      return BAD_INDEX;
+    }
     if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
         return BAD_VALUE;
     }
@@ -1774,7 +1779,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,36 +1810,28 @@
     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)) {
+    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.
+        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 || 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
-        // 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__) {
-            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());
+        ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
+              operation, sensor.getName().string(), sensor.getRequiredPermission().string());
     }
 
     return canAccess;
@@ -1852,6 +1852,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..052cbfe 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -424,6 +424,7 @@
     static AppOpsManager sAppOpsManager;
     static std::map<String16, int> sPackageTargetVersion;
     static Mutex sPackageTargetVersionLock;
+    static String16 sSensorInterfaceDescriptorPrefix;
 };
 
 } // namespace android
diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp
index d0c83d6..0e1af59 100644
--- a/services/sensorservice/hidl/Android.bp
+++ b/services/sensorservice/hidl/Android.bp
@@ -10,6 +10,7 @@
         "-Wall",
         "-Werror",
     ],
+    header_libs: ["jni_headers"],
     shared_libs: [
         "libbase",
         "libhidlbase",
@@ -24,6 +25,7 @@
     export_include_dirs: [
         "include/"
     ],
+    export_header_lib_headers: ["jni_headers"],
     local_include_dirs: [
         "include/sensorservicehidl/"
     ]
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 0182937..cf46e03 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -38,7 +38,6 @@
         "libbinder",
         "libbufferhubqueue",
         "libcutils",
-        "libdl",
         "libEGL",
         "libfmq",
         "libGLESv1_CM",
@@ -69,11 +68,11 @@
     },
     static_libs: [
         "libcompositionengine",
+        "libframetimeline",
         "libperfetto_client_experimental",
         "librenderengine",
         "libserviceutils",
         "libtrace_proto",
-        "libvr_manager",
         "libvrflinger",
     ],
     header_libs: [
@@ -147,6 +146,7 @@
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
+        "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
         "FrameTracer/FrameTracer.cpp",
@@ -154,15 +154,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",
@@ -170,15 +169,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",
@@ -188,22 +187,18 @@
     ],
 }
 
-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",
+cc_defaults {
+    name: "libsurfaceflinger_binary",
+    defaults: [
+        "surfaceflinger_defaults",
+        "libsurfaceflinger_production_defaults",
     ],
     cflags: [
+        "-DLOG_TAG=\"SurfaceFlinger\"",
         "-DUSE_VR_COMPOSER=1",
     ],
     // VrComposer is not used when building surfaceflinger for vendors
+    // TODO: Is this ever built for vendors?
     target: {
         vendor: {
             cflags: [
@@ -211,15 +206,6 @@
             ],
         },
     },
-    logtags: ["EventLog/EventLogTags.logtags"],
-}
-
-cc_defaults {
-    name: "libsurfaceflinger_binary",
-    defaults: ["surfaceflinger_defaults"],
-    cflags: [
-        "-DLOG_TAG=\"SurfaceFlinger\"",
-    ],
     shared_libs: [
         "android.frameworks.displayservice@1.0",
         "android.hardware.configstore-utils",
@@ -241,23 +227,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 8ecdd95..cf60b71 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -319,7 +319,7 @@
     return hasReadyFrame();
 }
 
-bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
+bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
@@ -342,7 +342,7 @@
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
 
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer && outputLayer->requiresClientComposition()) {
         nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
@@ -359,13 +359,15 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
-    const auto displayId = displayDevice->getId();
     if (presentFence->isValid()) {
         mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
         mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-    } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+    } else if (!display) {
+        // Do nothing.
+    } else if (const auto displayId = display->getId();
+               displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
         const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
@@ -537,20 +539,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);
@@ -600,14 +588,8 @@
     return true;
 }
 
-bool BufferLayer::needsFiltering(const sp<const DisplayDevice>& displayDevice) const {
-    // If we are not capturing based on the state of a known display device,
-    // just return false.
-    if (displayDevice == nullptr) {
-        return false;
-    }
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer == nullptr) {
         return false;
     }
@@ -621,15 +603,9 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-bool BufferLayer::needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
                                                const ui::Transform& inverseParentTransform) const {
-    // If we are not capturing based on the state of a known display device,
-    // just return false.
-    if (displayDevice == nullptr) {
-        return false;
-    }
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer == nullptr) {
         return false;
     }
@@ -637,7 +613,7 @@
     // We need filtering if the sourceCrop rectangle size does not match the
     // viewport rectangle size (not a 1:1 render)
     const auto& compositionState = outputLayer->getState();
-    const ui::Transform& displayTransform = displayDevice->getTransform();
+    const ui::Transform& displayTransform = display->getTransform();
     const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
     // Undo the transformation of the displayFrame so that we're back into
     // layer-stack space.
@@ -843,7 +819,7 @@
     mDrawingState.inputInfo = tmpInputInfo;
 }
 
-void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) const {
+void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
     mTransformHint = getFixedTransformHint();
     if (mTransformHint == ui::Transform::ROT_INVALID) {
         mTransformHint = displayTransformHint;
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index cfccc8a..54c060b 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -50,10 +50,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;
 
@@ -79,10 +76,9 @@
 
     bool isHdrY410() const override;
 
-    bool onPostComposition(sp<const DisplayDevice> displayDevice,
-                           const std::shared_ptr<FenceTime>& glDoneFence,
+    bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
-                           const CompositorTiming& compositorTiming) override;
+                           const CompositorTiming&) override;
 
     // latchBuffer - called each time the screen is redrawn and returns whether
     // the visible regions need to be recomputed (this is a fairly heavy
@@ -119,40 +115,6 @@
 
     ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
-    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) const override;
-
-    // -----------------------------------------------------------------------
-
-    // -----------------------------------------------------------------------
-    // 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;
-
 protected:
     struct BufferInfo {
         nsecs_t mDesiredPresentTime;
@@ -187,9 +149,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
@@ -209,20 +168,52 @@
 
     virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
+    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
     // Transform hint provided to the producer. This must be accessed holding
     /// the mStateLock.
-    mutable ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+    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 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;
+
+    // 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 sp<const DisplayDevice>& displayDevice) const override;
-    bool needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+    bool needsFiltering(const DisplayDevice*) const override;
+    bool needsFilteringForScreenshots(const DisplayDevice*,
                                       const ui::Transform& inverseParentTransform) const override;
 
     // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
     // and its parent layer is not bounded
     Rect getBufferSize(const State& s) const override;
 
+    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 648d129..94cbfa1 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>
 
@@ -452,6 +452,13 @@
 }
 
 void BufferLayerConsumer::onDisconnect() {
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // Nothing to do if we're already abandoned.
+        return;
+    }
+
     mLayer->onDisconnect();
 }
 
@@ -486,6 +493,13 @@
 
 void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                                    FrameEventHistoryDelta* outDelta) {
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // Nothing to do if we're already abandoned.
+        return;
+    }
+
     mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
 }
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index c71a1d9..a28902d 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -34,7 +34,6 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class DispSync;
 class Layer;
 class String8;
 
@@ -331,8 +330,8 @@
     // construction time.
     const uint32_t mTexName;
 
-    // The layer for this BufferLayerConsumer
-    Layer* mLayer;
+    // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
+    Layer* mLayer GUARDED_BY(mMutex);
 
     wp<ContentsChangedListener> mContentsChangedListener;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 5d80768..89284f2 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -31,6 +31,7 @@
 #include "SurfaceInterceptor.h"
 
 #include "FrameTracer/FrameTracer.h"
+#include "Scheduler/LayerHistory.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -58,7 +59,7 @@
     }
 }
 
-void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) const {
+void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
     BufferLayer::setTransformHint(displayTransformHint);
     mConsumer->setTransformHint(mTransformHint);
 }
@@ -130,10 +131,6 @@
 // -----------------------------------------------------------------------
 
 bool BufferQueueLayer::fenceHasSignaled() const {
-    if (latchUnsignaledBuffers()) {
-        return true;
-    }
-
     if (!hasFrameUpdate()) {
         return true;
     }
@@ -207,8 +204,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();
@@ -323,9 +324,6 @@
         }
 
         uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
-        mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
-                                           mQueueItems[0].mFenceTime,
-                                           FrameTracer::FrameEvent::ACQUIRE_FENCE);
         mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
                                                FrameTracer::FrameEvent::LATCH);
@@ -392,14 +390,19 @@
 
 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
     { // Autolock scope
         const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime);
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+                                                 LayerHistory::LayerUpdateType::Buffer);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
@@ -458,8 +461,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 9bcb63a..1ac0453 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -35,16 +35,11 @@
     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;
 
-    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) const override;
-
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
 
     // If a buffer was replaced this frame, release the former buffer
@@ -56,40 +51,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;
-
-    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;
@@ -115,19 +82,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 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 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;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a1ed6d7..884cc0c 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 {
@@ -98,9 +97,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;
     }
 }
@@ -277,13 +273,11 @@
     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);
     desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+                                             LayerHistory::LayerUpdateType::Buffer);
 
     addFrameEvent(acquireFence, postTime, desiredPresentTime);
     return true;
@@ -428,10 +422,6 @@
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
 bool BufferStateLayer::fenceHasSignaled() const {
-    if (latchUnsignaledBuffers()) {
-        return true;
-    }
-
     const bool fenceSignaled =
             getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
     if (!fenceSignaled) {
@@ -579,20 +569,13 @@
         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;
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 00fa7f7..89d9a00 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;
@@ -117,6 +115,8 @@
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
 private:
+    friend class SlotGenerationTest;
+
     bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
                                  nsecs_t requestedPresentTime);
 
@@ -141,8 +141,6 @@
     // Crop that applies to the buffer
     Rect computeCrop(const State& s);
 
-private:
-    friend class SlotGenerationTest;
     bool willPresentCurrentTransaction() const;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index e9063e5..f64be3a 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:
@@ -80,7 +76,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 fda451b..b37ca33 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -21,7 +21,6 @@
         "liblog",
         "libnativewindow",
         "libprotobuf-cpp-lite",
-        "libsync",
         "libtimestats",
         "libui",
         "libutils",
@@ -122,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/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..b4ed92f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -116,7 +116,7 @@
     FloatRect geomLayerBounds;
 
     // length of the shadow in screen space
-    float shadowRadius;
+    float shadowRadius{0.f};
 
     /*
      * Geometry state
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index baf5258..6552c54 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,11 +163,11 @@
     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(const ui::Transform&, uint32_t orientation,
+                               const Rect& orientedDisplaySpaceRect,
+                               const Rect& layerStackSpaceRect, const Rect& displaySpaceRect) = 0;
     // Sets the bounds to use
-    virtual void setBounds(const ui::Size&) = 0;
+    virtual void setDisplaySpaceSize(const ui::Size&) = 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..9d15665
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -0,0 +1,56 @@
+/*
+ * 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;
+};
+
+} // namespace compositionengine
+
+inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
+    return android::base::StringPrintf("ProjectionSpace(bounds = %s, content = %s)",
+                                       to_string(space.bounds).c_str(),
+                                       to_string(space.content).c_str());
+}
+
+// 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/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 5ce2fdc..f680460 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -63,6 +63,12 @@
     // Sets the dataspace used for rendering the surface
     virtual void setBufferDataspace(ui::Dataspace) = 0;
 
+    // Sets the pixel format used for rendering the surface.
+    // Changing the pixel format of the buffer will result in buffer
+    // reallocation as well as some reconfiguration of the graphics context,
+    // which are both expensive operations.
+    virtual void setBufferPixelFormat(ui::PixelFormat) = 0;
+
     // Configures the protected rendering on the surface
     virtual void setProtected(bool useProtected) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 9ca7d2f..7a4f738 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -70,11 +70,13 @@
     using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
     using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
     using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
+    using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
     virtual bool anyLayersRequireClientComposition() const;
     virtual bool allLayersRequireClientComposition() const;
     virtual void applyChangedTypesToLayers(const ChangedTypes&);
     virtual void applyDisplayRequests(const DisplayRequests&);
     virtual void applyLayerRequestsToLayers(const LayerRequests&);
+    virtual void applyClientTargetRequests(const ClientTargetProperty&);
 
     // Internal
     virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6f25e63..e009894 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,10 +38,10 @@
     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(const ui::Transform&, uint32_t orientation,
+                       const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+                       const Rect& displaySpaceRect) override;
+    void setDisplaySpaceSize(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66ed2b6..462d952 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>
@@ -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 content in this space is always in a single
+    // orientation.
+    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 content in this space is always in a single
+    // orientation.
+    ProjectionSpace orientedDisplaySpace;
+
+    // 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.
+    // 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/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 692d78d..5127a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -49,6 +49,7 @@
 
     const sp<Fence>& getClientTargetAcquireFence() const override;
     void setBufferDataspace(ui::Dataspace) override;
+    void setBufferPixelFormat(ui::PixelFormat) override;
     void setDisplaySize(const ui::Size&) override;
     void setProtected(bool useProtected) override;
     status_t beginFrame(bool mustRecompose) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4661c5d..5350611 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,10 +36,9 @@
     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_METHOD5(setProjection,
+                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&));
+    MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ed4d492..a0cae6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -36,6 +36,7 @@
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD1(setProtected, void(bool));
     MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+    MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
     MOCK_METHOD2(prepareFrame, void(bool, bool));
     MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ab26939..d201104 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -259,6 +259,7 @@
         applyChangedTypesToLayers(changes->changedTypes);
         applyDisplayRequests(changes->displayRequests);
         applyLayerRequestsToLayers(changes->layerRequests);
+        applyClientTargetRequests(changes->clientTargetProperty);
     }
 
     // Determine what type of composition we are doing from the final state
@@ -326,6 +327,16 @@
     }
 }
 
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+    if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
+        return;
+    }
+    auto outputState = editState();
+    outputState.dataspace = clientTargetProperty.dataspace;
+    getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
+    getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
+}
+
 compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
     auto result = impl::Output::presentAndGetFrameFences();
 
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/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e8f54f5..816a09b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -105,26 +105,32 @@
     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(const ui::Transform& transform, uint32_t orientation,
+                           const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+                           const Rect& displaySpaceRect) {
     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.content = displaySpaceRect;
+    // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+    ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+    if (orientation == ui::Transform::ROT_90 || orientation == ui::Transform::ROT_270) {
+        std::swap(orientedSize.width, orientedSize.height);
+    }
+    outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+
+    outputState.layerStackSpace.content = layerStackSpaceRect;
+    outputState.layerStackSpace.bounds = layerStackSpaceRect;
+    outputState.needsFiltering = transform.needsBilinearFiltering();
 
     dirtyEntireOutput();
 }
 
-// TODO(b/121291683): Rename setSize() once more is moved.
-void Output::setBounds(const ui::Size& size) {
+void Output::setDisplaySpaceSize(const ui::Size& size) {
     mRenderSurface->setDisplaySize(size);
-    // TODO(b/121291683): Rename outputState.size once more is moved.
-    editState().bounds = Rect(mRenderSurface->getSize());
+    editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
@@ -232,7 +238,7 @@
 
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
-    editState().bounds = Rect(mRenderSurface->getSize());
+    editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
@@ -251,7 +257,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 +342,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 +545,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 +562,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;
 }
 
@@ -862,8 +868,8 @@
     ALOGV("hasClientComposition");
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
-    clientCompositionDisplay.clip = outputState.sourceClip;
+    clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+    clientCompositionDisplay.clip = outputState.layerStackSpace.content;
     clientCompositionDisplay.orientation = outputState.orientation;
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
@@ -921,9 +927,8 @@
 
     const nsecs_t renderEngineStart = systemTime();
     status_t status =
-            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
-                                    buf->getNativeBuffer(), /*useFramebufferCache=*/true,
-                                    std::move(fd), &readyFence);
+            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+                                    /*useFramebufferCache=*/true, std::move(fd), &readyFence);
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
@@ -948,11 +953,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();
@@ -985,18 +989,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()) {
@@ -1093,7 +1096,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 ca5be48..776fdde 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -37,11 +37,12 @@
     dumpVal(out, "transform", transform);
 
     out.append("\n   ");
-
-    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, "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 81f2dd1..376b4b3 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.
@@ -149,9 +149,9 @@
         // a modification of the axes of rotation. To account for this we
         // need to reorient the inverse rotation in terms of the current
         // axes of rotation.
-        bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
-        bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
-        if (is_h_flipped == is_v_flipped) {
+        bool isHFlipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
+        bool isVFlipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
+        if (isHFlipped == isVFlipped) {
             invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
         }
         std::swap(winWidth, winHeight);
@@ -160,18 +160,18 @@
             activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
 
     // below, crop is intersected with winCrop expressed in crop's coordinate space
-    float xScale = crop.getWidth() / float(winWidth);
-    float yScale = crop.getHeight() / float(winHeight);
+    const float xScale = crop.getWidth() / float(winWidth);
+    const float yScale = crop.getHeight() / float(winHeight);
 
-    float insetL = winCrop.left * xScale;
-    float insetT = winCrop.top * yScale;
-    float insetR = (winWidth - winCrop.right) * xScale;
-    float insetB = (winHeight - winCrop.bottom) * yScale;
+    const float insetLeft = winCrop.left * xScale;
+    const float insetTop = winCrop.top * yScale;
+    const float insetRight = (winWidth - winCrop.right) * xScale;
+    const float insetBottom = (winHeight - winCrop.bottom) * yScale;
 
-    crop.left += insetL;
-    crop.top += insetT;
-    crop.right -= insetR;
-    crop.bottom -= insetB;
+    crop.left += insetLeft;
+    crop.top += insetTop;
+    crop.right -= insetRight;
+    crop.bottom -= insetBottom;
 
     return crop;
 }
@@ -189,7 +189,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 +215,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};
@@ -568,7 +568,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 660baff..2773fd3 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -28,7 +28,6 @@
 
 #include <log/log.h>
 #include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
 #include <system/window.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
@@ -105,6 +104,10 @@
                                          static_cast<android_dataspace>(dataspace));
 }
 
+void RenderSurface::setBufferPixelFormat(ui::PixelFormat pixelFormat) {
+    native_window_set_buffers_format(mNativeWindow.get(), static_cast<int32_t>(pixelFormat));
+}
+
 void RenderSurface::setProtected(bool useProtected) {
     uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
     if (useProtected) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 62977a4..4519a9d 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;
@@ -654,6 +655,7 @@
             {{nullptr, hal::Composition::CLIENT}},
             hal::DisplayRequest::FLIP_CLIENT_TARGET,
             {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+            {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
     };
 
     // Since two calls are made to anyLayersRequireClientComposition with different return
@@ -906,7 +908,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;
@@ -927,7 +929,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;
@@ -948,7 +950,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;
@@ -969,7 +971,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/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 75a4fec..1dd5df4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -46,7 +46,7 @@
 
     MOCK_METHOD3(allocateVirtualDisplay,
                  std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
-    MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId));
+    MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
     MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
     MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
     MOCK_METHOD3(getDeviceCompositionChanges,
@@ -107,8 +107,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..df3da85 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -138,7 +138,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() {
@@ -223,7 +223,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 +245,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 +293,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);
 }
@@ -988,7 +988,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..23efd2d 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) {
@@ -240,24 +240,19 @@
     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;
 
-    mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
-                           needsFiltering);
+    mOutput->setProjection(transform, orientation, frame, viewport, destinationClip);
 
     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);
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+    EXPECT_EQ(destinationClip, mOutput->getState().displaySpace.content);
 }
 
 /*
- * Output::setBounds()
+ * Output::setDisplaySpaceSize()
  */
 
 TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
@@ -266,9 +261,9 @@
     EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
     EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
 
-    mOutput->setBounds(displaySize);
+    mOutput->setDisplaySpaceSize(displaySize);
 
-    EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
+    EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
 
     EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
 }
@@ -431,7 +426,7 @@
 
     mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
+    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
 }
 
 /*
@@ -440,7 +435,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 +447,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 +855,7 @@
     OutputRebuildLayerStacksTest() {
         mOutput.mState.isEnabled = true;
         mOutput.mState.transform = kIdentityTransform;
-        mOutput.mState.bounds = kOutputBounds;
+        mOutput.mState.displaySpace.bounds = kOutputBounds;
 
         mRefreshArgs.updatingOutputGeometryThisFrame = true;
 
@@ -1067,8 +1062,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 +1143,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 +1340,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 +1366,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,10 +2780,9 @@
         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.orientedDisplaySpace.content = kDefaultOutputFrame;
+        mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+        mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
         mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
         mOutput.mState.orientation = kDefaultOutputOrientation;
         mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -2834,7 +2828,6 @@
 
     static const Rect kDefaultOutputFrame;
     static const Rect kDefaultOutputViewport;
-    static const Rect kDefaultOutputSourceClip;
     static const Rect kDefaultOutputDestinationClip;
     static const mat4 kDefaultColorTransformMat;
 
@@ -2856,7 +2849,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;
@@ -3122,7 +3114,7 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
                                             Region::INVALID_REGION, kDefaultOutputOrientation})
             .execute()
@@ -3133,7 +3125,7 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
                                             Region::INVALID_REGION, kDefaultOutputOrientation})
             .execute()
@@ -3144,7 +3136,7 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
                                             kDefaultOutputOrientation})
@@ -3156,7 +3148,7 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
                                             kDefaultOutputOrientation})
@@ -3169,7 +3161,7 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(true)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
                                             Region::INVALID_REGION, kDefaultOutputOrientation})
             .execute()
@@ -3414,10 +3406,9 @@
 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.orientedDisplaySpace.content = kDisplayFrame;
+        mOutput.mState.layerStackSpace.content = kDisplayViewport;
+        mOutput.mState.displaySpace.content = kDisplayDestinationClip;
         mOutput.mState.transform = ui::Transform{kDisplayOrientation};
         mOutput.mState.orientation = kDisplayOrientation;
         mOutput.mState.needsFiltering = false;
@@ -3448,7 +3439,6 @@
 
     static const Rect kDisplayFrame;
     static const Rect kDisplayViewport;
-    static const Rect kDisplaySourceClip;
     static const Rect kDisplayDestinationClip;
 
     std::array<Layer, 3> mLayers;
@@ -3456,7 +3446,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 +3572,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 +3587,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3643,7 +3630,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 +3641,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 +3652,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 +3683,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3711,7 +3694,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3723,7 +3705,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3755,7 +3736,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3768,7 +3748,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3780,7 +3759,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
@@ -3811,7 +3789,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3823,7 +3800,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3835,7 +3811,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
@@ -3864,7 +3839,6 @@
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3876,7 +3850,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3888,7 +3861,6 @@
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
-            false, /* identity transform */
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
@@ -3945,15 +3917,13 @@
 
     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;
     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.orientedDisplaySpace.content = kPortraitFrame;
+    mOutput.mState.layerStackSpace.content = kPortraitViewport;
+    mOutput.mState.displaySpace.content = kPortraitDestinationClip;
     mOutput.mState.transform = ui::Transform{kPortraitOrientation};
     mOutput.mState.orientation = kPortraitOrientation;
     mOutput.mState.needsFiltering = false;
@@ -3982,7 +3952,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 +3969,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 +4002,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 +4047,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/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index fd47e45..519cc57 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 std::optional<DisplayId> DEFAULT_DISPLAY_ID = {PhysicalDisplayId(123u)};
 const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
 
 using testing::_;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9af9cad..b53f88d 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) {
@@ -116,6 +117,10 @@
     }
 }
 
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+    mDeviceProductInfo = std::move(info);
+}
+
 uint32_t DisplayDevice::getPageFlipCount() const {
     return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
@@ -151,94 +156,80 @@
 }
 
 void DisplayDevice::setDisplaySize(int width, int height) {
-    mCompositionDisplay->setBounds(ui::Size(width, height));
+    mCompositionDisplay->setDisplaySpaceSize(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 w = displayBounds.width();
-    const int h = displayBounds.height();
+    const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
+    const int displayWidth = displayBounds.width();
+    const int displayHeight = displayBounds.height();
 
-    ui::Transform R;
-    if (const auto flags = ui::Transform::toRotationFlags(orientation);
-        flags != ui::Transform::ROT_INVALID) {
-        R.set(flags, w, h);
-    }
-
-    if (!frame.isValid()) {
+    if (!orientedDisplaySpaceRect.isValid()) {
         // the destination frame can be invalid if it has never been set,
         // in that case we assume the whole display frame.
-        frame = Rect(w, h);
+        orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
     }
 
-    if (viewport.isEmpty()) {
-        // viewport can be invalid if it has never been set, in that case
+    if (layerStackSpaceRect.isEmpty()) {
+        // layerStackSpaceRect 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
+        // It's also invalid to have an empty layerStackSpaceRect, so we handle that
         // case in the same way.
-        viewport = Rect(w, h);
-        if (R.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);
+        layerStackSpaceRect = Rect(displayWidth, displayHeight);
+        if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+            std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
         }
     }
 
-    ui::Transform TL, TP, S;
-    float src_width  = viewport.width();
-    float src_height = viewport.height();
-    float dst_width  = frame.width();
-    float dst_height = frame.height();
-    if (src_width != dst_width || src_height != dst_height) {
-        float sx = dst_width  / src_width;
-        float sy = dst_height / src_height;
-        S.set(sx, 0, 0, sy);
+    ui::Transform logicalTranslation, physicalTranslation, scale;
+    const float sourceWidth = layerStackSpaceRect.width();
+    const float sourceHeight = layerStackSpaceRect.height();
+    const float destWidth = orientedDisplaySpaceRect.width();
+    const float destHeight = orientedDisplaySpaceRect.height();
+    if (sourceWidth != destWidth || sourceHeight != destHeight) {
+        const float scaleX = destWidth / sourceWidth;
+        const float scaleY = destHeight / sourceHeight;
+        scale.set(scaleX, 0, 0, scaleY);
     }
 
-    float src_x = viewport.left;
-    float src_y = viewport.top;
-    float dst_x = frame.left;
-    float dst_y = frame.top;
-    TL.set(-src_x, -src_y);
-    TP.set(dst_x, dst_y);
+    const float sourceX = layerStackSpaceRect.left;
+    const float sourceY = layerStackSpaceRect.top;
+    const float destX = orientedDisplaySpaceRect.left;
+    const float destY = orientedDisplaySpaceRect.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) {
-            R.set(flags, w, h);
-        }
+    // 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;
+    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(transformOrientation);
+    ui::Transform rotation;
+    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
+        rotation.set(transformOrientationFlags, displayWidth, displayHeight);
     }
 
-    // The viewport and frame are both in the logical orientation.
+    // 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.
-    ui::Transform globalTransform = R * TP * S * TL;
+    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;
+    Rect displaySpaceRect = globalTransform.transform(layerStackSpaceRect);
+    if (displaySpaceRect.isEmpty()) {
+        displaySpaceRect = displayBounds;
     }
-
-    uint32_t transformOrientation;
+    // Make sure the displaySpaceRect is contained in the display bounds
+    displaySpaceRect.intersect(displayBounds, &displaySpaceRect);
 
     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);
+    getCompositionDisplay()->setProjection(globalTransform, transformOrientationFlags,
+                                           orientedDisplaySpaceRect, layerStackSpaceRect,
+                                           displaySpaceRect);
 }
 
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -267,6 +258,12 @@
     StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
     StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+    StringAppendF(&result, "deviceProductInfo=");
+    if (mDeviceProductInfo) {
+        mDeviceProductInfo->dump(result);
+    } else {
+        result.append("{}");
+    }
     getCompositionDisplay()->dump(result);
 }
 
@@ -285,7 +282,7 @@
 }
 
 const Rect& DisplayDevice::getBounds() const {
-    return mCompositionDisplay->getState().bounds;
+    return mCompositionDisplay->getState().displaySpace.bounds;
 }
 
 const Region& DisplayDevice::getUndefinedRegion() const {
@@ -304,16 +301,12 @@
     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 8c86153..35a8b62 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -40,7 +40,6 @@
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
-#include "RenderArea.h"
 #include "Scheduler/HwcStrongTypes.h"
 
 namespace android {
@@ -59,12 +58,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,10 +94,13 @@
 
     static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
 
+    ui::Transform::RotationFlags getTransformHint() const {
+        return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
+    }
+
     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;
 
@@ -132,6 +136,11 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
+    void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+    const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+        return mDeviceProductInfo;
+    }
+
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
@@ -178,14 +187,16 @@
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
+
+    std::optional<DeviceProductInfo> mDeviceProductInfo;
 };
 
 struct DisplayDeviceState {
     struct Physical {
-        DisplayId id;
+        PhysicalDisplayId id;
         DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-
+        std::optional<DeviceProductInfo> deviceProductInfo;
         bool operator==(const Physical& other) const {
             return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
         }
@@ -197,8 +208,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;
@@ -233,118 +244,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 97eeea2..a3f1b52 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1354,6 +1354,12 @@
     return error;
 }
 
+Error Composer::getClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+    mReader.takeClientTargetProperty(display, outClientTargetProperty);
+    return Error::NONE;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
@@ -1662,10 +1668,23 @@
     *state = data.presentOrValidateState;
 }
 
+void CommandReader::takeClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+    auto found = mReturnData.find(display);
+
+    // If not found, return the default values.
+    if (found == mReturnData.end()) {
+        outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+        outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+        return;
+    }
+
+    ReturnData& data = found->second;
+    *outClientTargetProperty = data.clientTargetProperty;
+}
+
 } // namespace impl
-
 } // namespace Hwc2
-
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index aa43f09..00ef782 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -240,6 +240,8 @@
                                                 const std::vector<uint8_t>& value) = 0;
     virtual V2_4::Error getLayerGenericMetadataKeys(
             std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+    virtual Error getClientTargetProperty(
+            Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
 };
 
 namespace impl {
@@ -282,6 +284,10 @@
     // Get what stage succeeded during PresentOrValidate: Present or Validate
     void takePresentOrValidateStage(Display display, uint32_t * state);
 
+    // Get the client target properties requested by hardware composer.
+    void takeClientTargetProperty(Display display,
+                                  IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
 private:
     void resetData();
 
@@ -479,6 +485,9 @@
                                         bool mandatory, const std::vector<uint8_t>& value) override;
     V2_4::Error getLayerGenericMetadataKeys(
             std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+    Error getClientTargetProperty(
+            Display display,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
 
 private:
 #if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..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) {
@@ -71,12 +66,8 @@
 
 DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
     DeviceProductInfo info;
-    std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
-    info.name[edid.displayName.size()] = '\0';
-
-    const auto productId = std::to_string(edid.productId);
-    std::copy(productId.begin(), productId.end(), info.productId.begin());
-    info.productId[productId.size()] = '\0';
+    info.name.assign(edid.displayName);
+    info.productId = std::to_string(edid.productId);
     info.manufacturerPnpId = edid.pnpId;
 
     constexpr uint8_t kModelYearFlag = 0xff;
@@ -99,8 +90,6 @@
     if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
         const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
         info.relativeAddress = {address.a, address.b, address.c, address.d};
-    } else {
-        info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
     }
     return info;
 }
@@ -132,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) {
@@ -158,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) &&
@@ -190,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) {
@@ -203,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)) {
@@ -238,7 +220,6 @@
 
     constexpr size_t kDescriptorCount = 4;
     constexpr size_t kDescriptorLength = 18;
-    static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
 
     for (size_t i = 0; i < kDescriptorCount; i++) {
         if (view.size() < kDescriptorLength) {
@@ -330,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(
@@ -346,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/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index fb82033..08559bd 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;
@@ -668,6 +672,11 @@
     return static_cast<Error>(intError);
 }
 
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
+    const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+    return static_cast<Error>(error);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
@@ -885,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});
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index f4c7fdd..12f26f6 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.
@@ -234,6 +232,8 @@
     [[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>*) const = 0;
     [[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
+            hal::ClientTargetProperty* outClientTargetProperty) = 0;
 };
 
 namespace impl {
@@ -305,6 +305,8 @@
     hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>* outSupportedContentTypes) const override;
     hal::Error setContentType(hal::ContentType) override;
+    hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
@@ -450,5 +452,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 038cec4..05ef599 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -284,7 +284,8 @@
     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) {
@@ -541,9 +542,12 @@
     error = hwcDisplay->getRequests(&displayRequests, &layerRequests);
     RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
 
-    outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
-                                               std::move(layerRequests)});
+    DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
+    error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
 
+    outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
+                                               std::move(layerRequests),
+                                               std::move(clientTargetProperty)});
     error = hwcDisplay->acceptChanges();
     RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
 
@@ -862,7 +866,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;
@@ -870,7 +875,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();
@@ -934,7 +940,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 b7e9f3a..698e3af 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -64,40 +64,43 @@
     const uint32_t id;
 };
 
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
 class HWComposer {
 public:
-    virtual ~HWComposer();
-
-    virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
-
-    virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, 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;
-
-    // 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;
-
-    virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0;
-
-    // Attempts to create a new layer on this display
-    virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
-    // Destroy a previously created layer
-    virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
-
     struct DeviceRequestedChanges {
         using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+        using ClientTargetProperty = hal::ClientTargetProperty;
         using DisplayRequests = hal::DisplayRequest;
         using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
 
         ChangedTypes changedTypes;
         DisplayRequests displayRequests;
         LayerRequests layerRequests;
+        ClientTargetProperty clientTargetProperty;
     };
 
+    virtual ~HWComposer();
+
+    virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
+
+    virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
+                                              DisplayIdentificationData* outData) const = 0;
+
+    virtual bool hasCapability(hal::Capability) const = 0;
+    virtual bool hasDisplayCapability(DisplayId, 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*) = 0;
+
+    virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
+
+    // Attempts to create a new layer on this display
+    virtual HWC2::Layer* createLayer(DisplayId) = 0;
+    // Destroy a previously created layer
+    virtual void destroyLayer(DisplayId, HWC2::Layer*) = 0;
+
     // Gets any required composition change requests from the HWC device.
     //
     // Note that frameUsesClientComposition must be set correctly based on
@@ -109,104 +112,97 @@
             DisplayId, 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(DisplayId, 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(DisplayId) = 0;
 
     // set power mode
-    virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0;
+    virtual status_t setPowerMode(DisplayId, 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(DisplayId, const mat4& transform) = 0;
 
     // reset state when an external, non-virtual display is disconnected
-    virtual void disconnectDisplay(DisplayId displayId) = 0;
+    virtual void disconnectDisplay(DisplayId) = 0;
 
     // get the present fence received from the last call to present.
-    virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
+    virtual sp<Fence> getPresentFence(DisplayId) 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(DisplayId, 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(DisplayId, 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(DisplayId) = 0;
 
     // Fetches the HDR capabilities of the given display
-    virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
+    virtual status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) = 0;
 
-    virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
+    virtual int32_t getSupportedPerFrameMetadata(DisplayId) 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(DisplayId, ui::ColorMode) const = 0;
 
-    virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+    virtual mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) = 0;
 
     // Returns the attributes of the color sampling engine.
-    virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
-                                                           ui::PixelFormat* outFormat,
+    virtual status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
                                                            ui::Dataspace* outDataspace,
                                                            uint8_t* outComponentMask) = 0;
-    virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+    virtual status_t setDisplayContentSamplingEnabled(DisplayId, 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(DisplayId, 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(DisplayId, 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;
+    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    virtual void setVsyncEnabled(DisplayId, hal::Vsync enabled) = 0;
 
-    virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
-    virtual bool isConnected(DisplayId displayId) const = 0;
+    virtual nsecs_t getRefreshTimestamp(DisplayId) const = 0;
+    virtual bool isConnected(DisplayId) 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;
+            DisplayId) const = 0;
 
-    virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
-            DisplayId displayId) const = 0;
-    virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
+    virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const = 0;
+    virtual int getActiveConfigIndex(DisplayId) const = 0;
 
-    virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
+    virtual std::vector<ui::ColorMode> getColorModes(DisplayId) const = 0;
 
-    virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
-                                        ui::RenderIntent renderIntent) = 0;
+    virtual status_t setActiveColorMode(DisplayId, ui::ColorMode mode, ui::RenderIntent) = 0;
 
     virtual bool isUsingVrComposer() const = 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 bool isVsyncPeriodSwitchSupported(DisplayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(DisplayId) const = 0;
     virtual status_t setActiveConfigWithConstraints(
-            DisplayId displayId, size_t configId,
-            const hal::VsyncPeriodChangeConstraints& constraints,
+            DisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
-    virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+    virtual status_t setAutoLowLatencyMode(DisplayId, 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;
+            DisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(DisplayId, hal::ContentType) = 0;
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
@@ -219,8 +215,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 {
@@ -234,118 +230,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(DisplayId, 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(DisplayId) override;
     // Destroy a previously created layer
-    void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+    void destroyLayer(DisplayId, HWC2::Layer*) override;
 
     status_t getDeviceCompositionChanges(
             DisplayId, 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(DisplayId, 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(DisplayId) override;
 
     // set power mode
-    status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override;
+    status_t setPowerMode(DisplayId, 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(DisplayId, const mat4& transform) override;
 
     // reset state when an external, non-virtual display is disconnected
-    void disconnectDisplay(DisplayId displayId) override;
+    void disconnectDisplay(DisplayId) override;
 
     // get the present fence received from the last call to present.
-    sp<Fence> getPresentFence(DisplayId displayId) const override;
+    sp<Fence> getPresentFence(DisplayId) const override;
 
     // Get last release fence for the given layer
-    sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+    sp<Fence> getLayerReleaseFence(DisplayId, 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(DisplayId, 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(DisplayId) override;
 
     // Fetches the HDR capabilities of the given display
-    status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+    status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) override;
 
-    int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+    int32_t getSupportedPerFrameMetadata(DisplayId) 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(DisplayId, ui::ColorMode) const override;
 
-    mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+    mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) override;
 
     // Returns the attributes of the color sampling engine.
-    status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+    status_t getDisplayedContentSamplingAttributes(DisplayId, 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(DisplayId, bool enabled, uint8_t componentMask,
+                                              uint64_t maxFrames) override;
+    status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(DisplayId, float brightness) override;
 
     // 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.
-    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 onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    void setVsyncEnabled(DisplayId, hal::Vsync enabled) override;
 
-    nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
-    bool isConnected(DisplayId displayId) const override;
+    nsecs_t getRefreshTimestamp(DisplayId) const override;
+    bool isConnected(DisplayId) 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;
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(DisplayId) 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(DisplayId) const override;
+    int getActiveConfigIndex(DisplayId) const override;
 
-    std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+    std::vector<ui::ColorMode> getColorModes(DisplayId) const override;
 
-    status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
-                                ui::RenderIntent renderIntent) override;
+    status_t setActiveColorMode(DisplayId, ui::ColorMode, ui::RenderIntent) override;
 
     bool isUsingVrComposer() const 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,
+    bool isVsyncPeriodSwitchSupported(DisplayId) const override;
+    nsecs_t getDisplayVsyncPeriod(DisplayId) const override;
+    status_t setActiveConfigWithConstraints(DisplayId, 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(DisplayId, bool) override;
+    status_t getSupportedContentTypes(DisplayId, std::vector<hal::ContentType>*) override;
+    status_t setContentType(DisplayId, hal::ContentType) override;
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
 
@@ -362,17 +352,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();
@@ -407,7 +396,7 @@
     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;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 66ee425..bb2888e 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -53,6 +53,7 @@
 using Connection = IComposerCallback::Connection;
 using ContentType = IComposerClient::ContentType;
 using Capability = IComposer::Capability;
+using ClientTargetProperty = IComposerClient::ClientTargetProperty;
 using DisplayCapability = IComposerClient::DisplayCapability;
 using DisplayRequest = IComposerClient::DisplayRequest;
 using DisplayType = IComposerClient::DisplayType;
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/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
new file mode 100644
index 0000000..9a6b328
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -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.
+ */
+
+#include "DisplayRenderArea.h"
+#include "DisplayDevice.h"
+
+namespace android {
+namespace {
+
+RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
+                                                 const DisplayDevice& display) {
+    if (!useIdentityTransform) {
+        return RenderArea::RotationFlags::ROT_0;
+    }
+
+    const ui::Rotation orientation = display.getPhysicalOrientation() + display.getOrientation();
+    return ui::Transform::toRotationFlags(orientation);
+}
+
+} // 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..6ba4c43
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+    name: "libframetimeline",
+    defaults: ["surfaceflinger_defaults"],
+    srcs: [
+        "FrameTimeline.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libgui",
+        "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..c95440a
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 <cinttypes>
+
+namespace android::frametimeline::impl {
+
+using base::StringAppendF;
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+    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(const std::string& layerName, PredictionState predictionState,
+                           frametimeline::TimelineItem&& predictions)
+      : mLayerName(layerName),
+        mPresentState(PresentState::Unknown),
+        mPredictionState(predictionState),
+        mPredictions(predictions),
+        mActuals({0, 0, 0}),
+        mActualQueueTime(0) {}
+
+void SurfaceFrame::setPresentState(PresentState state) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentState = state;
+}
+
+PredictionState SurfaceFrame::getPredictionState() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mPredictionState;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mPresentState;
+}
+
+TimelineItem SurfaceFrame::getActuals() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mActuals;
+}
+
+void SurfaceFrame::setActuals(frametimeline::TimelineItem&& actuals) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals = actuals;
+}
+
+void SurfaceFrame::setPresentTime(nsecs_t presentTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.presentTime = presentTime;
+}
+
+void SurfaceFrame::dump(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
+    StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
+    StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
+    StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime);
+    StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime);
+    StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime);
+    StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime);
+}
+
+FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {}
+
+FrameTimeline::DisplayFrame::DisplayFrame()
+      : surfaceFlingerPredictions(TimelineItem()),
+        surfaceFlingerActuals(TimelineItem()),
+        predictionState(PredictionState::None) {
+    this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+        const std::string& layerName, std::optional<int64_t> token) {
+    if (!token) {
+        return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
+                                                    TimelineItem());
+    }
+    std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
+    if (predictions) {
+        return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+                                                    std::move(*predictions));
+    }
+    return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
+                                                TimelineItem());
+}
+
+void FrameTimeline::addSurfaceFrame(
+        std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
+        SurfaceFrame::PresentState state) {
+    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) {
+    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->surfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+                                 const std::shared_ptr<FenceTime>& presentFence) {
+    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) {
+            auto& displayFrame = pendingPresentFence.second;
+            displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+            for (auto& surfaceFrame : displayFrame->surfaceFrames) {
+                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
+                    // Only presented SurfaceFrames need to be updated
+                    surfaceFrame->setPresentTime(signalTime);
+                }
+            }
+        }
+
+        mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+        --i;
+    }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+    while (mDisplayFrames.size() >= kMaxDisplayFrames) {
+        // 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>();
+}
+
+void FrameTimeline::dump(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+    for (const auto& displayFrame : mDisplayFrames) {
+        StringAppendF(&result, "---Display Frame---\n");
+        StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerPredictions.startTime);
+        StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerActuals.startTime);
+        StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerPredictions.endTime);
+        StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerActuals.endTime);
+        StringAppendF(&result, "Predicted Present time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerPredictions.presentTime);
+        StringAppendF(&result, "Actual Present time : %" PRId64 "\n",
+                      displayFrame->surfaceFlingerActuals.presentTime);
+        for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) {
+            StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i);
+            displayFrame->surfaceFrames[i]->dump(result);
+        }
+    }
+}
+
+} // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
new file mode 100644
index 0000000..291e30e
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -0,0 +1,232 @@
+/*
+ * 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 <deque>
+#include <mutex>
+
+#include <ui/FenceTime.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+namespace android::frametimeline {
+
+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;
+};
+
+/*
+ * 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);
+};
+
+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() = 0;
+    virtual TimelineItem getActuals() = 0;
+    virtual PresentState getPresentState() = 0;
+    virtual PredictionState getPredictionState() = 0;
+
+    virtual void setPresentState(PresentState state) = 0;
+    virtual void setActuals(TimelineItem&& actuals) = 0;
+
+    // There is no prediction for Queue time and it is not a part of TimelineItem. Set it
+    // separately.
+    virtual void setActualQueueTime(nsecs_t actualQueueTime) = 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.
+    virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
+            const std::string& layerName, 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;
+};
+
+namespace impl {
+
+using namespace std::chrono_literals;
+
+class TokenManager : public android::frametimeline::TokenManager {
+public:
+    TokenManager() : mCurrentToken(0) {}
+    ~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(const std::string& layerName, PredictionState predictionState,
+                 TimelineItem&& predictions);
+    ~SurfaceFrame() = default;
+
+    TimelineItem getPredictions() override { return mPredictions; };
+    TimelineItem getActuals() override;
+    PresentState getPresentState() override;
+    PredictionState getPredictionState() override;
+    void setActuals(TimelineItem&& actuals) override;
+    void setActualQueueTime(nsecs_t actualQueueTime) override {
+        mActualQueueTime = actualQueueTime;
+    };
+    void setPresentState(PresentState state) override;
+    void setPresentTime(nsecs_t presentTime);
+    void dump(std::string& result);
+
+private:
+    const std::string mLayerName;
+    PresentState mPresentState GUARDED_BY(mMutex);
+    PredictionState mPredictionState GUARDED_BY(mMutex);
+    const TimelineItem mPredictions;
+    TimelineItem mActuals GUARDED_BY(mMutex);
+    nsecs_t mActualQueueTime;
+    std::mutex mMutex;
+};
+
+class FrameTimeline : public android::frametimeline::FrameTimeline {
+public:
+    FrameTimeline();
+    ~FrameTimeline() = default;
+
+    frametimeline::TokenManager& getTokenManager() override { return mTokenManager; }
+    std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
+            const std::string& layerName, 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 dump(std::string& result);
+
+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;
+    };
+
+    void flushPendingPresentFences() REQUIRES(mMutex);
+    void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+
+    // 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;
+    static constexpr uint32_t kMaxDisplayFrames = 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;
+};
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index b986f38..2dfb9a9 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -25,7 +25,7 @@
 #include "FrameTracer.h"
 
 #include <android-base/stringprintf.h>
-#include <perfetto/trace/clock_snapshot.pbzero.h>
+#include <perfetto/common/builtin_clock.pbzero.h>
 
 #include <algorithm>
 #include <mutex>
@@ -34,7 +34,6 @@
 
 namespace android {
 
-using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
 void FrameTracer::initialize() {
     std::call_once(mInitializationFlag, [this]() {
         perfetto::TracingInitArgs args;
@@ -136,7 +135,7 @@
                               uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
                               FrameEvent::BufferEventType type, nsecs_t duration) {
     auto packet = ctx.NewTracePacket();
-    packet->set_timestamp_clock_id(Clock::MONOTONIC);
+    packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
     packet->set_timestamp(timestamp);
     auto* event = packet->set_graphics_frame_event()->set_buffer_event();
     event->set_buffer_id(static_cast<uint32_t>(bufferID));
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 25929ed1..eced6bd 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>
@@ -67,12 +68,14 @@
 #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;
 
 std::atomic<int32_t> Layer::sSequence{1};
 
@@ -80,7 +83,8 @@
       : 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;
@@ -119,6 +123,13 @@
     mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
 
+    if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+        // Set an invalid color so there is no color fill.
+        mCurrentState.color.r = -1.0_hf;
+        mCurrentState.color.g = -1.0_hf;
+        mCurrentState.color.b = -1.0_hf;
+    }
+
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
 
@@ -129,6 +140,14 @@
 
     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);
+    } else {
+        // A create layer request from a non system request cannot specify the owner uid
+        mOwnerUid = mCallingUid;
+    }
 }
 
 void Layer::onFirstRef() {
@@ -145,11 +164,10 @@
     mFlinger->onLayerDestroyed(this);
 }
 
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client,
-                                     std::string name, uint32_t w, uint32_t h, uint32_t flags,
-                                     LayerMetadata metadata)
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+                                     uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
       : flinger(flinger),
-        client(client),
+        client(std::move(client)),
         name(std::move(name)),
         w(w),
         h(h),
@@ -223,13 +241,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() {
@@ -592,7 +620,7 @@
 // ---------------------------------------------------------------------------
 
 std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+        compositionengine::LayerFE::ClientCompositionTargetSettings& /* targetSettings */) {
     if (!getCompositionState()) {
         return {};
     }
@@ -602,11 +630,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();
@@ -623,9 +647,9 @@
 }
 
 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 {};
     }
@@ -718,9 +742,8 @@
     return {*shadowSettings};
 }
 
-Hwc2::IComposerClient::Composition Layer::getCompositionType(
-        const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
+Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display);
     if (outputLayer == nullptr) {
         return Hwc2::IComposerClient::Composition::INVALID;
     }
@@ -731,12 +754,6 @@
     }
 }
 
-bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().clearClientTarget;
-}
-
 bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
     if (point->getFrameNumber() <= mCurrentFrameNumber) {
         // Don't bother with a SyncPoint, since we've already latched the
@@ -856,11 +873,13 @@
         }
     }
 
-    // If we still have pending updates, wake SurfaceFlinger back up and point
-    // it at this layer so we can process them
+    // If we still have pending updates, we need to ensure SurfaceFlinger
+    // 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.
     if (!mPendingStates.empty()) {
         setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTransactionFlags(eTraversalNeeded);
+        mFlinger->setTraversalNeeded();
     }
 
     mCurrentState.modified = false;
@@ -982,8 +1001,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) {
@@ -1314,6 +1332,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) {
@@ -1361,8 +1383,15 @@
     // First traverse the tree and count how many layers has votes
     int layersWithVote = 0;
     traverseTree([&layersWithVote](Layer* layer) {
-        if (layer->mCurrentState.frameRate.rate > 0 ||
-            layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+        const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+                layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
+        const auto layerVotedWithNoVote =
+                layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+
+        // We do not count layers that are ExactOrMultiple for the same reason
+        // we are allowing touch boost for those layers. See
+        // RefreshRateConfigs::getBestRefreshRate for more details.
+        if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
             layersWithVote++;
         }
     });
@@ -1393,7 +1422,8 @@
     }
 
     // Activate the layer in Scheduler's LayerHistory
-    mFlinger->mScheduler->recordLayerHistory(this, systemTime());
+    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                             LayerHistory::LayerUpdateType::SetFrameRate);
 
     mCurrentState.sequence++;
     mCurrentState.frameRate = frameRate;
@@ -1422,6 +1452,13 @@
 
 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;
     // We don't set eTransactionNeeded, because just receiving a deferral
@@ -1472,19 +1509,11 @@
     return usage;
 }
 
-void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
-    ui::Transform::RotationFlags transformHint = ui::Transform::ROT_0;
-    // Disable setting transform hint if the debug flag is set.
-    if (!mFlinger->mDebugDisableTransformHint) {
-        // The transform hint is used to improve performance, but we can
-        // only have a single transform hint, it cannot
-        // apply to all displays.
-        const ui::Transform& planeTransform = display->getTransform();
-        transformHint = static_cast<ui::Transform::RotationFlags>(planeTransform.getOrientation());
-        if (transformHint & ui::Transform::ROT_INVALID) {
-            transformHint = ui::Transform::ROT_0;
-        }
+void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
+    if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
+        transformHint = ui::Transform::ROT_0;
     }
+
     setTransformHint(transformHint);
 }
 
@@ -1493,18 +1522,18 @@
 // ----------------------------------------------------------------------------
 
 // TODO(marissaw): add new layer state info to layer debugging
-LayerDebugInfo Layer::getLayerDebugInfo() const {
+LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
     using namespace std::string_literals;
 
     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;
 
-    info.mVisibleRegion = debugGetVisibleRegionOnDefaultDisplay();
+    info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
     info.mX = ds.active_legacy.transform.tx();
@@ -1547,7 +1576,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 | ");
@@ -1555,12 +1584,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) {
@@ -1574,8 +1603,8 @@
     }
 }
 
-void Layer::miniDump(std::string& result, const sp<DisplayDevice>& displayDevice) const {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
+void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display);
     if (!outputLayer) {
         return;
     }
@@ -1602,7 +1631,7 @@
         StringAppendF(&result, "  %10d | ", layerState.z);
     }
     StringAppendF(&result, "  %10d | ", mWindowType);
-    StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
+    StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
     StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
     const Rect& frame = outputLayerState.displayFrame;
     StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
@@ -1611,17 +1640,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 {
@@ -1648,8 +1680,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() {
@@ -1802,7 +1834,7 @@
         onRemovedFromCurrentState();
     }
 
-    if (callSetTransactionFlags || attachChildren()) {
+    if (attachChildren() || callSetTransactionFlags) {
         setTransactionFlags(eTransactionNeeded);
     }
     return true;
@@ -1830,9 +1862,9 @@
         if (client != nullptr && parentClient != client) {
             if (child->mLayerDetached) {
                 child->mLayerDetached = false;
+                child->attachChildren();
                 changed = true;
             }
-            changed |= child->attachChildren();
         }
     }
 
@@ -2120,7 +2152,9 @@
             // but a transform matrix can define horizontal and vertical scales.
             // Let's take the average between both of them and pass into the shader, practically we
             // never do this type of transformation on windows anyway.
-            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
+            auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
+            parentState.radius *= (scaleX + scaleY) / 2.0f;
             return parentState;
         }
     }
@@ -2130,12 +2164,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;
@@ -2175,27 +2209,28 @@
 }
 
 LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
-                                const sp<const DisplayDevice>& device) const {
+                                const DisplayDevice* display) {
     LayerProto* layerProto = layersProto.add_layers();
-    writeToProtoDrawingState(layerProto, traceFlags);
+    writeToProtoDrawingState(layerProto, traceFlags, display);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
     if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
         // Only populate for the primary display.
-        if (device) {
-            const Hwc2::IComposerClient::Composition compositionType = getCompositionType(device);
+        if (display) {
+            const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
             layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
         }
     }
 
     for (const sp<Layer>& layer : mDrawingChildren) {
-        layer->writeToProto(layersProto, traceFlags, device);
+        layer->writeToProto(layersProto, traceFlags, display);
     }
 
     return layerProto;
 }
 
-void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
+void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
+                                     const DisplayDevice* display) {
     ui::Transform transform = getTransform();
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
@@ -2224,12 +2259,13 @@
         layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
         layerInfo->set_corner_radius(getRoundedCornerState().radius);
+        layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
         LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
         LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
         if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
-            LayerProtoHelper::writeToProto(debugGetVisibleRegionOnDefaultDisplay(),
+            LayerProtoHelper::writeToProto(getVisibleRegion(display),
                                            [&]() { return layerInfo->mutable_visible_region(); });
         }
         LayerProtoHelper::writeToProto(surfaceDamageRegion,
@@ -2251,7 +2287,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;
@@ -2318,10 +2354,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(); });
     }
 
@@ -2338,6 +2383,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;
 
@@ -2346,17 +2400,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.
@@ -2366,6 +2411,15 @@
     if (!layerBounds.isValid()) {
         layerBounds = getCroppedBufferSize(getDrawingState());
     }
+
+    const float xScale = t.getScaleX();
+    const float yScale = t.getScaleY();
+    if (xScale != 1.0f || yScale != 1.0f) {
+        info.touchableRegion.scaleSelf(xScale, yScale);
+        xSurfaceInset = std::round(xSurfaceInset * xScale);
+        ySurfaceInset = std::round(ySurfaceInset * yScale);
+    }
+
     layerBounds = t.transform(layerBounds);
 
     // clamp inset to layer bounds
@@ -2380,10 +2434,22 @@
     info.frameRight = layerBounds.right;
     info.frameBottom = layerBounds.bottom;
 
+    ui::Transform inputTransform(t);
+    inputTransform.set(layerBounds.left, layerBounds.top);
+    info.transform = inputTransform.inverse();
+
     // 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();
+    // 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();
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
@@ -2419,7 +2485,7 @@
     return mDrawingParent.promote()->getClonedRoot();
 }
 
-bool Layer::hasInput() const {
+bool Layer::hasInputInfo() const {
     return mDrawingState.inputInfo.token != nullptr;
 }
 
@@ -2428,22 +2494,14 @@
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
-        const sp<const DisplayDevice>& display) const {
+        const DisplayDevice* display) const {
+    if (!display) return nullptr;
     return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
 }
 
-Region Layer::debugGetVisibleRegionOnDefaultDisplay() const {
-    sp<DisplayDevice> displayDevice = mFlinger->getDefaultDisplayDeviceLocked();
-    if (displayDevice == nullptr) {
-        return {};
-    }
-
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (outputLayer == nullptr) {
-        return {};
-    }
-
-    return outputLayer->getState().visibleRegion;
+Region Layer::getVisibleRegion(const DisplayDevice* display) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    return outputLayer ? outputLayer->getState().visibleRegion : Region();
 }
 
 void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
@@ -2536,7 +2594,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) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 224ea19..913f13a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_LAYER_H
-#define ANDROID_LAYER_H
+#pragma once
 
 #include <compositionengine/LayerFE.h>
 #include <gui/BufferQueue.h>
@@ -56,8 +55,6 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
 class Client;
 class Colorizer;
 class DisplayDevice;
@@ -74,11 +71,9 @@
 class SurfaceInterceptor;
 }
 
-// ---------------------------------------------------------------------------
-
 struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client, std::string name,
-                      uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata);
+    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
+                      uint32_t flags, LayerMetadata);
 
     SurfaceFlinger* flinger;
     const sp<Client> client;
@@ -87,25 +82,26 @@
     uint32_t h;
     uint32_t flags;
     LayerMetadata metadata;
+
     pid_t callingPid;
     uid_t callingUid;
-    sp<const DisplayDevice> displayDevice;
     uint32_t textureName;
 };
 
 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,
@@ -273,17 +269,56 @@
         ui::Transform::RotationFlags fixedTransformHint;
     };
 
+    /*
+     * 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
@@ -356,13 +391,9 @@
     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;
@@ -395,22 +426,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();
 
@@ -420,49 +442,6 @@
     virtual void useSurfaceDamage() {}
     virtual void useEmptyDamage() {}
 
-    uint32_t getTransactionFlags() const { return mTransactionFlags; }
-    uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags);
-
-    // Deprecated, please use compositionengine::Output::belongsInOutput()
-    // instead.
-    // TODO(lpique): Move the remaining callers (screencap) to the new function.
-    bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
-        return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
-    }
-
-    FloatRect getBounds(const Region& activeTransparentRegion) const;
-    FloatRect getBounds() const;
-
-    // Compute bounds for the layer and cache the results.
-    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
-
-    // Returns the buffer scale transform if a scaling mode is set.
-    ui::Transform getBufferScaleTransform() const;
-
-    // Get effective layer transform, taking into account all its parent transform with any
-    // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
-    ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
-    // Returns the bounds of the layer without any buffer scaling.
-    FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
-    int32_t getSequence() const { return sequence; }
-
-    // For tracing.
-    // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
-    // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
-    // creates its tracks by buffer id and has no way of associating a buffer back to the process
-    // that created it, the current implementation is only sufficient for cases where a buffer is
-    // 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
      *
@@ -473,25 +452,6 @@
     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.
-     */
-    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
-     * policy, but it still can not be visible.
-     */
-    bool isHiddenByPolicy() const;
-
-    /*
      * Returns whether this layer can receive input.
      */
     virtual bool canReceiveInput() const;
@@ -517,23 +477,6 @@
     // 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 = SurfaceTracing::TRACE_ALL,
-                             const sp<const DisplayDevice>& device = nullptr) const;
-
-    // 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 = SurfaceTracing::TRACE_ALL) const;
-    // 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;
-
     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; }
@@ -544,7 +487,8 @@
         return s.activeTransparentRegion_legacy;
     }
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
-    virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; }
+    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,
@@ -554,83 +498,26 @@
     // 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 sp<const DisplayDevice>&,
-                                              const ui::Transform&) const {
+    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
         return false;
     }
 
-    // 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;
-
-    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; }
 
-    Hwc2::IComposerClient::Composition getCompositionType(
-            const sp<const DisplayDevice>& display) const;
-    bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-
     virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-    virtual void setTransformHint(ui::Transform::RotationFlags /*transformHint*/) const {}
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
+    virtual bool onPostComposition(const DisplayDevice*,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming& /*compositorTiming*/) {
+                                   const CompositorTiming&) {
         return false;
     }
 
@@ -639,11 +526,6 @@
 
     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.
-     */
-    uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
      * latchBuffer - called each time the screen is redrawn and returns whether
@@ -661,32 +543,6 @@
     virtual void latchAndReleaseBuffer() {}
 
     /*
-     * Remove relative z for the layer if its relative parent is not part of the
-     * provided layer tree.
-     */
-    void removeRelativeZ(const std::vector<Layer*>& layersInTree);
-
-    /*
-     * Remove from current state and mark for removal.
-     */
-    void removeFromCurrentState();
-
-    /*
-     * called with the state lock from a binder thread when the layer is
-     * removed from the current list to the pending removal list
-     */
-    void onRemovedFromCurrentState();
-
-    /*
-     * Called when the layer is added back to the current state list.
-     */
-    void addToCurrentState();
-
-    // Updates the transform hint in our SurfaceFlingerConsumer to match
-    // the current orientation of the display device.
-    void updateTransformHint(const sp<const DisplayDevice>& display) const;
-
-    /*
      * returns the rectangle that crops the content of the layer and scales it
      * to the layer's size.
      */
@@ -708,33 +564,192 @@
 
     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);
+
+    // Deprecated, please use compositionengine::Output::belongsInOutput()
+    // instead.
+    // TODO(lpique): Move the remaining callers (screencap) to the new function.
+    bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
+
+    FloatRect getBounds(const Region& activeTransparentRegion) const;
+    FloatRect getBounds() const;
+
+    // Compute bounds for the layer and cache the results.
+    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
+
+    // Returns the buffer scale transform if a scaling mode is set.
+    ui::Transform getBufferScaleTransform() const;
+
+    // Get effective layer transform, taking into account all its parent transform with any
+    // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
+    ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
+
+    // Returns the bounds of the layer without any buffer scaling.
+    FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
+
+    int32_t getSequence() const { return sequence; }
+
+    // For tracing.
+    // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
+    // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
+    // creates its tracks by buffer id and has no way of associating a buffer back to the process
+    // that created it, the current implementation is only sufficient for cases where a buffer is
+    // only used within a single layer.
+    uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
+
+    /*
+     * isSecure - true if this surface is secure, that is if it prevents
+     * screenshots or VNC servers.
+     */
+    bool isSecure() const;
+
+    /*
+     * 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
+     * policy, but it still can not be visible.
+     */
+    bool isHiddenByPolicy() const;
+
+    bool isRemovedFromCurrentState() 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*);
+    // 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,
+                                 uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
+
+    InputWindowInfo::Type getWindowType() const { return mWindowType; }
+
+    void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+    bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
+
+    void updateMirrorInfo();
+
+    /*
+     * doTransaction - process the transaction. This is a good place to figure
+     * out which attributes of the surface have changed.
+     */
+    uint32_t doTransaction(uint32_t transactionFlags);
+
+    /*
+     * Remove relative z for the layer if its relative parent is not part of the
+     * provided layer tree.
+     */
+    void removeRelativeZ(const std::vector<Layer*>& layersInTree);
+
+    /*
+     * Remove from current state and mark for removal.
+     */
+    void removeFromCurrentState();
+
+    /*
+     * called with the state lock from a binder thread when the layer is
+     * removed from the current list to the pending removal list
+     */
+    void onRemovedFromCurrentState();
+
+    /*
+     * Called when the layer is added back to the current state list.
+     */
+    void addToCurrentState();
+
+    /*
+     * Sets display transform hint on BufferLayerConsumer.
+     */
+    void updateTransformHint(ui::Transform::RotationFlags);
+
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
 
-    LayerDebugInfo getLayerDebugInfo() const;
+    LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
 
-    /* always call base class first */
-    static void miniDumpHeader(std::string& result);
-    void miniDump(std::string& result, const sp<DisplayDevice>& display) const;
+    void miniDump(std::string& result, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
     void dumpFrameEvents(std::string& result);
     void dumpCallingUidPid(std::string& result) const;
     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
@@ -751,14 +766,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
@@ -768,17 +776,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;
 
@@ -790,7 +796,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);
@@ -804,28 +810,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;
-    }
-
-    compositionengine::OutputLayer* findOutputLayerForDisplay(
-            const sp<const DisplayDevice>& display) const;
-
-    Region debugGetVisibleRegionOnDefaultDisplay() const;
+    int32_t getZ(LayerVector::StateSet) const;
 
     /**
      * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
@@ -835,53 +820,41 @@
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
-    bool setFrameRate(FrameRate frameRate);
-    virtual FrameRate getFrameRateForLayerTree() const;
-    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
+    bool setFrameRate(FrameRate);
+
+    // 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; }
+
+    // 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)
@@ -909,6 +882,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.
@@ -923,48 +953,9 @@
     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:
-    // -----------------------------------------------------------------------
-    bool usingRelativeZ(LayerVector::StateSet stateSet) const;
+    // constant
+    sp<SurfaceFlinger> mFlinger;
 
     bool mPremultipliedAlpha{true};
     const std::string mName;
@@ -1032,25 +1023,42 @@
     bool mChildrenChanged{false};
 
     // Window types from WindowManager.LayoutParams
-    const int mWindowType;
+    const InputWindowInfo::Type mWindowType;
 
 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;
+
     /**
      * 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.
@@ -1067,17 +1075,17 @@
     // 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;
     uid_t mCallingUid;
 
+    // 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 current layer is a clone of mClonedFrom. This means that this layer will update it's
     // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
     // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1088,24 +1096,6 @@
     // final shadow radius for this layer. If a shadow is specified for a layer, then effective
     // 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();
 };
 
 } // namespace android
-
-#define RETURN_IF_NO_HWC_LAYER(displayDevice, ...)                                     \
-    do {                                                                               \
-        if (!hasHwcLayer(displayDevice)) {                                             \
-            ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \
-                  __FUNCTION__, displayDevice->getDebugName().c_str());                \
-            return __VA_ARGS__;                                                        \
-        }                                                                              \
-    } while (false)
-
-#endif // ANDROID_LAYER_H
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/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/OWNERS b/services/surfaceflinger/OWNERS
index f2bc65d..c5a4689 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -3,8 +3,8 @@
 alecmouri@google.com
 chaviw@google.com
 lpy@google.com
-marissaw@google.com
 racarr@google.com
 steventhomas@google.com
 stoza@google.com
 vhau@google.com
+vishnun@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f602412..d8477e7 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -106,41 +106,71 @@
         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();
 }
@@ -176,7 +206,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 +226,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,7 +239,19 @@
 }
 
 void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
-    auto buffer = mBufferCache[refreshRate.getFps()];
+    mCurrentFps = refreshRate.getFps();
+    auto buffer = mBufferCache[*mCurrentFps][mFrame];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+    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, {});
 
     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..a2fc692 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -35,8 +35,10 @@
 #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 +61,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 +73,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 +87,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 +118,7 @@
         if (mVsyncListening) return;
 
         mPhaseIntervalSetting = Phase::ZERO;
-        mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
-                                                         mLastCallbackTime);
+        mVSyncSource->setVSyncEnabled(true);
         mVsyncListening = true;
     }
 
@@ -126,23 +131,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 +157,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 +173,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 +187,7 @@
 
 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
       : RegionSamplingThread(flinger, scheduler,
-                             TimingTunables{defaultRegionSamplingOffset,
+                             TimingTunables{defaultRegionSamplingWorkDuration,
                                             defaultRegionSamplingPeriod,
                                             defaultRegionSamplingTimerTimeout}) {}
 
@@ -243,7 +249,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 +342,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 +364,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 +384,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 +415,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 +433,39 @@
                   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(),
+        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);
+    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;
+    };
+
+    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 +476,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..f513535 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -74,18 +74,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 +98,13 @@
 }
 
 DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
-                                      uint32_t count, nsecs_t expectedVSyncTimestamp) {
+                                      uint32_t count, nsecs_t expectedVSyncTimestamp,
+                                      nsecs_t deadlineTimestamp) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
     event.vsync.count = count;
     event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+    event.vsync.deadlineTimestamp = deadlineTimestamp;
     return event;
 }
 
@@ -139,6 +139,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 +153,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);
@@ -206,9 +202,10 @@
     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(
@@ -269,28 +266,6 @@
     }
 }
 
-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 +286,13 @@
     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);
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       expectedVSyncTimestamp));
+                                       expectedVSyncTimestamp, deadlineTimestamp));
     mCondition.notify_all();
 }
 
@@ -366,9 +342,6 @@
                         mInterceptVSyncsCallback(event->header.timestamp);
                     }
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-                    mLastConfigChangeEvent = *event;
-                    break;
             }
         }
 
@@ -381,10 +354,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 +414,11 @@
 
                 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();
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
-                                                   ++mVSyncState->count, expectedVSyncTime));
+                                                   ++mVSyncState->count, expectedVSyncTime,
+                                                   deadlineTimestamp));
             }
         }
     }
@@ -460,9 +431,7 @@
             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:
@@ -508,8 +477,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..fa1ca64 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -57,7 +57,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,7 +66,8 @@
     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;
 };
@@ -81,19 +83,13 @@
     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;
 
 private:
     virtual void onFirstRef();
@@ -122,17 +118,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;
@@ -153,7 +146,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 +160,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,7 +181,8 @@
             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);
 
@@ -201,7 +195,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 8958d9a..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>
@@ -79,7 +80,8 @@
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                          LayerUpdateType /*updateType*/) {
     std::lock_guard lock(mLock);
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -108,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) {
@@ -121,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)) {
@@ -179,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 a1ae35c..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>
 
@@ -52,8 +53,18 @@
     // Sets the display size. Client is responsible for synchronization.
     virtual void setDisplayArea(uint32_t displayArea) = 0;
 
+    // Sets whether a config change is pending to be applied
+    virtual void setConfigChangePending(bool pending) = 0;
+
+    // Represents which layer activity is recorded
+    enum class LayerUpdateType {
+        Buffer,       // a new buffer queued
+        AnimationTX,  // a new transaction with eAnimation flag set
+        SetFrameRate, // setFrameRate API was called
+    };
+
     // Marks the layer as active, and records the given state to its history.
-    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
+    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
 
     using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
@@ -61,6 +72,7 @@
     virtual Summary summarize(nsecs_t now) = 0;
 
     virtual void clear() = 0;
+    virtual std::string dump() const = 0;
 };
 
 namespace impl {
@@ -78,13 +90,16 @@
 
     void setDisplayArea(uint32_t /*displayArea*/) override {}
 
+    void setConfigChangePending(bool /*pending*/) override {}
+
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
     android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
 
     void clear() override;
+    std::string dump() const override;
 
 private:
     friend class android::scheduler::LayerHistoryTest;
@@ -124,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.
@@ -134,13 +149,16 @@
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
 
+    void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
+
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
     android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
 
     void clear() override;
+    std::string dump() const override;
 
 private:
     friend android::scheduler::LayerHistoryTestV2;
@@ -178,6 +196,9 @@
 
     // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
     const bool mUseFrameRatePriority;
+
+    // Whether a config change is in progress or not
+    std::atomic<bool> mConfigChangePending = false;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index b067466..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,44 +58,44 @@
     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& name = layer->getName();
-    const auto noVoteTag = "LFPS NoVote " + name;
-    const auto heuristicVoteTag = "LFPS Heuristic " + name;
-    const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
-    const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
-    const auto minVoteTag = "LFPS Min " + name;
-    const auto maxVoteTag = "LFPS Max " + name;
+    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+    };
 
-    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__, name.c_str(), fps);
+    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,
                                    LayerVoteType type) {
     const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
-    auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type);
+    auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                            LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -103,7 +103,7 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now);
+    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
@@ -125,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
@@ -143,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)));
         }
     }
 
@@ -174,8 +175,10 @@
                         return LayerVoteType::NoVote;
                 }
             }();
-            if (layer->isVisible() && (frameRate.rate > 0 || voteType == LayerVoteType::NoVote)) {
-                info->setLayerVote(voteType, frameRate.rate);
+
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                info->setLayerVote(type, frameRate.rate);
             } else {
                 info->resetLayerVote();
             }
@@ -183,10 +186,10 @@
         }
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
         }
 
-        info->clearHistory();
+        info->onLayerInactive(now);
         std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
     }
 
@@ -207,7 +210,14 @@
     std::lock_guard lock(mLock);
 
     for (const auto& [layer, info] : activeLayers()) {
-        info->clearHistory();
+        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 bf1fb88..44f20d0 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -15,33 +15,51 @@
  */
 
 // #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 {
 
-LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
-      : mHighRefreshRatePeriod(highRefreshRatePeriod),
-        mDefaultVote(defaultVote),
-        mLayerVote({defaultVote, 0.0f}) {}
+const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
+bool LayerInfoV2::sTraceEnabled = false;
 
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
+                         LayerHistory::LayerVoteType defaultVote)
+      : mName(name),
+        mHighRefreshRatePeriod(highRefreshRatePeriod),
+        mDefaultVote(defaultVote),
+        mLayerVote({defaultVote, 0.0f}),
+        mRefreshRateHistory(name) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+                                     LayerUpdateType updateType, bool pendingConfigChange) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
-
-    FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
-
-    mFrameTimes.push_back(frameTime);
-    if (mFrameTimes.size() > HISTORY_SIZE) {
-        mFrameTimes.pop_front();
+    switch (updateType) {
+        case LayerUpdateType::AnimationTX:
+            mLastAnimationTime = std::max(lastPresentTime, now);
+            break;
+        case LayerUpdateType::SetFrameRate:
+        case LayerUpdateType::Buffer:
+            FrameTimeData frameTime = {.presetTime = lastPresentTime,
+                                       .queueTime = mLastUpdatedTime,
+                                       .pendingConfigChange = pendingConfigChange};
+            mFrameTimes.push_back(frameTime);
+            if (mFrameTimes.size() > HISTORY_SIZE) {
+                mFrameTimes.pop_front();
+            }
+            break;
     }
 }
 
@@ -52,21 +70,14 @@
 }
 
 bool LayerInfoV2::isFrequent(nsecs_t now) const {
-    // Find the first valid frame time
-    auto it = mFrameTimes.begin();
-    for (; it != mFrameTimes.end(); ++it) {
-        if (isFrameTimeValid(*it)) {
-            break;
-        }
-    }
-
     // If we know nothing about this layer we consider it as frequent as it might be the start
     // of an animation.
-    if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
+    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
         return true;
     }
 
     // Find the first active frame
+    auto it = mFrameTimes.begin();
     for (; it != mFrameTimes.end(); ++it) {
         if (it->queueTime >= getActiveLayerThreshold(now)) {
             break;
@@ -83,80 +94,204 @@
     return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
 }
 
+bool LayerInfoV2::isAnimating(nsecs_t now) const {
+    return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
 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::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
-    static constexpr float MARGIN = 1.0f; // 1Hz
-
-    if (!hasEnoughDataForHeuristic()) {
-        ALOGV("Not enough data");
-        return std::nullopt;
-    }
-
-    // Calculate the refresh rate by finding the average delta between frames
+std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
     nsecs_t totalPresentTimeDeltas = 0;
+    nsecs_t totalQueueTimeDeltas = 0;
+    bool missingPresentTime = false;
+    int numFrames = 0;
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
-        // If there are no presentation timestamp provided we can't calculate the refresh rate
-        if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+        // Ignore frames captured during a config change
+        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
             return std::nullopt;
         }
 
+        totalQueueTimeDeltas +=
+                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+        numFrames++;
+
+        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;
+        }
+
         totalPresentTimeDeltas +=
                 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
     }
-    const float averageFrameTime =
-            static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
 
-    // Now once we calculated the refresh rate we need to make sure that all the frames we captured
-    // are evenly distributed and we don't calculate the average across some burst of frames.
-    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
-        const nsecs_t presentTimeDeltas =
-                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
-        if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
-            return std::nullopt;
+    // Calculate the average frame time based on presentation timestamps. If those
+    // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+    // we calculated a refresh rate based on presentation timestamps in the past. The reason
+    // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+    // 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);
+}
+
+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 = 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);
+
+            // 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);
         }
     }
 
-    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) {
     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+        ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
         return {mLayerVote.type, mLayerVote.fps};
     }
 
+    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()};
     }
 
+    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
     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 ad91f18..33dc66f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -43,6 +43,8 @@
 
 // Stores history of present times and refresh rates for a layer.
 class LayerInfoV2 {
+    using LayerUpdateType = LayerHistory::LayerUpdateType;
+
     // Layer is considered frequent if the earliest value in the window of most recent present times
     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
     // favor of a low refresh rate.
@@ -54,7 +56,14 @@
     friend class LayerHistoryTestV2;
 
 public:
-    LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote);
+    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);
 
     LayerInfoV2(const LayerInfo&) = delete;
     LayerInfoV2& operator=(const LayerInfoV2&) = delete;
@@ -62,7 +71,8 @@
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+                            bool pendingConfigChange);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -82,12 +92,23 @@
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
-    void clearHistory() {
+    // 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
         // buffer as Max as we don't know anything about this layer or Min as this layer is
         // posting infrequent updates.
-        mFrameTimeValidSince = std::chrono::steady_clock::now();
+        const auto timePoint = std::chrono::nanoseconds(now);
+        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+        mLastRefreshRate = {};
+        mRefreshRateHistory.clear();
+    }
+
+    void clearHistory(nsecs_t now) {
+        onLayerInactive(now);
+        mFrameTimes.clear();
     }
 
 private:
@@ -95,32 +116,105 @@
     struct FrameTimeData {
         nsecs_t presetTime; // desiredPresentTime, if provided
         nsecs_t queueTime;  // buffer queue time
+        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::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+    std::optional<nsecs_t> calculateAverageFrameTime() const;
     bool isFrameTimeValid(const FrameTimeData&) const;
 
+    const std::string mName;
+
     // Used for sanitizing the heuristic data
     const nsecs_t mHighRefreshRatePeriod;
     LayerHistory::LayerVoteType mDefaultVote;
 
+    LayerVote mLayerVote;
+
     nsecs_t mLastUpdatedTime = 0;
 
-    float mLastReportedRefreshRate = 0.0f;
+    nsecs_t mLastAnimationTime = 0;
 
-    // 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/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 8d958df..8661b6e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -31,6 +31,23 @@
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
 using RefreshRate = RefreshRateConfigs::RefreshRate;
 
+std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
+    switch (vote) {
+        case LayerVoteType::NoVote:
+            return "NoVote";
+        case LayerVoteType::Min:
+            return "Min";
+        case LayerVoteType::Max:
+            return "Max";
+        case LayerVoteType::Heuristic:
+            return "Heuristic";
+        case LayerVoteType::ExplicitDefault:
+            return "ExplicitDefault";
+        case LayerVoteType::ExplicitExactOrMultiple:
+            return "ExplicitExactOrMultiple";
+    }
+}
+
 const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
         const std::vector<LayerRequirement>& layers) const {
     std::lock_guard lock(mLock);
@@ -98,12 +115,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());
 
-    *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;
@@ -128,28 +157,40 @@
         }
     }
 
+    const bool hasExplicitVoteLayers =
+            explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0;
+
     // 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 && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) {
-        *touchConsidered = true;
+    if (globalSignals.touch && !hasExplicitVoteLayers) {
+        ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
+        setTouchConsidered();
         return getMaxRefreshRateByPolicyLocked();
     }
 
-    if (!touchActive && idle) {
+    // If the primary range consists of a single refresh rate then we can only
+    // move out the of range if layers explicitly request a different refresh
+    // rate.
+    const Policy* policy = getCurrentPolicyLocked();
+    const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
+
+    if (!globalSignals.touch && globalSignals.idle &&
+        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+        ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+        setIdleConsidered();
         return getMinRefreshRateByPolicyLocked();
     }
 
-    if (layers.empty()) {
-        return getCurrentRefreshRateByPolicyLocked();
+    if (layers.empty() || noVoteLayers == layers.size()) {
+        return getMaxRefreshRateByPolicyLocked();
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
+        ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
         return getMinRefreshRateByPolicyLocked();
     }
 
-    const Policy* policy = getCurrentPolicyLocked();
-
     // Find the best refresh rate based on score
     std::vector<std::pair<const RefreshRate*, float>> scores;
     scores.reserve(mAppRequestRefreshRates.size());
@@ -159,7 +200,8 @@
     }
 
     for (const auto& layer : layers) {
-        ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
+        ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
+              layerVoteTypeString(layer.vote).c_str(), layer.weight);
         if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
             continue;
         }
@@ -169,10 +211,10 @@
         for (auto i = 0u; i < scores.size(); i++) {
             bool inPrimaryRange =
                     scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
-            if (!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.
+            if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+                !(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;
             }
 
@@ -213,7 +255,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] =
@@ -243,9 +285,9 @@
 
                     return 1.0f / iter;
                 }();
-                ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f",
-                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
-                      layerScore);
+                ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
+                      layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
+                      scores[i].first->name.c_str(), layerScore);
                 scores[i].second += weight * layerScore;
                 continue;
             }
@@ -259,14 +301,29 @@
             ? getBestRefreshRate(scores.rbegin(), scores.rend())
             : getBestRefreshRate(scores.begin(), scores.end());
 
+    if (primaryRangeIsSingleRate) {
+        // If we never scored any layers, then choose the rate from the primary
+        // range instead of picking a random score from the app range.
+        if (std::all_of(scores.begin(), scores.end(),
+                        [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+            ALOGV("layers not scored - choose %s",
+                  getMaxRefreshRateByPolicyLocked().getName().c_str());
+            return getMaxRefreshRateByPolicyLocked();
+        } else {
+            return *bestRefreshRate;
+        }
+    }
+
     // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
     // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
     const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
-    if (touchActive && explicitDefaultVoteLayers == 0 &&
+
+    if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
         bestRefreshRate->fps < touchRefreshRate.fps) {
-        *touchConsidered = true;
+        setTouchConsidered();
+        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
         return touchRefreshRate;
     }
 
@@ -340,7 +397,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());
 
@@ -500,4 +558,64 @@
                        &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;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2657dee..eed8486 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;
@@ -194,15 +195,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 +220,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.
@@ -255,11 +271,37 @@
     // Stores the current configId the device operates at
     void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
 
+    // Returns a string that represents the layer vote type
+    static std::string layerVoteTypeString(LayerVoteType vote);
+
+    // Returns a known frame rate that is the closest to frameRate
+    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;
+
 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,
@@ -317,6 +359,10 @@
     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 d73fd8b..5271ccc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,11 +20,11 @@
 
 #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>
@@ -39,9 +39,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 +49,7 @@
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
+#include "VsyncController.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -60,67 +59,80 @@
         }                                                            \
     } while (false)
 
+using namespace std::string_literals;
+
 namespace android {
 
-std::unique_ptr<DispSync> createDispSync() {
-    // 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 default_rate = 60;
-        static constexpr auto initial_period =
-                std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(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>(
-                                                   initial_period)
-                                                   .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);
-    } 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)
-      : mPrimaryDispSync(createDispSync()),
-        mEventControlThread(new impl::EventControlThread(std::move(function))),
-        mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
-        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 +158,20 @@
     }
 }
 
-Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
-                     std::unique_ptr<EventControlThread> eventControlThread,
-                     const scheduler::RefreshRateConfigs& configs,
-                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
-                     bool useContentDetection)
-      : mPrimaryDispSync(std::move(primaryDispSync)),
-        mEventControlThread(std::move(eventControlThread)),
-        mSupportKernelTimer(false),
+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,20 +180,42 @@
     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);
 }
 
 Scheduler::ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs,
+        const char* connectionName, std::chrono::nanoseconds workDuration,
+        std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
+    auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                            std::move(interceptCallback));
     return createConnection(std::move(eventThread));
@@ -228,8 +264,36 @@
     mConnections[handle].thread->onScreenReleased();
 }
 
-void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                HwcConfigIndexType configId, nsecs_t 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() {
+    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) {
     RETURN_IF_INVALID_HANDLE(handle);
     mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
@@ -244,14 +308,15 @@
     mConnections.at(handle).thread->dump(result);
 }
 
-void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+                            std::chrono::nanoseconds readyDuration) {
     RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->setPhaseOffset(phaseOffset);
+    mConnections[handle].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) {
@@ -276,20 +341,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 +362,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 +402,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 +418,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 +431,7 @@
 }
 
 void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
-    if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+    if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
         enableHardwareVsync();
     } else {
         disableHardwareVsync(false);
@@ -375,56 +439,54 @@
 }
 
 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) {
     if (!mLayerHistory) return;
 
-    // If the content detection feature is off, all layers are registered at NoVote. We still
-    // keep the layer history, since we use it for other features (like Frame Rate API), so layers
-    // still need to be registered.
-    if (!mUseContentDetection) {
-        mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
-                                     mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+    const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
+    const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
+
+    if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+        mLayerHistory->registerLayer(layer, minFps, maxFps,
                                      scheduler::LayerHistory::LayerVoteType::NoVote);
-        return;
-    }
+    } 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 (!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;
 
-    // In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
-    if (!mUseContentDetectionV2) {
-        const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
-        const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
-                ? lowFps
-                : mRefreshRateConfigs.getMaxRefreshRate().getFps();
-
-        mLayerHistory->registerLayer(layer, lowFps, highFps,
+        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, mRefreshRateConfigs.getMinRefreshRate().getFps(),
-                                         mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+            mLayerHistory->registerLayer(layer, minFps, maxFps,
                                          scheduler::LayerHistory::LayerVoteType::Min);
-        } else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
-            mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
-                                         mRefreshRateConfigs.getMaxRefreshRate().getFps(),
-                                         scheduler::LayerHistory::LayerVoteType::NoVote);
         } else {
-            mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
-                                         mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+            mLayerHistory->registerLayer(layer, minFps, maxFps,
                                          scheduler::LayerHistory::LayerVoteType::Heuristic);
         }
     }
 }
 
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+                                   LayerHistory::LayerUpdateType updateType) {
     if (mLayerHistory) {
-        mLayerHistory->record(layer, presentTime, systemTime());
+        mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+    }
+}
+
+void Scheduler::setConfigChangePending(bool pending) {
+    if (mLayerHistory) {
+        mLayerHistory->setConfigChangePending(pending);
     }
 }
 
@@ -444,13 +506,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);
     }
 }
 
@@ -461,19 +531,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();
         }
     }
@@ -512,7 +573,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 */);
     }
 
@@ -520,59 +581,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;
-    handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */);
+    // 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>
-void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
-    ConfigEvent event = ConfigEvent::None;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     HwcConfigIndexType newConfigId;
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (*currentState == newState) {
-            return;
+            return false;
         }
         *currentState = newState;
-        newConfigId = calculateRefreshRateConfigIndexType();
+        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
         if (mFeatures.configId == newConfigId) {
-            return;
+            // 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);
+    mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                         consideredSignals.idle ? ConfigEvent::None
+                                                                : ConfigEvent::Changed);
+    return consideredSignals.touch;
 }
 
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+        scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
     ATRACE_CALL();
+    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.
@@ -585,7 +669,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();
@@ -593,6 +677,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();
         }
 
@@ -607,18 +692,10 @@
                 .getConfigId();
     }
 
-    bool touchConsidered;
-    const auto& ret = mRefreshRateConfigs
-                              .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle,
-                                                  &touchConsidered)
-                              .getConfigId();
-    if (touchConsidered) {
-        // Clear layer history if refresh rate was selected based on touch to allow
-        // the hueristic to pick up with the new rate.
-        mLayerHistory->clear();
-    }
-
-    return ret;
+    return mRefreshRateConfigs
+            .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+                                consideredSignals)
+            .getConfigId();
 }
 
 std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4a0280f..0b5c9d2 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"
@@ -39,19 +38,27 @@
 namespace android {
 
 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
+
+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 {
@@ -59,19 +66,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,
+                                      std::chrono::nanoseconds workDuration,
+                                      std::chrono::nanoseconds readyDuration,
                                       impl::EventThread::InterceptVSyncsCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -80,23 +81,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);
 
@@ -108,17 +110,17 @@
     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*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
+    void setConfigChangePending(bool pending);
 
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();
@@ -133,6 +135,7 @@
 
     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();
@@ -148,6 +151,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;
 
@@ -157,12 +165,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>);
@@ -177,14 +204,18 @@
 
     // handles various timer features to change the refresh rate.
     template <class T>
-    void 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() REQUIRES(mFeatureStateLock);
+    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 {
@@ -205,14 +236,11 @@
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
-    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<scheduler::LayerHistory> mLayerHistory;
-
-    // Whether to use idle timer callbacks that support the kernel timer.
-    const bool mSupportKernelTimer;
+    const std::unique_ptr<LayerHistory> mLayerHistory;
 
     // Timer that records time between requests for next vsync.
     std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -234,9 +262,19 @@
         TimerState displayPowerTimer = TimerState::Expired;
 
         std::optional<HwcConfigIndexType> configId;
-        scheduler::LayerHistory::Summary contentRequirements;
+        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;
@@ -246,10 +284,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..0407900 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,54 @@
     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;
+    };
+
+    /*
      * 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 +152,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..e90edf7 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"
@@ -54,7 +50,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 +65,22 @@
 }
 
 nsecs_t VSyncPredictor::currentPeriod() const {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     return std::get<0>(mRateMap.find(mIdealPeriod)->second);
 }
 
 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;
     }
 
@@ -131,14 +134,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];
     }
@@ -170,9 +173,9 @@
 }
 
 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
-    auto const [slope, intercept] = getVSyncPredictionModel(lk);
+    auto const [slope, intercept] = getVSyncPredictionModel(lock);
 
     if (mTimestamps.empty()) {
         traceInt64If("VSP-mode", 1);
@@ -208,8 +211,8 @@
 }
 
 std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
-    std::lock_guard<std::mutex> lk(mMutex);
-    return VSyncPredictor::getVSyncPredictionModel(lk);
+    std::lock_guard lock(mMutex);
+    return VSyncPredictor::getVSyncPredictionModel(lock);
 }
 
 std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
@@ -220,7 +223,7 @@
 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,37 +239,31 @@
 
 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) {
@@ -279,5 +276,3 @@
 
 } // 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..5f2ec49 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -52,11 +52,10 @@
      */
     void setPeriod(nsecs_t period) final;
 
-    /* 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;
 
     std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
 
@@ -75,7 +74,7 @@
     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);
@@ -85,7 +84,7 @@
 
     std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
 
-    int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+    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 5f0c9ce..7b5d462 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -30,134 +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)
+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;
     }
@@ -167,7 +56,7 @@
         return true;
     }
 
-    std::lock_guard<std::mutex> lk(mMutex);
+    std::lock_guard lock(mMutex);
     if (mExternalIgnoreFences || mInternalIgnoreFences) {
         return true;
     }
@@ -180,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);
         }
@@ -192,7 +81,7 @@
         }
         mUnfiredFences.push_back(fence);
     } else {
-        timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+        timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
     }
 
     if (!timestampAccepted) {
@@ -204,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();
 }
 
@@ -221,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;
@@ -238,34 +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 (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;
@@ -275,7 +148,14 @@
         return false;
     }
 
-    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+    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 : mTracker.currentPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
     auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -287,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",
@@ -378,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 31ddf5a..449d4c3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,73 +22,54 @@
 #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;
 };
 
 class SystemClock : public Clock {
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 05a6fc3..107c540 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
+    virtual bool needsMoreSamples() 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 3b66fa9..9d35a3f 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/1.0/IPower.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>
@@ -49,14 +52,13 @@
 #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 +74,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 +88,7 @@
 #include <functional>
 #include <mutex>
 #include <optional>
+#include <type_traits>
 #include <unordered_map>
 
 #include "BufferLayer.h"
@@ -101,23 +103,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"
@@ -125,6 +130,19 @@
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 
+#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
+
+#define ON_MAIN_THREAD(expr)                                       \
+    [&] {                                                          \
+        LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
+        UnnecessaryLock lock(mStateLock);                          \
+        return (expr);                                             \
+    }()
+
+#undef NO_THREAD_SAFETY_ANALYSIS
+#define NO_THREAD_SAFETY_ANALYSIS \
+    _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
+
 namespace android {
 
 using namespace std::string_literals;
@@ -133,7 +151,7 @@
 using namespace android::hardware::configstore::V1_0;
 using namespace android::sysprop;
 
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
 using base::StringAppendF;
 using ui::ColorMode;
 using ui::Dataspace;
@@ -173,12 +191,12 @@
 #pragma clang diagnostic pop
 
 template <typename Mutex>
-struct ConditionalLockGuard {
-    ConditionalLockGuard(Mutex& mutex, bool lock) : mutex(mutex), lock(lock) {
+struct SCOPED_CAPABILITY ConditionalLockGuard {
+    ConditionalLockGuard(Mutex& mutex, bool lock) ACQUIRE(mutex) : mutex(mutex), lock(lock) {
         if (lock) mutex.lock();
     }
 
-    ~ConditionalLockGuard() {
+    ~ConditionalLockGuard() RELEASE() {
         if (lock) mutex.unlock();
     }
 
@@ -188,6 +206,27 @@
 
 using ConditionalLock = ConditionalLockGuard<Mutex>;
 
+struct SCOPED_CAPABILITY TimedLock {
+    TimedLock(Mutex& mutex, nsecs_t timeout, const char* whence) ACQUIRE(mutex)
+          : mutex(mutex), status(mutex.timedLock(timeout)) {
+        ALOGE_IF(!locked(), "%s timed out locking: %s (%d)", whence, strerror(-status), status);
+    }
+
+    ~TimedLock() RELEASE() {
+        if (locked()) mutex.unlock();
+    }
+
+    bool locked() const { return status == NO_ERROR; }
+
+    Mutex& mutex;
+    const status_t status;
+};
+
+struct SCOPED_CAPABILITY UnnecessaryLock {
+    explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
+    ~UnnecessaryLock() RELEASE() {}
+};
+
 // TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
 constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
 
@@ -217,13 +256,28 @@
 
 }  // 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;
@@ -279,10 +333,13 @@
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(std::make_unique<FrameTracer>()),
+        mFrameTimeline(std::make_shared<frametimeline::impl::FrameTimeline>()),
         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");
@@ -360,10 +417,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,
@@ -387,6 +440,10 @@
     const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+    mGraphicBufferProducerListSizeLogThreshold =
+            std::max(static_cast<int>(0.95 *
+                                      static_cast<double>(mMaxGraphicBufferProducerListSize)),
+                     1);
 
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
@@ -405,10 +462,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;
@@ -502,11 +564,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);
         }
     }
 
@@ -515,7 +577,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 {
@@ -561,13 +623,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();
@@ -582,7 +637,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;
@@ -674,10 +737,11 @@
                 signalTransaction();
             }));
         };
-        mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
-                                            getHwComposer()
-                                                    .fromPhysicalDisplayId(*display->getId())
-                                                    .value_or(0),
+        auto hwcDisplayId =
+                getHwComposer()
+                        .fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*display->getId()))
+                        .value_or(0);
+        mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(), hwcDisplayId,
                                             vrFlingerRequestDisplayCallback);
         if (!mVrFlinger) {
             ALOGE("Failed to start vrflinger");
@@ -798,8 +862,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;
 }
@@ -832,7 +897,7 @@
     info->density /= ACONFIGURATION_DENSITY_MEDIUM;
 
     info->secure = display->isSecure();
-    info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+    info->deviceProductInfo = display->getDeviceProductInfo();
 
     return NO_ERROR;
 }
@@ -883,9 +948,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
@@ -895,7 +961,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
@@ -913,7 +979,7 @@
         return BAD_VALUE;
     }
 
-    mScheduler->getDisplayStatInfo(stats);
+    mScheduler->getDisplayStatInfo(stats, systemTime());
     return NO_ERROR;
 }
 
@@ -971,11 +1037,11 @@
         // 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);
     }
 
     if (mRefreshRateOverlay) {
@@ -991,7 +1057,7 @@
     }
 
     auto future = schedule([=]() -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
+        const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set allowed display configs for invalid display token %p",
                   displayToken.get());
@@ -1033,16 +1099,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);
+        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
+                                                  static_cast<PhysicalDisplayId>(*display->getId()),
+                                                  mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
 
@@ -1053,9 +1119,10 @@
 
     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);
 }
 
 void SurfaceFlinger::performSetActiveConfig() {
@@ -1176,7 +1243,7 @@
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    schedule([=] {
+    schedule([=]() MAIN_THREAD {
         Vector<ColorMode> modes;
         getDisplayColorModes(displayToken, &modes);
         bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
@@ -1222,7 +1289,7 @@
 }
 
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
-    static_cast<void>(schedule([=] {
+    static_cast<void>(schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             getHwComposer().setAutoLowLatencyMode(*displayId, on);
         } else {
@@ -1253,7 +1320,7 @@
 }
 
 void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
-    static_cast<void>(schedule([=] {
+    static_cast<void>(schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
             getHwComposer().setContentType(*displayId, type);
@@ -1297,30 +1364,6 @@
     return NO_ERROR;
 }
 
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
-        const DisplayDevice& display) const {
-    // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
-    // avoid repetitive HAL IPC and EDID parsing.
-    const auto displayId = display.getId();
-    LOG_FATAL_IF(!displayId);
-
-    const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
-    LOG_FATAL_IF(!hwcDisplayId);
-
-    uint8_t port;
-    DisplayIdentificationData data;
-    if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
-        ALOGV("%s: No identification data.", __FUNCTION__);
-        return {};
-    }
-
-    const auto info = parseDisplayIdentificationData(port, data);
-    if (!info) {
-        return {};
-    }
-    return info->deviceProductInfo;
-}
-
 status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                                ui::PixelFormat* outFormat,
                                                                ui::Dataspace* outDataspace,
@@ -1343,7 +1386,7 @@
 status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
                                                           bool enable, uint8_t componentMask,
                                                           uint64_t maxFrames) {
-    return schedule([=]() -> status_t {
+    return schedule([=]() MAIN_THREAD -> status_t {
                if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
                    return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
                                                                            componentMask,
@@ -1409,25 +1452,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
-        NO_THREAD_SAFETY_ANALYSIS {
-    // Try to acquire a lock for 1s, fail gracefully
-    const status_t err = mStateLock.timedLock(s2ns(1));
-    const bool locked = (err == NO_ERROR);
-    if (!locked) {
-        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
-        return TIMED_OUT;
-    }
-
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
     outLayers->clear();
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        outLayers->push_back(layer->getLayerDebugInfo());
-    });
-
-    mStateLock.unlock();
+    schedule([=] {
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            outLayers->push_back(layer->getLayerDebugInfo(display.get()));
+        });
+    }).wait();
     return NO_ERROR;
 }
 
@@ -1484,7 +1523,7 @@
         return BAD_VALUE;
     }
 
-    return promise::chain(schedule([=] {
+    return promise::chain(schedule([=]() MAIN_THREAD {
                if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
                    return getHwComposer().setDisplayBrightness(*displayId, brightness);
                } else {
@@ -1496,10 +1535,10 @@
             .get();
 }
 
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
-    PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+    Boost powerBoost = static_cast<Boost>(boostId);
 
-    if (powerHint == PowerHint::INTERACTION) {
+    if (powerBoost == Boost::INTERACTION) {
         mScheduler->notifyTouchEvent();
     }
 
@@ -1533,7 +1572,7 @@
     mEventQueue->refresh();
 }
 
-nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
     const auto displayId = getInternalDisplayIdLocked();
     if (!displayId || !getHwComposer().isConnected(*displayId)) {
         return 0;
@@ -1565,7 +1604,7 @@
     bool periodFlushed = false;
     mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
-        mVSyncModulator->onRefreshRateChangeCompleted();
+        modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
     }
 }
 
@@ -1597,7 +1636,7 @@
 }
 
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
-                                       hal::Connection connection) NO_THREAD_SAFETY_ANALYSIS {
+                                       hal::Connection connection) {
     ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
           connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
 
@@ -1645,29 +1684,20 @@
     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([=]() NO_THREAD_SAFETY_ANALYSIS { 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->getId(), mHWCVsyncPendingState);
         }
-    }
+    }));
 }
 
-// Note: it is assumed the caller holds |mStateLock| when this is called
 void SurfaceFlinger::resetDisplayState() {
     mScheduler->disableHardwareVsync(true);
     // Clear the drawing state so that the logic inside of
@@ -1742,7 +1772,7 @@
     setPowerModeInternal(display, currentDisplayPowerMode);
 
     // Reset the timing values to account for the period of the swapped in HWC
-    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
 
     // The present fences returned from vr_hwc are not an accurate
@@ -1760,15 +1790,15 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
+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) NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
     ATRACE_CALL();
     const sp<Fence>& fence = previousFrameFence();
 
@@ -1782,7 +1812,7 @@
     return status == -ETIME;
 }
 
-nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+nsecs_t SurfaceFlinger::previousFramePresentTime() {
     const sp<Fence>& fence = previousFrameFence();
 
     if (fence == Fence::NO_FENCE) {
@@ -1794,14 +1824,13 @@
 
 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) NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
@@ -1815,7 +1844,7 @@
     }
 }
 
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
 
     const nsecs_t frameStart = systemTime();
@@ -1829,10 +1858,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",
@@ -1847,7 +1873,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",
@@ -1888,10 +1914,10 @@
         // We received the present fence from the HWC, so we assume it successfully updated
         // the config, hence we update SF.
         mSetActiveConfigPending = false;
-        setActiveConfigInternal();
+        ON_MAIN_THREAD(setActiveConfigInternal());
     }
 
-    if (framePending && mPropagateBackpressure) {
+    if (framePending) {
         if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
             signalLayerUpdate();
             return;
@@ -1905,25 +1931,19 @@
     // ...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 displayDevice = getDefaultDisplayDeviceLocked();
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
         // Only report jank when the display is on, as displays in DOZE
         // power mode may operate at a different frame rate than is
         // reported in their config, which causes noticeable (but less
         // severe) jank.
-        if (displayDevice && displayDevice->getPowerMode() == hal::PowerMode::ON) {
+        if (display && display->getPowerMode() == hal::PowerMode::ON) {
             const nsecs_t currentTime = systemTime();
             const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
             if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
-                ATRACE_NAME("Jank detected");
-                ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
-                const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
-                {
-                    ATRACE_NAME("Pushing to statsd");
-                    android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
-                                               jankyDurationMillis, mMissedFrameJankCount);
-                }
+                jankDurationToUpload = jankDuration;
             }
 
             // We either reported a jank event or we missed the trace
@@ -1945,6 +1965,12 @@
         mTracingEnabledChanged = false;
     }
 
+    if (mRefreshRateOverlaySpinner) {
+        if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+            mRefreshRateOverlay->onInvalidate();
+        }
+    }
+
     bool refreshNeeded;
     {
         ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
@@ -1969,13 +1995,14 @@
         mScheduler->chooseRefreshRateForContent();
     }
 
-    performSetActiveConfig();
+    ON_MAIN_THREAD(performSetActiveConfig());
 
     updateCursorAsync();
     updateInputFlinger();
 
     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
@@ -1996,8 +2023,10 @@
 
     bool flushedATransaction = flushTransactionQueues();
 
-    bool runHandleTransaction = transactionFlags &&
-            ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+    bool runHandleTransaction =
+            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+            flushedATransaction ||
+            mForceTraversal;
 
     if (runHandleTransaction) {
         handleTransaction(eTransactionMask);
@@ -2018,8 +2047,9 @@
     mRefreshPending = false;
 
     compositionengine::CompositionRefreshArgs refreshArgs;
-    refreshArgs.outputs.reserve(mDisplays.size());
-    for (const auto& [_, display] : mDisplays) {
+    const auto& displays = ON_MAIN_THREAD(mDisplays);
+    refreshArgs.outputs.reserve(displays.size());
+    for (const auto& [_, display] : displays) {
         refreshArgs.outputs.push_back(display->getCompositionDisplay());
     }
     mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
@@ -2027,7 +2057,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);
     }
@@ -2074,21 +2104,18 @@
 
     const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
 
-    mHadClientComposition =
-            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
-                auto& displayDevice = tokenDisplayPair.second;
-                return displayDevice->getCompositionDisplay()->getState().usesClientComposition &&
-                        !displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
-            });
-    mHadDeviceComposition =
-            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
-                auto& displayDevice = tokenDisplayPair.second;
-                return displayDevice->getCompositionDisplay()->getState().usesDeviceComposition;
-            });
+    mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+        const auto& state = pair.second->getCompositionDisplay()->getState();
+        return state.usesClientComposition && !state.reusedClientComposition;
+    });
+    mHadDeviceComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+        const auto& state = pair.second->getCompositionDisplay()->getState();
+        return state.usesDeviceComposition;
+    });
     mReusedClientComposition =
-            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
-                auto& displayDevice = tokenDisplayPair.second;
-                return displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
+            std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+                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
@@ -2096,7 +2123,9 @@
         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) {
@@ -2158,12 +2187,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;
     }
@@ -2173,7 +2202,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 =
@@ -2191,17 +2220,17 @@
     ALOGV("postComposition");
 
     nsecs_t dequeueReadyTime = systemTime();
-    for (auto& layer : mLayersWithQueuedFrames) {
+    for (auto layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
-    // |mStateLock| not needed as we are on the main thread
-    const auto displayDevice = getDefaultDisplayDeviceLocked();
+
+    const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (displayDevice && displayDevice->getCompositionDisplay()->getState().usesClientComposition) {
+    if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+                std::make_shared<FenceTime>(display->getCompositionDisplay()
                                                     ->getRenderSurface()
                                                     ->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -2211,14 +2240,13 @@
 
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
-    mPreviousPresentFences[0] = displayDevice
-            ? getHwComposer().getPresentFence(*displayDevice->getId())
-            : Fence::NO_FENCE;
+    mPreviousPresentFences[0] =
+            display ? getHwComposer().getPresentFence(*display->getId()) : 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,
@@ -2232,8 +2260,8 @@
     }
 
     mDrawingState.traverse([&](Layer* layer) {
-        bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
-                                                     presentFenceTime, compositorTiming);
+        const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                                           presentFenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
@@ -2242,14 +2270,15 @@
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
     mTransactionCompletedThread.sendCallbacks();
 
-    if (displayDevice && displayDevice->isPrimary() &&
-        displayDevice->getPowerMode() == hal::PowerMode::ON && presentFenceTime->isValid()) {
+    if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
+        presentFenceTime->isValid()) {
         mScheduler->addPresentFence(presentFenceTime);
     }
 
+    const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+
     if (!hasSyncFramework) {
-        if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
-            displayDevice->isPoweredOn()) {
+        if (isDisplayConnected && display->isPoweredOn()) {
             mScheduler->enableHardwareVsync();
         }
     }
@@ -2260,11 +2289,10 @@
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
+        } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime =
-                    getHwComposer().getRefreshTimestamp(*displayDevice->getId());
+            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
             mAnimFrameTracker.setActualPresentTime(presentTime);
         }
         mAnimFrameTracker.advanceFrame();
@@ -2285,8 +2313,15 @@
     const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
     mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
 
-    if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
-        !displayDevice->isPoweredOn()) {
+    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;
     }
 
@@ -2338,11 +2373,11 @@
 }
 
 FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
-    return displayDevice.getViewport().toFloatRect();
+    return displayDevice.getLayerStackSpaceRect().toFloatRect();
 }
 
 void SurfaceFlinger::computeLayerBounds() {
-    for (const auto& pair : mDisplays) {
+    for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
         const auto& displayDevice = pair.second;
         const auto display = displayDevice->getCompositionDisplay();
         for (const auto& layer : mDrawingState.layersSortedByZ) {
@@ -2357,10 +2392,8 @@
     }
 }
 
-void SurfaceFlinger::postFrame()
-{
-    // |mStateLock| not needed as we are on the main thread
-    const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::postFrame() {
+    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
     if (display && getHwComposer().isConnected(*display->getId())) {
         uint32_t flipCount = display->getPageFlipCount();
         if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
@@ -2388,7 +2421,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);
 
@@ -2406,7 +2439,7 @@
             continue;
         }
 
-        const DisplayId displayId = info->id;
+        const auto displayId = info->id;
         const auto it = mPhysicalDisplayTokens.find(displayId);
 
         if (event.connection == hal::Connection::CONNECTED) {
@@ -2418,8 +2451,10 @@
                 }
 
                 DisplayDeviceState state;
-                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
-                                  event.hwcDisplayId};
+                state.physical = {.id = displayId,
+                                  .type = getHwComposer().getDisplayConnectionType(displayId),
+                                  .hwcDisplayId = event.hwcDisplayId,
+                                  .deviceProductInfo = info->deviceProductInfo};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = info->name;
 
@@ -2535,10 +2570,12 @@
         LOG_ALWAYS_FATAL_IF(!displayId);
         auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
         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;
@@ -2617,7 +2654,7 @@
     mDisplays.emplace(displayToken, display);
     if (!state.isVirtual()) {
         LOG_FATAL_IF(!displayId);
-        dispatchDisplayHotplugEvent(displayId->value, true);
+        dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), true);
     }
 
     if (display->isPrimary()) {
@@ -2633,7 +2670,7 @@
 
         if (!display->isVirtual()) {
             LOG_FATAL_IF(!displayId);
-            dispatchDisplayHotplugEvent(displayId->value, false);
+            dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), false);
         }
     }
 
@@ -2647,6 +2684,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();
         }
@@ -2656,7 +2694,8 @@
         }
         processDisplayAdded(displayToken, currentState);
         if (currentState.physical) {
-            initializeDisplays();
+            const auto display = getDisplayDeviceLocked(displayToken);
+            setPowerModeInternal(display, hal::PowerMode::ON);
         }
         return;
     }
@@ -2666,10 +2705,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) {
@@ -2740,7 +2779,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;
@@ -2753,7 +2793,6 @@
                 mInputInfoChanged = true;
             }
         });
-        mTraversalNeededMainThread = false;
     }
 
     /*
@@ -2788,7 +2827,7 @@
         sp<const DisplayDevice> hintDisplay;
         uint32_t currentlayerStack = 0;
         bool first = true;
-        mCurrentState.traverse([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
@@ -2826,7 +2865,7 @@
             // could be null if there is no display available at all to get
             // the transform hint from.
             if (hintDisplay) {
-                layer->updateTransformHint(hintDisplay);
+                layer->updateTransformHint(hintDisplay->getTransformHint());
             }
 
             first = false;
@@ -2877,23 +2916,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() {
@@ -2901,10 +2943,9 @@
     mPendingInputWindowCommands.clear();
 }
 
-void SurfaceFlinger::updateCursorAsync()
-{
+void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
-    for (const auto& [_, display] : mDisplays) {
+    for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
         if (display->getId()) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
@@ -2914,7 +2955,7 @@
 }
 
 void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
-                                       Scheduler::ConfigEvent event) NO_THREAD_SAFETY_ANALYSIS {
+                                       Scheduler::ConfigEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Sheduler::chooseRefreshRateForContent
@@ -2923,7 +2964,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.
@@ -2941,24 +2982,25 @@
                                                           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();
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+            mScheduler->createConnection("app",
+                                         /*workDuration=*/configs.late.appWorkDuration,
+                                         /*readyDuration=*/configs.late.sfWorkDuration,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
-                                         [this](nsecs_t timestamp) {
+            mScheduler->createConnection("sf",
+                                         /*workDuration=*/configs.late.sfWorkDuration,
+                                         /*readyDuration=*/0ns, [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
     mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
-                            mPhaseConfiguration->getCurrentOffsets());
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
@@ -2972,12 +3014,30 @@
     // 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()));
+}
+
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+    mScheduler->setDuration(mAppConnectionHandle,
+                            /*workDuration=*/config.appWorkDuration,
+                            /*readyDuration=*/config.sfWorkDuration);
+    mScheduler->setDuration(mSfConnectionHandle,
+                            /*workDuration=*/config.sfWorkDuration,
+                            /*readyDuration=*/0ns);
+}
+
+void SurfaceFlinger::commitTransaction() {
     commitTransactionLocked();
     mTransactionPending = false;
     mAnimTransactionPending = false;
@@ -3013,15 +3073,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(); });
@@ -3040,7 +3104,7 @@
 }
 
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
-    for (const auto& [token, displayDevice] : mDisplays) {
+    for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
         auto display = displayDevice->getCompositionDisplay();
         if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             display->editState().dirtyRegion.orSelf(dirty);
@@ -3074,7 +3138,7 @@
         if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
-                mLayersWithQueuedFrames.push_back(layer);
+                mLayersWithQueuedFrames.emplace(layer);
             } else {
                 ATRACE_NAME("!layer->shouldPresentNow()");
                 layer->useEmptyDamage();
@@ -3178,10 +3242,15 @@
                                 "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
                                 mGraphicBufferProducerList.size(),
                                 mMaxGraphicBufferProducerListSize, mNumLayers.load());
+            if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+                ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+                      mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+                      mNumLayers.load());
+            }
         }
 
         if (const auto display = getDefaultDisplayDeviceLocked()) {
-            lbc->updateTransformHint(display);
+            lbc->updateTransformHint(display->getTransformHint());
         }
         if (outTransformHint) {
             *outTransformHint = lbc->getTransformHint();
@@ -3212,19 +3281,20 @@
 }
 
 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;
 }
 
+void SurfaceFlinger::setTraversalNeeded() {
+    mForceTraversal = true;
+}
+
 bool SurfaceFlinger::flushTransactionQueues() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
@@ -3250,7 +3320,8 @@
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
                                       transaction.buffer, transaction.postTime,
                                       transaction.privileged, transaction.hasListenerCallbacks,
-                                      transaction.listenerCallbacks, /*isMainThread*/ true);
+                                      transaction.listenerCallbacks, transaction.originPID,
+                                      transaction.originUID, /*isMainThread*/ true);
                 transactionQueue.pop();
                 flushedATransaction = true;
             }
@@ -3294,7 +3365,7 @@
     return true;
 }
 
-void SurfaceFlinger::setTransactionState(
+status_t 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,
@@ -3330,17 +3401,23 @@
         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);
+                                               hasListenerCallbacks, listenerCallbacks, originPID,
+                                               originUID);
         setTransactionFlags(eTransactionFlushNeeded);
-        return;
+        return NO_ERROR;
     }
 
     applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
                           uncacheBuffer, postTime, privileged, hasListenerCallbacks,
-                          listenerCallbacks);
+                          listenerCallbacks, originPID, originUID, /*isMainThread*/ false);
+    return NO_ERROR;
 }
 
 void SurfaceFlinger::applyTransactionState(
@@ -3348,7 +3425,7 @@
         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, bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3384,6 +3461,12 @@
     for (const ComposerState& state : states) {
         clientStateFlags |= setClientStateLocked(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,
+                                               LayerHistory::LayerUpdateType::AnimationTX);
+            }
+        }
     }
 
     for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
@@ -3396,7 +3479,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);
@@ -3416,18 +3503,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);
+        }
+
+        // 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;
@@ -3464,6 +3567,12 @@
                 break;
             }
         }
+    } else {
+        // Update VsyncModulator state machine even if transaction is not needed.
+        if (schedule == TransactionSchedule::EarlyStart ||
+            schedule == TransactionSchedule::EarlyEnd) {
+            modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+        }
     }
 }
 
@@ -3492,12 +3601,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;
         }
     }
@@ -3668,7 +3777,7 @@
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
+    if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
         if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eLayerStackChanged) {
@@ -3732,7 +3841,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;
@@ -3754,7 +3863,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");
@@ -3842,13 +3951,8 @@
 }
 
 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,
@@ -3910,7 +4014,12 @@
     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);
+            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>);
+            metadata.setInt32(METADATA_WINDOW_TYPE,
+                              static_cast<U>(InputWindowInfo::Type::NAVIGATION_BAR_PANEL));
             primaryDisplayOnly = true;
         }
     }
@@ -4040,7 +4149,6 @@
                                                 LayerMetadata metadata, sp<IBinder>* handle,
                                                 sp<Layer>* outLayer) {
     LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
-    args.displayDevice = getDefaultDisplayDevice();
     args.textureName = getNewTexture();
     sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
     *handle = layer->getHandle();
@@ -4116,8 +4224,8 @@
     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);
@@ -4125,8 +4233,7 @@
                         {});
 
     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.
@@ -4137,14 +4244,7 @@
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    static_cast<void>(schedule([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
-}
-
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
-    if (mHWCVsyncState != enabled) {
-        getHwComposer().setVsyncEnabled(displayId, enabled);
-        mHWCVsyncState = enabled;
-    }
+    static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4168,16 +4268,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);
         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;
@@ -4194,7 +4294,7 @@
         }
 
         // 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);
         mVisibleRegionsDirty = true;
@@ -4204,7 +4304,7 @@
         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
@@ -4228,7 +4328,7 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
+    schedule([=]() MAIN_THREAD {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4241,8 +4341,7 @@
     }).wait();
 }
 
-status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
-                                bool asProto) NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
     std::string result;
 
     IPCThreadState* ipc = IPCThreadState::self();
@@ -4254,22 +4353,9 @@
         StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
                       pid, uid);
     } else {
-        // Try to get the main lock, but give up after one second
-        // (this would indicate SF is stuck, but we want to be able to
-        // print something in dumpsys).
-        status_t err = mStateLock.timedLock(s2ns(1));
-        bool locked = (err == NO_ERROR);
-        if (!locked) {
-            StringAppendF(&result,
-                          "SurfaceFlinger appears to be unresponsive (%s [%d]), dumping anyways "
-                          "(no locks held)\n",
-                          strerror(-err), err);
-        }
-
         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)},
@@ -4283,18 +4369,23 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-        const auto it = dumpers.find(flag);
-        if (it != dumpers.end()) {
-            (it->second)(args, asProto, result);
-        } else if (!asProto) {
-            dumpAllLocked(args, result);
+        bool dumpLayers = true;
+        {
+            TimedLock lock(mStateLock, s2ns(1), __FUNCTION__);
+            if (!lock.locked()) {
+                StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+                              strerror(-lock.status), lock.status);
+            }
+
+            if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+                (it->second)(args, asProto, result);
+                dumpLayers = false;
+            } else if (!asProto) {
+                dumpAllLocked(args, result);
+            }
         }
 
-        if (locked) {
-            mStateLock.unlock();
-        }
-
-        if (it == dumpers.end()) {
+        if (dumpLayers) {
             const LayersProto layersProto = dumpProtoFromMainThread();
             if (asProto) {
                 result.append(layersProto.SerializeAsString());
@@ -4325,7 +4416,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]);
@@ -4387,10 +4478,10 @@
     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,
@@ -4411,7 +4502,7 @@
     }
 
     mScheduler->dump(mAppConnectionHandle, result);
-    mScheduler->getPrimaryDispSync().dump(result);
+    mScheduler->dumpVsync(result);
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4495,7 +4586,8 @@
         if (!displayId) {
             continue;
         }
-        const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
+        const auto hwcDisplayId =
+                getHwComposer().fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*displayId));
         if (!hwcDisplayId) {
             continue;
         }
@@ -4568,11 +4660,11 @@
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
     // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
-    const auto display = getDefaultDisplayDeviceLocked();
+    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
 
     LayersProto layersProto;
     for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags, display);
+        layer->writeToProto(layersProto, traceFlags, display.get());
     }
 
     return layersProto;
@@ -4637,8 +4729,6 @@
     result.append("Build configuration:");
     colorizer.reset(result);
     appendSfConfigString(result);
-    appendUiConfigString(result);
-    appendGuiConfigString(result);
     result.append("\n");
 
     result.append("\nDisplay identification data:\n");
@@ -4681,7 +4771,7 @@
         StringAppendF(&result, "Composition layers\n");
         mDrawingState.traverseInZOrder([&](Layer* layer) {
             auto* compositionState = layer->getCompositionState();
-            if (!compositionState) return;
+            if (!compositionState || !compositionState->isVisible) return;
 
             android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
                                          layer->getDebugName() ? layer->getDebugName()
@@ -4734,12 +4824,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,9 +4861,9 @@
 
         StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
         Layer::miniDumpHeader(result);
-        const sp<DisplayDevice> displayDevice = display;
-        mCurrentState.traverseInZOrder(
-                [&](Layer* layer) { layer->miniDump(result, displayDevice); });
+
+        const DisplayDevice& ref = *display;
+        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
         result.append("\n");
     }
 
@@ -4845,7 +4945,7 @@
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
         case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
         case GET_DISPLAYED_CONTENT_SAMPLE:
-        case NOTIFY_POWER_HINT:
+        case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
             // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
@@ -4898,11 +4998,13 @@
         // special permissions.
         case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+        // captureLayers and captureDisplay will handle the permission check in the function
+        case CAPTURE_LAYERS:
+        case CAPTURE_DISPLAY:
         case SET_DISPLAY_BRIGHTNESS: {
             return OK;
         }
-        case CAPTURE_LAYERS:
-        case CAPTURE_SCREEN:
+
         case ADD_REGION_SAMPLING_LISTENER:
         case REMOVE_REGION_SAMPLING_LISTENER: {
             // codes that require permission check
@@ -4916,7 +5018,7 @@
             }
             return OK;
         }
-        case CAPTURE_SCREEN_BY_ID: {
+        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) {
@@ -4934,9 +5036,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1036 are currently used for backdoors. The code
+    // Numbers from 1000 to 1037 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 <= 1037) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5079,14 +5181,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
@@ -5255,7 +5357,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) {
@@ -5277,6 +5380,23 @@
                 }
                 return NO_ERROR;
             }
+            // Inject a hotplug connected event for the primary display. This will deallocate and
+            // reallocate the display state including framebuffers.
+            case 1037: {
+                const auto token = getInternalDisplayToken();
+
+                sp<DisplayDevice> display;
+                {
+                    Mutex::Autolock lock(mStateLock);
+                    display = getDisplayDeviceLocked(token);
+                }
+                const auto hwcId =
+                        getHwComposer().fromPhysicalDisplayId(PhysicalDisplayId(*display->getId()));
+
+                onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -5309,8 +5429,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);
@@ -5320,6 +5439,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:
@@ -5333,45 +5481,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:
@@ -5384,6 +5493,76 @@
     }
 }
 
+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::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);
+        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.
+        if (args.useRGBColorSpace) {
+            dataspace = Dataspace::V0_SRGB;
+        } else {
+            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, captureListener);
+}
+
 status_t SurfaceFlinger::setSchedFifo(bool enabled) {
     static constexpr int kFifoPriority = 2;
     static constexpr int kOtherPriority = 0;
@@ -5404,9 +5583,9 @@
     return NO_ERROR;
 }
 
-const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
-    const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
-    if (displayToken) {
+sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+    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
@@ -5414,7 +5593,7 @@
     return getDisplayByLayerStack(displayOrLayerStack);
 }
 
-const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
+sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
     for (const auto& [token, display] : mDisplays) {
         if (display->getLayerStack() == layerStack) {
             return display;
@@ -5423,154 +5602,61 @@
     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(uint64_t displayOrLayerStack,
+                                        const sp<IScreenCaptureListener>& captureListener) {
+    ui::LayerStack layerStack;
+    wp<DisplayDevice> displayWeak;
+    ui::Size size;
+    ui::Dataspace dataspace;
     {
         Mutex::Autolock lock(mStateLock);
-        display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+        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, 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;
@@ -5584,25 +5670,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);
@@ -5617,25 +5702,44 @@
             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.
+        if (args.useRGBColorSpace) {
+            dataspace = Dataspace::V0_SRGB;
+        } else {
+            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;
             }
 
@@ -5651,84 +5755,104 @@
         });
     };
 
-    bool outCapturedSecureLayers = false;
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
-                               outCapturedSecureLayers);
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+                               args.pixelFormat, 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 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");
-
-    return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
-                               false /* regionSampling */, outCapturedSecureLayers);
+    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, 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,
-                                            ANativeWindowBuffer* 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());
+    });
+
+    // 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;
@@ -5752,6 +5876,7 @@
     fillLayer.alpha = half(alpha);
     clientCompositionLayers.push_back(fillLayer);
 
+    const auto display = renderArea.getDisplayDevice();
     std::vector<Layer*> renderedLayers;
     Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
@@ -5759,13 +5884,12 @@
         Region clip(renderArea.getBounds());
         compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
                 clip,
-                useIdentityTransform,
-                layer->needsFilteringForScreenshots(renderArea.getDisplayDevice(), transform) ||
+                layer->needsFilteringForScreenshots(display.get(), transform) ||
                         renderArea.needsFiltering(),
                 renderArea.isSecure(),
                 supportProtectedContent,
                 clearRegion,
-                displayViewport,
+                layerStackSpaceRect,
                 clientCompositionDisplay.outputDataspace,
                 true,  /* realContentIsVisible */
                 false, /* clearContent */
@@ -5813,30 +5937,7 @@
             layer->onLayerDisplayed(releaseFence);
         }
     }
-}
 
-status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
-                                                 TraverseLayersFunction traverseLayers,
-                                                 ANativeWindowBuffer* 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;
 }
 
@@ -5862,22 +5963,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);
         });
     }
@@ -5918,8 +6022,10 @@
         const nsecs_t vsyncPeriod = getHwComposer()
                                             .getConfigs(*displayId)[policy->defaultConfig.value()]
                                             ->getVsyncPeriod();
-        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    policy->defaultConfig, vsyncPeriod);
+        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle,
+                                                     static_cast<PhysicalDisplayId>(
+                                                             *display->getId()),
+                                                     policy->defaultConfig, vsyncPeriod);
         return NO_ERROR;
     }
 
@@ -5945,14 +6051,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);
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
+                                              static_cast<PhysicalDisplayId>(*display->getId()),
+                                              display->getActiveConfig(), vsyncPeriod);
+    toggleKernelIdleTimer();
 
     auto configId = mScheduler->getPreferredConfigId();
     auto& preferredRefreshRate = configId
@@ -5988,7 +6095,7 @@
     }
 
     auto future = schedule([=]() -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
+        const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set desired display configs for invalid display token %p",
                   displayToken.get());
@@ -6054,10 +6161,6 @@
     }
 }
 
-void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
-    mFlinger->setInputWindowsFinished();
-}
-
 wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
     Mutex::Autolock _l(mStateLock);
     return fromHandleLocked(handle);
@@ -6143,7 +6246,7 @@
         return BAD_VALUE;
     }
 
-    static_cast<void>(schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
+    static_cast<void>(schedule([=] {
         Mutex::Autolock lock(mStateLock);
         if (authenticateSurfaceTextureLocked(surface)) {
             sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6172,8 +6275,7 @@
         sp<IBinder> token;
 
         if (mFrameRateFlexibilityTokenCount == 0) {
-            // |mStateLock| not needed as we are on the main thread
-            const auto display = getDefaultDisplayDeviceLocked();
+            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
 
             // 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
@@ -6223,8 +6325,7 @@
         mFrameRateFlexibilityTokenCount--;
         ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
         if (mFrameRateFlexibilityTokenCount == 0) {
-            // |mStateLock| not needed as we are on the main thread
-            const auto display = getDefaultDisplayDeviceLocked();
+            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
             constexpr bool kOverridePolicy = true;
             status_t result = setDesiredDisplayConfigSpecsInternal(display, {}, kOverridePolicy);
             LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
@@ -6236,7 +6337,7 @@
     static_cast<void>(schedule([=] {
         std::unique_ptr<RefreshRateOverlay> overlay;
         if (enable) {
-            overlay = std::make_unique<RefreshRateOverlay>(*this);
+            overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
         }
 
         {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 509de0f..28762c9 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>
@@ -59,7 +58,7 @@
 #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 +88,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;
@@ -179,8 +187,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 +223,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 +274,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 +282,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&&);
@@ -289,12 +297,6 @@
     // The CompositionEngine encapsulates all composition related interfaces and actions.
     compositionengine::CompositionEngine& getCompositionEngine() const;
 
-    // returns the default Display
-    sp<const DisplayDevice> getDefaultDisplayDevice() {
-        Mutex::Autolock _l(mStateLock);
-        return getDefaultDisplayDeviceLocked();
-    }
-
     // Obtains a name from the texture pool, or, if the pool is empty, posts a
     // synchronous message to the main thread to obtain one on the fly
     uint32_t getNewTexture();
@@ -302,14 +304,6 @@
     // 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);
-    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
@@ -342,6 +336,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(
+            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;
@@ -358,21 +371,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) {}
@@ -404,29 +414,111 @@
         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(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)
+              : states(composerStates),
+                displays(displayStates),
+                flags(transactionFlags),
+                desiredPresentTime(desiredPresentTime),
+                buffer(uncacheBuffer),
+                postTime(postTime),
+                privileged(privileged),
+                hasListenerCallbacks(hasListenerCallbacks),
+                listenerCallbacks(listenerCallbacks),
+                originPID(originPID),
+                originUID(originUID) {}
+
+        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;
+    };
+
+    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...)) {
+            setVsyncConfig(*config);
+        }
+    }
+
+    static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+
+    // 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(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;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -435,19 +527,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;
@@ -472,7 +557,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,
@@ -505,23 +590,20 @@
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
-    status_t notifyPowerHint(int32_t hintId) override;
+    status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility) override;
     status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
-    /* ------------------------------------------------------------------------
-     * DeathRecipient interface
-     */
+
+    // 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,
@@ -534,15 +616,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
@@ -551,17 +647,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.
@@ -605,9 +690,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&);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -615,7 +702,7 @@
      */
     bool handlePageFlip();
 
-    /* ------------------------------------------------------------------------
+    /*
      * Transactions
      */
     void applyTransactionState(const Vector<ComposerState>& state,
@@ -625,7 +712,8 @@
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               bool isMainThread = false) REQUIRES(mStateLock);
+                               int originPID, int originUID, bool isMainThread = false)
+            REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
@@ -634,7 +722,13 @@
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+    // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+    // where there are still pending transactions but we know they won't be ready until a frame
+    // arrives from a different layer. So we need to ensure we performTransaction from invalidate
+    // but there is no need to try and wake up immediately to do it. Rather we rely on
+    // onFrameAvailable or another layer update to wake us up.
+    void setTraversalNeeded();
+    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
@@ -642,24 +736,7 @@
     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,
@@ -705,81 +782,66 @@
     // 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 renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
+                                    const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd,
+                                    bool regionSampling, ScreenCaptureResults&);
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+                                 ui::PixelFormat, const sp<IScreenCaptureListener>&);
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+                                 bool regionSampling, const sp<IScreenCaptureListener>&);
 
-    void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                ANativeWindowBuffer* 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);
-    const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
-    const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
-    status_t captureScreenImplLocked(const RenderArea& renderArea,
-                                     TraverseLayersFunction traverseLayers,
-                                     ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                     bool forSystem, int* outSyncFd, bool regionSampling,
-                                     bool& outCapturedSecureLayers);
-    void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
-                                 const LayerVector::Visitor& visitor);
+    sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+    sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
 
-    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
     void initializeDisplays();
 
-    // NOTE: can only be called from the main thread or with mStateLock held
-    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const {
+    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
+            REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
     }
 
-    // NOTE: can only be called from the main thread or with mStateLock held
-    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) {
+    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
         const auto it = mDisplays.find(displayToken);
         return it == mDisplays.end() ? nullptr : it->second;
     }
 
-    sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
+    sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
     }
 
-    sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
+    sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
         if (const auto token = getInternalDisplayTokenLocked()) {
             return getDisplayDeviceLocked(token);
         }
         return nullptr;
     }
 
-    std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
+    sp<const DisplayDevice> getDefaultDisplayDevice() EXCLUDES(mStateLock) {
+        Mutex::Autolock lock(mStateLock);
+        return getDefaultDisplayDeviceLocked();
+    }
 
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
-    /* ------------------------------------------------------------------------
+    /*
      * H/W composer
      */
 
@@ -805,7 +867,7 @@
     // acquiring mStateLock.
     HWComposer& getHwComposer() const;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Compositing
      */
     void invalidateHwcGeometry();
@@ -819,7 +881,7 @@
 
     void postFrame();
 
-    /* ------------------------------------------------------------------------
+    /*
      * Display management
      */
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
@@ -827,10 +889,11 @@
             std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& displaySurface,
-            const sp<IGraphicBufferProducer>& producer);
+            const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
     void processDisplayChangesLocked() REQUIRES(mStateLock);
-    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
-    void processDisplayRemoved(const wp<IBinder>& displayToken);
+    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+            REQUIRES(mStateLock);
+    void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
                                const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
@@ -838,15 +901,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);
 
@@ -873,12 +935,14 @@
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const {
+    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 {
+    std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
+            REQUIRES(mStateLock) {
         for (const auto& [id, token] : mPhysicalDisplayTokens) {
             if (token == displayToken) {
                 return id;
@@ -888,12 +952,12 @@
     }
 
     // TODO(b/74619554): Remove special cases for primary display.
-    sp<IBinder> getInternalDisplayTokenLocked() const {
+    sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
         const auto displayId = getInternalDisplayIdLocked();
         return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
     }
 
-    std::optional<DisplayId> getInternalDisplayIdLocked() const {
+    std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
         const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
@@ -901,33 +965,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;
@@ -945,9 +982,9 @@
     void recordBufferingStats(const std::string& layerName,
                               std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(std::string& result) const;
-    void dumpDisplayIdentificationData(std::string& result) const;
+    void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
     void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
-    void dumpWideColorInfo(std::string& result) const;
+    void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
     LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
@@ -971,20 +1008,36 @@
 
     void onFrameRateFlexibilityTokenReleased();
 
-    /* ------------------------------------------------------------------------
+    /*
      * VrFlinger
      */
-    void resetDisplayState();
+    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
@@ -995,7 +1048,7 @@
     bool mTransactionPending = false;
     bool mAnimTransactionPending = false;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
-    bool mTraversalNeededMainThread = false;
+    bool mForceTraversal = false;
 
     // global color transform states
     Daltonizer mDaltonizer;
@@ -1005,6 +1058,10 @@
     // Can't be unordered_set because wp<> isn't hashable
     std::set<wp<IBinder>> mGraphicBufferProducerList;
     size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+    // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
+    // this threshold, then begin logging.
+    size_t mGraphicBufferProducerListSizeLogThreshold =
+            static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
 
     void removeGraphicBufferProducerAsync(const wp<IBinder>&);
 
@@ -1027,7 +1084,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};
@@ -1042,27 +1103,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;
-    };
-    // protected by mStateLock
-    std::vector<HotplugEvent> mPendingHotplugEvents;
+    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;
-    std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
+    std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+    std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
+            GUARDED_BY(mStateLock);
 
-    // protected by mStateLock
-    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken;
+    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
 
     // don't use a lock for these, we don't care
     int mDebugRegion = 0;
@@ -1070,7 +1121,6 @@
     bool mDebugDisableTransformHint = false;
     volatile nsecs_t mDebugInTransaction = 0;
     bool mForceFullDamage = false;
-    bool mPropagateBackpressure = true;
     bool mPropagateBackpressureClientComposition = false;
     std::unique_ptr<SurfaceInterceptor> mInterceptor;
 
@@ -1082,6 +1132,7 @@
 
     const std::shared_ptr<TimeStats> mTimeStats;
     const std::unique_ptr<FrameTracer> mFrameTracer;
+    const std::shared_ptr<frametimeline::FrameTimeline> mFrameTimeline;
     bool mUseHwcVirtualDisplays = false;
     // If blurs should be enabled on this device.
     bool mSupportsBlur = false;
@@ -1117,35 +1168,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
      */
 
@@ -1154,10 +1179,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
@@ -1188,7 +1209,7 @@
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
 
-    /* ------------------------------------------------------------------------
+    /*
      * Scheduler
      */
     std::unique_ptr<Scheduler> mScheduler;
@@ -1196,30 +1217,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
@@ -1241,21 +1248,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;
@@ -1275,14 +1273,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..fb08e69 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,21 +54,18 @@
     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(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index bd40cfb..86e5a7a 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -26,14 +26,11 @@
 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<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6f4fcc6..753476e 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,16 +67,11 @@
 // 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;
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 80102bd..c15d0df 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -143,7 +143,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 +221,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)
 {
@@ -483,18 +490,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) {
     Transaction* transaction(increment->mutable_transaction());
     transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
     transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+    setTransactionOriginLocked(transaction, originPID, originUID);
+
     for (const auto& compState: stateUpdates) {
         addSurfaceChangesLocked(transaction, compState.state);
     }
@@ -613,17 +622,17 @@
     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) {
     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);
 }
 
 void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 896bdcc..1798b5a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -62,7 +62,8 @@
     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) = 0;
 
     // Intercept surface data
     virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
@@ -97,7 +98,8 @@
     // 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) override;
 
     // Intercept surface data
     void saveSurfaceCreation(const sp<const Layer>& layer) override;
@@ -160,8 +162,9 @@
             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);
     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,6 +185,8 @@
     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};
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index c90b1b8..894ee6d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -87,7 +87,9 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     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());
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 4e7f67d..49cf80c 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.");
 
@@ -57,12 +77,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/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..f3f5626 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -123,6 +123,8 @@
   bool is_relative_of = 51;
   // Layer's background blur radius in pixels.
   int32 background_blur_radius = 52;
+
+  uint32 owner_uid = 53;
 }
 
 message PositionProto {
@@ -191,17 +193,18 @@
 
     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 {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index cfc301b..7b1f0fb 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -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
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 94ebe89..719c46e 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,10 +21,13 @@
         "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",
@@ -32,6 +35,7 @@
         "MirrorLayer_test.cpp",
         "MultiDisplayLayerBounds_test.cpp",
         "RelativeZ_test.cpp",
+        "ScreenCapture_test.cpp",
         "SetFrameRate_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
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..e2c038d 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
 
 /**
@@ -188,7 +207,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));
@@ -260,9 +279,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 +291,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..3261308
--- /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->getHandle(), 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->getHandle())
+            .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->getHandle(),
+                                          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->getHandle(),
+                                          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->getHandle()).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/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
new file mode 100644
index 0000000..3dca391
--- /dev/null
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "LayerTransactionTest.h"
+
+namespace android {
+
+class EffectLayerTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+    }
+
+    sp<SurfaceControl> mParentLayer;
+};
+
+TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Default effect Layer has solid black fill");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerWithNoFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect layer with nofill option is transparent");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::RED);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerCanSetColor) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setColor(effectLayer,
+                   half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
+                         Color::GREEN.b / 255.0f});
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect Layer can set color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN);
+    }
+}
+
+} // 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/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 42d1f5a..cfec0d2 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 {
@@ -56,11 +61,11 @@
 }
 
 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/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..d4e952a 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -40,6 +40,8 @@
 
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+
+        mCaptureArgs.displayToken = mDisplay;
     }
 
     virtual void TearDown() {
@@ -249,6 +251,9 @@
     sp<SurfaceControl> mBlackBgSurface;
     bool mColorManagementUsed;
 
+    DisplayCaptureArgs mCaptureArgs;
+    ScreenCaptureResults mCaptureResults;
+
 private:
     void SetUpDisplay() {
         mDisplay = mClient->getInternalDisplayToken();
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 97cba63..8d715e1 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(
@@ -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 d666d7e..7d4314f 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -220,6 +220,51 @@
         shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
         shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
         shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+    auto transaction = Transaction()
+                               .setCornerRadius(parent, cornerRadius)
+                               .setCrop_legacy(parent, Rect(0, 0, size, size))
+                               .reparent(child, parent->getHandle())
+                               .setPosition(child, 0, size)
+                               // Rotate by half PI
+                               .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+    } else {
+        transaction.setFrame(parent, Rect(0, 0, size, size));
+    }
+    transaction.apply();
+
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Edges are transparent
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom - testArea), Color::BLACK);
+        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::GREEN);
     }
 }
 
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 84780ba..ab74c50 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -61,8 +61,10 @@
 
     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);
 }
 
@@ -165,17 +167,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..c56d473 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) {
@@ -588,174 +553,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);
@@ -1287,432 +1084,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/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/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 1180cac..3e0b3c6 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -207,6 +207,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->getHandle(), 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->getHandle()).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->getHandle()).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..962a0cf
--- /dev/null
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -0,0 +1,713 @@
+/*
+ * 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, 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->getHandle(), 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->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.
+    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, 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->getHandle())
+            .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
\ No newline at end of file
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/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..87fc08a 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;
@@ -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;
@@ -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;
@@ -808,7 +814,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 +840,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 +994,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;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7574ff1..6f1f1f2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -40,14 +40,13 @@
         "DispSyncSourceTest.cpp",
         "DisplayIdentificationTest.cpp",
         "DisplayTransactionTest.cpp",
-        "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
+        "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
         "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
-        "PhaseOffsetsTest.cpp",
         "PromiseTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
@@ -58,29 +57,33 @@
         "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",
         "libcompositionengine_mocks",
+        "libframetimeline",
         "libgui_mocks",
         "libperfetto_client_experimental",
         "librenderengine_mocks",
@@ -89,7 +92,6 @@
     shared_libs: [
         "libprotoutil",
         "libstatssocket",
-        "libsurfaceflinger",
         "libtimestats",
         "libtimestats_proto",
     ],
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8713b2b..159a215 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -35,16 +35,16 @@
 #include <utils/String8.h>
 
 #include "BufferQueueLayer.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 +80,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;
 
@@ -142,16 +142,22 @@
                         new EventThreadConnection(sfEventThread.get(), 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() {
@@ -229,28 +235,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);
     }
@@ -348,7 +353,7 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                                    base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -397,7 +402,7 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                                    base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -648,7 +653,7 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -697,7 +702,7 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -775,7 +780,7 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -891,7 +896,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 +935,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();
@@ -1073,7 +1077,8 @@
 
 struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
     static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay);
+        const auto outputLayer =
+                TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
         LOG_FATAL_IF(!outputLayer);
         outputLayer->editState().forceClientComposition = true;
     }
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/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..02ce079 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -22,6 +22,8 @@
 
 #include "DisplayHardware/DisplayIdentification.h"
 
+using ::testing::ElementsAre;
+
 namespace android {
 namespace {
 
@@ -312,93 +314,92 @@
     using ManufactureYear = DeviceProductInfo::ManufactureYear;
     using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
     using ModelYear = DeviceProductInfo::ModelYear;
-    using RelativeAddress = DeviceProductInfo::RelativeAddress;
 
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("", info.name.data());
+        EXPECT_EQ("", info.name);
         EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
-        EXPECT_STREQ("12610", info.productId.data());
+        EXPECT_EQ("12610", info.productId);
         ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
         EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
-        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+        EXPECT_TRUE(info.relativeAddress.empty());
     }
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("HP ZR30w", info.name.data());
+        EXPECT_EQ("HP ZR30w", info.name);
         EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
-        EXPECT_STREQ("10348", info.productId.data());
+        EXPECT_EQ("10348", info.productId);
         ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
         const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
         EXPECT_EQ(2012, date.year);
         EXPECT_EQ(2, date.week);
-        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+        EXPECT_TRUE(info.relativeAddress.empty());
     }
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("SAMSUNG", info.name.data());
+        EXPECT_EQ("SAMSUNG", info.name);
         EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
-        EXPECT_STREQ("2302", info.productId.data());
+        EXPECT_EQ("2302", info.productId);
         ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
         const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
         EXPECT_EQ(2011, date.year);
         EXPECT_EQ(41, date.week);
-        EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+        EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
     }
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("Panasonic-TV", info.name.data());
+        EXPECT_EQ("Panasonic-TV", info.name);
         EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
-        EXPECT_STREQ("41622", info.productId.data());
+        EXPECT_EQ("41622", info.productId);
         ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
         const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
         EXPECT_EQ(2019, date.year);
-        EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+        EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
     }
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("Hisense", info.name.data());
+        EXPECT_EQ("Hisense", info.name);
         EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
-        EXPECT_STREQ("0", info.productId.data());
+        EXPECT_EQ("0", info.productId);
         ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
         const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
         EXPECT_EQ(2019, date.year);
         EXPECT_EQ(18, date.week);
-        EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+        EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
     }
     {
         const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("LP2361", info.name.data());
+        EXPECT_EQ("LP2361", info.name);
         EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
-        EXPECT_STREQ("9373", info.productId.data());
+        EXPECT_EQ("9373", info.productId);
         ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
         EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
-        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+        EXPECT_TRUE(info.relativeAddress.empty());
     }
 }
 
-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 ce5f35c..b939b9a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -23,6 +23,7 @@
 
 #include <type_traits>
 
+#include <android/hardware/power/Boost.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/impl/Display.h>
@@ -44,12 +45,12 @@
 #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/MockSchedulerCallback.h"
 #include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -57,6 +58,8 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using android::hardware::power::Boost;
+
 using testing::_;
 using testing::AnyNumber;
 using testing::DoAll;
@@ -85,7 +88,7 @@
 using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
 using HWC2Display = TestableSurfaceFlinger::HWC2Display;
 
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+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;
 
@@ -152,8 +155,9 @@
     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::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;
 
@@ -210,10 +214,10 @@
             .WillOnce(Return(new EventThreadConnection(mSFEventThread, 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) {
@@ -250,7 +254,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;
@@ -329,10 +333,10 @@
  */
 
 template <typename PhysicalDisplay>
-struct PhysicalDisplayId {};
+struct PhysicalDisplayIdType {};
 
-template <DisplayId::Type displayId>
-using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
+template <uint64_t displayId>
+using VirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
 
 struct NoDisplayId {};
 
@@ -340,18 +344,18 @@
 struct IsPhysicalDisplayId : std::bool_constant<false> {};
 
 template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
 
 template <typename>
 struct DisplayIdGetter;
 
 template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
-    static std::optional<DisplayId> get() {
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+    static std::optional<PhysicalDisplayId> get() {
         if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
-            return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
-                                                ? LEGACY_DISPLAY_TYPE_PRIMARY
-                                                : LEGACY_DISPLAY_TYPE_EXTERNAL);
+            return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+                                                       ? LEGACY_DISPLAY_TYPE_PRIMARY
+                                                       : LEGACY_DISPLAY_TYPE_EXTERNAL);
         }
 
         const auto info =
@@ -361,9 +365,10 @@
     }
 };
 
-template <DisplayId::Type displayId>
-struct DisplayIdGetter<VirtualDisplayId<displayId>> {
-    static std::optional<DisplayId> get() { return DisplayId{displayId}; }
+template <uint64_t displayId>
+struct DisplayIdGetter<VirtualDisplayIdType<displayId>> {
+    // TODO(b/160679868) Use VirtualDisplayId
+    static std::optional<PhysicalDisplayId> get() { return PhysicalDisplayId{displayId}; }
 };
 
 template <>
@@ -377,7 +382,7 @@
 };
 
 template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
     static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
 };
 
@@ -388,19 +393,19 @@
 
 constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
 
-template <DisplayId::Type displayId>
-struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<VirtualDisplayIdType<displayId>> {
     static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
 };
 
 template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<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.
+//     1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+//     2) VirtualDisplayIdType<...> 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>
@@ -628,10 +633,11 @@
 
 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>,
+      : DisplayVariant<PhysicalDisplayIdType<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,
+                          DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
                                          critical, Async::FALSE, Secure::TRUE,
                                          PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
                           PhysicalDisplay> {};
@@ -717,14 +723,14 @@
 
 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>;
+      : DisplayVariant<VirtualDisplayIdType<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<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
+                                         Async::TRUE, secure, Primary::FALSE,
+                                         GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+    using Base = DisplayVariant<VirtualDisplayIdType<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(
@@ -1319,10 +1325,8 @@
     // Call Expectations
 
     // The call disable vsyncs
-    EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, setVsyncEnabled(false)).Times(1);
 
-    // The call ends any display resyncs
-    EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
 
     // --------------------------------------------------------------------
     // Invocation
@@ -1347,6 +1351,30 @@
 }
 
 /* ------------------------------------------------------------------------
+ * 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 {
@@ -1495,10 +1523,9 @@
                                 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(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -1508,11 +1535,12 @@
                                 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(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);
     }
 
@@ -1522,10 +1550,8 @@
                                 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(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -1535,11 +1561,12 @@
                                 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(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);
     }
 
@@ -1794,7 +1821,9 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        state.physical = {*displayId, *connectionType, *hwcDisplayId};
+        state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+                          .type = *connectionType,
+                          .hwcDisplayId = *hwcDisplayId};
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1970,7 +1999,9 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+        expectedPhysical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+                            .type = *connectionType,
+                            .hwcDisplayId = *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
@@ -2457,11 +2488,11 @@
     EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) {
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    const Rect oldViewport(0, 0, 0, 0);
-    const Rect newViewport(0, 0, 123, 456);
+    const Rect oldLayerStackRect(0, 0, 0, 0);
+    const Rect newLayerStackRect(0, 0, 123, 456);
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2470,9 +2501,9 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().viewport = oldViewport;
-    display.mutableCurrentDisplayState().viewport = newViewport;
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+    display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2482,7 +2513,7 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport());
+    EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
 }
 
 TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
@@ -2498,9 +2529,9 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // There is a change to the viewport state
-    display.mutableDrawingDisplayState().frame = oldFrame;
-    display.mutableCurrentDisplayState().frame = newFrame;
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2510,7 +2541,7 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame());
+    EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
 }
 
 TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
@@ -2541,7 +2572,7 @@
     EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
     display.inject();
 
-    // There is a change to the viewport state
+    // There is a change to the layerStackSpaceRect state
     display.mutableDrawingDisplayState().width = oldWidth;
     display.mutableDrawingDisplayState().height = oldHeight;
     display.mutableCurrentDisplayState().width = newWidth;
@@ -2586,7 +2617,7 @@
     EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
     display.inject();
 
-    // There is a change to the viewport state
+    // There is a change to the layerStackSpaceRect state
     display.mutableDrawingDisplayState().width = oldWidth;
     display.mutableDrawingDisplayState().height = oldHeight;
     display.mutableCurrentDisplayState().width = oldWidth;
@@ -2807,8 +2838,8 @@
 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};
+    const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+    const Rect initialLayerStackRect = {5, 6, 7, 8};
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2819,16 +2850,16 @@
 
     // The current display state projection state is all set
     display.mutableCurrentDisplayState().orientation = initialOrientation;
-    display.mutableCurrentDisplayState().frame = initialFrame;
-    display.mutableCurrentDisplayState().viewport = initialViewport;
+    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.frame = initialFrame;
-    state.viewport = initialViewport;
+    state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+    state.layerStackSpaceRect = initialLayerStackRect;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2844,8 +2875,9 @@
     // The current display state is unchanged
     EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
 
-    EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame);
-    EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport);
+    EXPECT_EQ(initialOrientedDisplayRect,
+              display.getCurrentDisplayState().orientedDisplaySpaceRect);
+    EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
 }
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
@@ -2886,8 +2918,8 @@
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
     using Case = SimplePrimaryDisplayCase;
-    const Rect initialFrame = {0, 0, 0, 0};
-    const Rect desiredFrame = {5, 6, 7, 8};
+    const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+    const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2896,14 +2928,14 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The current display state does not have a frame
-    display.mutableCurrentDisplayState().frame = initialFrame;
+    // The current display state does not have a orientedDisplaySpaceRect
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
 
-    // The incoming request sets a frame
+    // The incoming request sets a orientedDisplaySpaceRect
     DisplayState state;
     state.what = DisplayState::eDisplayProjectionChanged;
     state.token = display.token();
-    state.frame = desiredFrame;
+    state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2917,13 +2949,14 @@
     EXPECT_EQ(eDisplayTransactionNeeded, flags);
 
     // The current display state has the new value.
-    EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame);
+    EXPECT_EQ(desiredOrientedDisplayRect,
+              display.getCurrentDisplayState().orientedDisplaySpaceRect);
 }
 
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) {
+TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
     using Case = SimplePrimaryDisplayCase;
-    const Rect initialViewport = {0, 0, 0, 0};
-    const Rect desiredViewport = {5, 6, 7, 8};
+    const Rect initialLayerStackRect = {0, 0, 0, 0};
+    const Rect desiredLayerStackRect = {5, 6, 7, 8};
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2932,14 +2965,14 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The current display state does not have a viewport
-    display.mutableCurrentDisplayState().viewport = initialViewport;
+    // The current display state does not have a layerStackSpaceRect
+    display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
 
-    // The incoming request sets a viewport
+    // The incoming request sets a layerStackSpaceRect
     DisplayState state;
     state.what = DisplayState::eDisplayProjectionChanged;
     state.token = display.token();
-    state.viewport = desiredViewport;
+    state.layerStackSpaceRect = desiredLayerStackRect;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2953,7 +2986,7 @@
     EXPECT_EQ(eDisplayTransactionNeeded, flags);
 
     // The current display state has the new value.
-    EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport);
+    EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
 }
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
@@ -3097,7 +3130,7 @@
     // processing.
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -3115,11 +3148,11 @@
     // 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 orientedDisplaySpaceRect state should be set to INVALID
+    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
 
-    // The viewport state should be set to INVALID
-    EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport);
+    // 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);
@@ -3130,7 +3163,7 @@
     auto displayDevice = primaryDisplay.mutableDisplayDevice();
     EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
 
-    // The display refresh period should be set in the frame tracker.
+    // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
     FrameStats stats;
     mFlinger.getAnimFrameTracker().getStats(&stats);
     EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
@@ -3183,9 +3216,9 @@
 };
 
 struct EventThreadBaseSupportedVariant {
-    static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
-        // The event control thread should not be notified.
-        EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
+    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);
@@ -3198,29 +3231,29 @@
         // These calls are only expected for the primary display.
 
         // Instead expect no calls.
-        setupEventAndEventControlThreadNoCallExpectations(test);
+        setupVsyncAndEventThreadNoCallExpectations(test);
     }
 
     static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         // These calls are only expected for the primary display.
 
         // Instead expect no calls.
-        setupEventAndEventControlThreadNoCallExpectations(test);
+        setupVsyncAndEventThreadNoCallExpectations(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 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) {
-        // There should be a call to setVsyncEnabled(false)
-        EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
+        // 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);
@@ -3228,20 +3261,14 @@
 };
 
 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);
+    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 setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
-    static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+    static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
 };
 
 // --------------------------------------------------------------------
@@ -3264,7 +3291,7 @@
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -3279,7 +3306,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -3293,7 +3320,6 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
-        Case::DispSync::setupEndResyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -3306,7 +3332,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -3318,7 +3344,7 @@
 struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -3328,7 +3354,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -3336,7 +3362,7 @@
 struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -3346,7 +3372,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -3356,7 +3382,6 @@
     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);
     }
 };
@@ -3365,7 +3390,7 @@
       : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
         Case::setupNoComposerPowerModeCallExpectations(test);
     }
 };
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..ae94f16 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&));
 };
@@ -74,7 +76,8 @@
                                                    ISurfaceComposer::ConfigChanged configChanged);
 
     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,
@@ -89,7 +92,8 @@
 
     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;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
@@ -114,8 +118,8 @@
     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,
@@ -159,10 +163,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() {
@@ -229,7 +235,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,14 +264,14 @@
 
     // 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);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
     // The interceptor should receive the event, but the 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(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -299,7 +305,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,17 +320,17 @@
 
     // 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);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    mCallback->onVSyncEvent(456, 123);
+    mCallback->onVSyncEvent(456, 123, 0);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    mCallback->onVSyncEvent(789, 777);
+    mCallback->onVSyncEvent(789, 777, 111);
     expectInterceptCallReceived(789);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -336,22 +342,22 @@
     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());
 
     // 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);
 
     // 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());
 
     // 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 +372,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 +392,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());
 
@@ -420,7 +426,7 @@
 
     // 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,
@@ -440,13 +446,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 +461,8 @@
 }
 
 TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
-    mThread->setPhaseOffset(321);
-    expectVSyncSetPhaseOffsetCallReceived(321);
+    mThread->setDuration(321ns, 456ns);
+    expectVSyncSetDurationCallReceived(321ns, 456ns);
 }
 
 TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
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..85d2834
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 <FrameTimeline/FrameTimeline.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <cinttypes>
+
+using namespace std::chrono_literals;
+
+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 {
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>();
+        mTokenManager = &mFrameTimeline->mTokenManager;
+        maxDisplayFrames = mFrameTimeline->kMaxDisplayFrames;
+        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;
+    }
+
+    std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
+    impl::TokenManager* mTokenManager;
+    FenceToFenceTimeMap fenceFactory;
+    uint64_t maxDisplayFrames;
+    nsecs_t maxTokenRetentionTime;
+};
+
+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_noToken) {
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", 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("layer1", token1);
+
+    EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
+    int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", 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("layer1", 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("layer1", surfaceFrameToken1);
+    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", 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("layer1", 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("layer1", 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("layer1", 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);
+}
+
+} // 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 71d17a9..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();
@@ -96,14 +105,14 @@
 
     // no layers are returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, mTime);
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_TRUE(history().summarize(mTime).empty());
         EXPECT_EQ(1, activeLayerCount());
     }
 
     // High FPS is returned once enough history has been recorded.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, mTime);
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(mTime).size());
         EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
         EXPECT_EQ(1, activeLayerCount());
@@ -121,7 +130,7 @@
 
     nsecs_t time = mTime;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -155,7 +164,7 @@
 
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time);
+        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -166,12 +175,12 @@
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
     // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time);
+    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -182,7 +191,7 @@
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -196,10 +205,10 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time);
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -209,7 +218,7 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
-    history().record(layer3.get(), time, time);
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
     EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
@@ -228,7 +237,7 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -246,7 +255,7 @@
 
     // layer3 becomes active and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 6fca673..cb376cd 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LayerHistoryTestV2"
 
+#include <Layer.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -26,6 +27,7 @@
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
 
 using testing::_;
 using testing::Return;
@@ -37,6 +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_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);
@@ -46,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(); }
 
@@ -59,6 +66,13 @@
                              [now](const auto& pair) { return pair.second->isFrequent(now); });
     }
 
+    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isAnimating(now); });
+    }
+
     void setLayerInfoVote(Layer* layer,
                           LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
         for (auto& [weak, info] : history().mLayerInfos) {
@@ -71,6 +85,26 @@
     }
 
     auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+    auto createLayer(std::string name) {
+        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)
@@ -82,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 {
@@ -105,7 +144,7 @@
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, time);
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -113,7 +152,7 @@
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, time);
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -130,7 +169,7 @@
 
     nsecs_t time = systemTime();
 
-    history().record(layer.get(), 0, time);
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
     auto summary = history().summarize(time);
     ASSERT_EQ(1, history().summarize(time).size());
     // Layer is still considered inactive so we expect to get Min
@@ -154,7 +193,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -177,7 +216,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -204,7 +243,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -232,7 +271,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -260,7 +299,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -292,7 +331,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -334,43 +373,48 @@
     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);
+        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));
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        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);
+    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++) {
-        history().record(layer2.get(), time, time);
+    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));
 
@@ -379,38 +423,41 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time);
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time);
+        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);
-    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);
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    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));
@@ -418,39 +465,43 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        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++) {
-        history().record(layer3.get(), time, time);
+    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));
@@ -466,7 +517,7 @@
 
     // the very first updates makes the layer frequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
         EXPECT_EQ(1, layerCount());
@@ -477,7 +528,7 @@
     }
 
     // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer.get(), time, time);
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     EXPECT_EQ(1, layerCount());
@@ -491,7 +542,7 @@
 
     // Now event if we post a quick few frame we should stay infrequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
 
         EXPECT_EQ(1, layerCount());
@@ -502,7 +553,7 @@
     }
 
     // More quick frames will get us to frequent again
-    history().record(layer.get(), time, time);
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     time += HI_FPS_PERIOD;
 
     EXPECT_EQ(1, layerCount());
@@ -512,5 +563,194 @@
     EXPECT_EQ(1, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
+    auto explicitVisiblelayer = createLayer();
+    auto explicitInvisiblelayer = createLayer();
+
+    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer.get(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    EXPECT_EQ(2, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // another update with the same cadence keep in infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // an update as animation will immediately vote for Max
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    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> {};
+
+TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
+    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+    auto heuristicLayer = createLayer("HeuristicLayer");
+
+    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    auto infrequentLayer = createLayer("InfrequentLayer");
+    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    const nsecs_t startTime = systemTime();
+
+    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+    history().record(heuristicLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    nsecs_t time = startTime;
+    nsecs_t lastInfrequentUpdate = startTime;
+    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+    int infrequentLayerUpdates = 0;
+    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+        time += heuristicUpdateDelta.count();
+        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+            ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+                  totalInfrequentLayerUpdates);
+            lastInfrequentUpdate = time;
+            history().record(infrequentLayer.get(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
+            infrequentLayerUpdates++;
+        }
+
+        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+            ASSERT_NE(0, history().summarize(time).size());
+            ASSERT_GE(2, history().summarize(time).size());
+
+            bool max = false;
+            bool min = false;
+            float heuristic = 0;
+            for (const auto& layer : history().summarize(time)) {
+                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+                    heuristic = layer.desiredRefreshRate;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+                    max = true;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+                    min = true;
+                }
+            }
+
+            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+                EXPECT_FLOAT_EQ(24.0f, heuristic);
+                EXPECT_FALSE(max);
+                if (history().summarize(time).size() == 2) {
+                    EXPECT_TRUE(min);
+                }
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
+                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
 } // namespace
 } // namespace android::scheduler
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 2b168b2..1f6f166 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,27 +344,22 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_72);
 
-    // If there are not layers, there is not content detection, so return the current
-    // refresh rate.
+    // If there are no layers we select the default frame rate, which is the max of the primary
+    // range.
     auto layers = std::vector<LayerRequirement>{};
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
-                                                     false, /*idle*/ false, &ignored));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 
-    // Current refresh rate can always be changed.
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+    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);
@@ -366,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);
@@ -532,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);
@@ -583,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);
@@ -621,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;
@@ -631,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;
@@ -641,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;
@@ -651,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;
@@ -661,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;
@@ -671,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;
@@ -681,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;
@@ -691,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;
@@ -701,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);
@@ -716,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);
@@ -763,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);
@@ -840,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);
@@ -901,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();
     }
 }
 
@@ -932,7 +853,6 @@
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -947,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) {
@@ -976,7 +893,6 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
-    bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -988,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);
@@ -1013,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;
@@ -1023,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;
@@ -1032,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;
@@ -1042,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;
@@ -1051,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);
@@ -1072,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;
@@ -1080,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;
@@ -1088,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;
@@ -1096,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;
@@ -1106,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}};
@@ -1132,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;
@@ -1141,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;
@@ -1150,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;
@@ -1159,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);
@@ -1200,12 +1110,132 @@
         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";
     }
 }
 
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+              0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    RefreshRateConfigs::GlobalSignals consideredSignals;
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz ExplicitDefault";
+    lr.focused = true;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+                                                     &consideredSignals));
+    EXPECT_EQ(false, consideredSignals.touch);
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+              0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 90.0f;
+    lr.name = "90Hz ExplicitDefault";
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+              0);
+
+    RefreshRateConfigs::GlobalSignals consideredSignals;
+    EXPECT_EQ(mExpected90Config,
+              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];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz ExplicitExactOrMultiple";
+    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, {.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, {.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, {.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, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, groupSwitching) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
@@ -1217,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;
@@ -1228,14 +1256,13 @@
     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());
 }
 
 TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
+            std::make_unique<RefreshRateConfigs>(m30_60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
@@ -1243,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, {60.f, 60.f}, {60.f, 90.f}}),
+                      {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_60, getFrameRate(LayerVoteType::Min, 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(
@@ -1296,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(
@@ -1310,38 +1347,130 @@
               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());
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 43b8e01..409f90d 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();
@@ -140,18 +135,18 @@
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), 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 +278,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..35619a3 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,32 +14,28 @@
  * 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:
@@ -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,90 @@
     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();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 0d6c799..d4591fc 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 {
 
@@ -176,15 +175,15 @@
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), 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/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41b5d49..db3e0bd 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,80 +19,88 @@
 #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();
+        }
+        mTouchTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
+        mTouchTimer->start();
+    }
+
+    bool isTouchActive() {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        return mFeatures.touch == Scheduler::TouchState::Active;
+    }
+
     ~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 add3327..b024568 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"
@@ -65,15 +65,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,13 +73,12 @@
         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;
     }
@@ -175,8 +165,10 @@
 
 } // namespace surfaceflinger::test
 
-class TestableSurfaceFlinger {
+class TestableSurfaceFlinger final : private ISchedulerCallback {
 public:
+    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
     SurfaceFlinger* flinger() { return mFlinger.get(); }
     TestableScheduler* scheduler() { return mScheduler; }
 
@@ -196,37 +188,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); }
@@ -246,28 +244,32 @@
         memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
     }
 
-    using HotplugEvent = SurfaceFlinger::HotplugEvent;
-
-    auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
-    auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
+    static auto& mutableLayerCurrentState(const sp<Layer>& layer) { return layer->mCurrentState; }
+    static auto& mutableLayerDrawingState(const sp<Layer>& layer) { return layer->mDrawingState; }
 
     auto& mutableStateLock() { return mFlinger->mStateLock; }
 
-    void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+    static auto findOutputLayerForDisplay(const sp<Layer>& layer,
+                                          const sp<const DisplayDevice>& display) {
+        return layer->findOutputLayerForDisplay(display.get());
+    }
+
+    static void setLayerSidebandStream(const sp<Layer>& layer,
+                                       const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
         layer->editCompositionState()->sidebandStream = sidebandStream;
     }
 
-    void setLayerCompositionType(sp<Layer> layer, hal::Composition type) {
-        auto outputLayer = layer->findOutputLayerForDisplay(mFlinger->getDefaultDisplayDevice());
+    void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+        auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
         LOG_ALWAYS_FATAL_IF(!outputLayer);
         auto& state = outputLayer->editState();
         LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
         (*state.hwc).hwcCompositionType = type;
-    };
+    }
 
-    void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+    static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
         layer->mPotentialCursor = potentialCursor;
     }
 
@@ -283,14 +285,14 @@
         return mFlinger->destroyDisplay(displayToken);
     }
 
-    auto resetDisplayState() { return mFlinger->resetDisplayState(); }
+    auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
 
     auto setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& dispSurface,
-            const sp<IGraphicBufferProducer>& producer) {
+            const sp<IGraphicBufferProducer>& producer) NO_THREAD_SAFETY_ANALYSIS {
         return mFlinger->setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
                                                        dispSurface, producer);
     }
@@ -315,6 +317,8 @@
         return mFlinger->onInitializeDisplays();
     }
 
+    auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
     // Allow reading display state without locking, as if called on the SF main thread.
     auto setPowerModeInternal(const sp<DisplayDevice>& display,
                               hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -323,19 +327,18 @@
 
     auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
 
-    auto captureScreenImplLocked(const RenderArea& renderArea,
-                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
-                                 ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                 bool forSystem, int* outSyncFd, bool regionSampling) {
-        bool ignored;
-        return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
-                                                 useIdentityTransform, forSystem, outSyncFd,
-                                                 regionSampling, ignored);
+    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 traverseLayersInDisplay(const sp<const DisplayDevice>& display,
-                                 const LayerVector::Visitor& visitor) {
-        return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+    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,
@@ -627,7 +630,9 @@
             if (const auto type = mCreationArgs.connectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-                state.physical = {*displayId, *type, *mHwcDisplayId};
+                state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+                                  .type = *type,
+                                  .hwcDisplayId = *mHwcDisplayId};
             }
 
             state.isSecure = mCreationArgs.isSecure;
@@ -651,6 +656,12 @@
         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;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7a1c7c6..0a24650 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -221,7 +221,7 @@
 }
 
 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) {
@@ -424,7 +424,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 +505,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()));
@@ -833,6 +833,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) {
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..28415bc 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 {
 
@@ -76,12 +75,12 @@
                         new EventThreadConnection(sfEventThread.get(), 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 +88,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;
@@ -126,7 +125,7 @@
         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);
@@ -159,7 +158,7 @@
         // 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,
@@ -182,7 +181,7 @@
         // 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;
@@ -247,7 +246,7 @@
     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
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index be49ef3..1e5139c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    bool needsMoreSamples() const final { return false; }
     void dump(std::string&) const final {}
 
 private:
@@ -64,7 +65,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 +75,19 @@
     }
 
     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; }
     void dump(std::string&) const final {}
 
 private:
@@ -102,30 +104,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 +142,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 +150,7 @@
     }
 
     nsecs_t const mWorkload;
+    nsecs_t const mReadyDuration;
     VSyncCallbackRegistration mCallback;
 
     std::mutex mMutex;
@@ -158,9 +167,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 +194,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 +212,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..69731fd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -63,19 +64,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 +90,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 +109,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 +144,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 +164,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 +194,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 +224,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 +256,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 +272,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 +328,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 +362,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 +390,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 +446,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 +457,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 +470,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 +493,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 +532,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 +562,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 +603,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 +665,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 +909,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 +917,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 +939,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 +954,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 +994,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 +1015,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 +1028,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 +1069,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..d4cd11d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -73,27 +73,27 @@
 
 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{
@@ -288,20 +320,6 @@
     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));
-}
-
 TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
     auto const simulatedVsyncs =
             generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
@@ -411,7 +429,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());
         }
     }
 }
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 3f14d65..0dcaf26 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,28 +41,10 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
     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,88 +63,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;
@@ -177,7 +117,7 @@
     VSyncDispatch::CallbackToken const mFakeToken{2398};
 
     nsecs_t lastCallbackTime = 0;
-    StubCallback outerCb;
+    // StubCallback outerCb;
     std::function<void(nsecs_t, nsecs_t)> innerCb;
 
     VSyncReactor mReactor;
@@ -212,7 +152,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 {
@@ -253,86 +193,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);
 }
 
@@ -341,16 +243,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);
 }
 
@@ -365,9 +269,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);
 }
 
@@ -385,22 +290,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);
 }
 
@@ -409,7 +314,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);
 }
 
@@ -417,23 +322,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);
     }
 }
@@ -442,263 +347,146 @@
     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)));
 }
 
-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(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
+    // Create a reactor which supports the kernel idle timer
+    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
+                                    kPendingLimit, true /* supportKernelIdleTimer */);
 
-TEST_F(VSyncReactorDeathTest, invalidChange) {
-    EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
+    idleReactor.setIgnorePresentFences(true);
 
-    // 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);
-}
+    // 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);
+    // 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);
 
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
-    ON_CALL(*mMockDispatch, schedule(_, _, _))
-            .WillByDefault(Return(ScheduleResult::CannotSchedule));
-    EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
-}
+    // 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);
 
-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), ".*");
+    EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
 } // 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.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2a31078..c2c5072 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -140,6 +140,7 @@
                              const std::vector<uint8_t>&));
     MOCK_METHOD1(getLayerGenericMetadataKeys,
                  V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
+    MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index dade9fc..fe99e77 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -93,6 +93,7 @@
     MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
     MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
     MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
+    MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
     MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
     MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
 };
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/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 054aaf8..eefdec1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -35,7 +35,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> &));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 119f580..078d8e07 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,8 +24,9 @@
 
 class MockLayer : public Layer {
 public:
-    explicit MockLayer(SurfaceFlinger* flinger)
-          : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+    MockLayer(SurfaceFlinger* flinger, std::string name)
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+    explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
 
     MOCK_CONST_METHOD0(getType, const char*());
     MOCK_METHOD0(getFrameSelectionPriority, int32_t());
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.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 5beee1c..ccb7bb9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -33,10 +33,10 @@
                       const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
     MOCK_METHOD0(disable, void());
     MOCK_METHOD0(isEnabled, bool());
-    MOCK_METHOD4(saveTransaction,
+    MOCK_METHOD6(saveTransaction,
                  void(const Vector<ComposerState>&,
                       const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
-                      const Vector<DisplayState>&, uint32_t));
+                      const Vector<DisplayState>&, uint32_t, int, int));
     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));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 81%
rename from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index f9bacc8..8a18123 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/MockEventControlThread.h"
+#include "mock/MockVSyncTracker.h"
+#include <thread>
 
+using namespace std::chrono_literals;
 namespace android {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
 
 } // namespace mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
new file mode 100644
index 0000000..03ddc85
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -0,0 +1,39 @@
+/*
+ * 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_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/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
similarity index 60%
rename from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
rename to services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 6ef352a..1d87546 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -18,17 +18,22 @@
 
 #include <gmock/gmock.h>
 
-#include "Scheduler/EventControlThread.h"
+#include "Scheduler/VsyncController.h"
 
 namespace android {
 namespace mock {
 
-class EventControlThread : public android::EventControlThread {
+class VsyncController : public android::scheduler::VsyncController {
 public:
-    EventControlThread();
-    ~EventControlThread() override;
+    VsyncController();
+    ~VsyncController() override;
 
-    MOCK_METHOD1(setVsyncEnabled, void(bool));
+    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 mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 5480b00..1d94f21 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -17,61 +17,86 @@
 
 #include <ui/Rect.h>
 #include <utils/String8.h>
+#include <functional>
+#include <future>
 #include "TransactionUtils.h"
 
 namespace android {
 
 namespace {
 
+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;
+};
+
 // A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
 // 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.useRGBColorSpace = 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;
+    }
+
     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.useRGBColorSpace = true;
+        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/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
new file mode 100644
index 0000000..c45a1a1
--- /dev/null
+++ b/services/vibratorservice/Android.bp
@@ -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.
+
+cc_library_shared {
+    name: "libvibratorservice",
+
+    srcs: [
+        "VibratorCallbackScheduler.cpp",
+        "VibratorHalController.cpp",
+        "VibratorHalWrapper.cpp",
+    ],
+
+    aidl: {
+       local_include_dirs: ["include"],
+       include_dirs: [
+           "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+       ],
+       export_aidl_headers: true
+    },
+
+    shared_libs: [
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "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",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    local_include_dirs: ["include"],
+
+    export_include_dirs: ["include"],
+}
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..46175ad
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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 {
+
+// -------------------------------------------------------------------------------------------------
+
+static constexpr int MAX_RETRIES = 1;
+
+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);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+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;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void HalController::init() {
+    std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+    if (mConnectedHal == nullptr) {
+        mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+    }
+}
+
+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
new file mode 100644
index 0000000..9672644
--- /dev/null
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -0,0 +1,517 @@
+/*
+ * 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 "VibratorHalWrapper"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <hardware/vibrator.h>
+
+#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;
+
+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 {
+
+// -------------------------------------------------------------------------------------------------
+
+template <class T>
+HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
+    if (cache.has_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;
+}
+
+template <class T>
+bool isStaticCastValid(Effect effect) {
+    T castEffect = static_cast<T>(effect);
+    auto iter = hardware::hidl_enum_range<T>();
+    return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+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) {
+    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+        return HalResult<T>::unsupported();
+    }
+    if (status.isOk()) {
+        return HalResult<T>::ok(data);
+    }
+    return HalResult<T>::failed(std::string(status.toString8().c_str()));
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
+    switch (status) {
+        case V1_0::Status::OK:
+            return HalResult<T>::ok(data);
+        case V1_0::Status::UNSUPPORTED_OPERATION:
+            return HalResult<T>::unsupported();
+        default:
+            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(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(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+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) {
+    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+        return HalResult<void>::unsupported();
+    }
+    if (status.isOk()) {
+        return HalResult<void>::ok();
+    }
+    return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
+    switch (status) {
+        case V1_0::Status::OK:
+            return HalResult<void>::ok();
+        case V1_0::Status::UNSUPPORTED_OPERATION:
+            return HalResult<void>::unsupported();
+        default:
+            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(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class HalCallbackWrapper : public Aidl::BnVibratorCallback {
+public:
+    HalCallbackWrapper(std::function<void()> completionCallback)
+          : mCompletionCallback(completionCallback) {}
+
+    binder::Status onComplete() override {
+        mCompletionCallback();
+        return binder::Status::ok();
+    }
+
+private:
+    const std::function<void()> mCompletionCallback;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::ping() {
+    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) {
+    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(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(getHal()->setAmplitude(convertedAmplitude));
+}
+
+HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
+    return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+    return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
+    return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
+    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+    return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
+                                    mCapabilities);
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
+    std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
+    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 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(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;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+    auto result = getHal()->ping();
+    return HalResult<void>::fromReturn(result);
+}
+
+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);
+    }
+}
+
+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));
+}
+
+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));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
+    ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
+    return HalResult<void>::unsupported();
+}
+
+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();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
+    ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
+    return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
+    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+    return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
+                                    mCapabilities);
+}
+
+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();
+}
+
+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();
+}
+
+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();
+}
+
+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_0::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+    if (isStaticCastValid<V1_0::Effect>(effect)) {
+        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",
+          Aidl::toString(effect).c_str());
+    return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+    if (isStaticCastValid<V1_0::Effect>(effect)) {
+        return performInternal(&V1_1::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
+    }
+    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+        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)) {
+        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",
+          Aidl::toString(effect).c_str());
+    return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool 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()>& completionCallback) {
+    if (isStaticCastValid<V1_0::Effect>(effect)) {
+        return performInternal(&V1_3::IVibrator::perform, getHal(),
+                               static_cast<V1_0::Effect>(effect), strength, completionCallback);
+    }
+    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+        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)) {
+        return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+                               static_cast<V1_2::Effect>(effect), strength, completionCallback);
+    }
+    if (isStaticCastValid<V1_3::Effect>(effect)) {
+        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",
+          Aidl::toString(effect).c_str());
+    return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+    Capabilities capabilities = Capabilities::NONE;
+
+    sp<V1_3::IVibrator> hal = getHal();
+    auto amplitudeResult = hal->supportsAmplitudeControl();
+    if (!amplitudeResult.isOk()) {
+        return HalResult<Capabilities>::fromReturn(amplitudeResult, 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);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // 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..0d4c55e
--- /dev/null
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -0,0 +1,398 @@
+/*
+ * 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(vibrator::HalResult<vibrator::Capabilities> result,
+                         vibrator::Capabilities query) {
+        if (!result.isOk()) {
+            return false;
+        }
+        return (result.value() & query) == query;
+    }
+};
+
+#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) {
+        mController.ping();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
+    for (auto _ : state) {
+        mController.tryReconnect();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, on, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.on(duration, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, off, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mController.on(duration, callback);
+        state.ResumeTiming();
+        mController.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto duration = 60s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        controller.on(duration, callback);
+        state.ResumeTiming();
+        controller.setAmplitude(amplitude);
+        state.PauseTiming();
+        controller.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto duration = 6000s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    mController.on(duration, callback);
+
+    for (auto _ : state) {
+        mController.setAmplitude(amplitude);
+    }
+
+    mController.off();
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.setExternalControl(true);
+        state.PauseTiming();
+        controller.setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.setExternalControl(true);
+        state.PauseTiming();
+        mController.setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto amplitude = UINT8_MAX;
+
+    mController.setExternalControl(true);
+
+    for (auto _ : state) {
+        mController.setAmplitude(amplitude);
+    }
+
+    mController.setExternalControl(false);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getCapabilities();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+    // First call to cache values.
+    mController.getCapabilities();
+
+    for (auto _ : state) {
+        mController.getCapabilities();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getSupportedEffects();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
+    // First call to cache values.
+    mController.getSupportedEffects();
+
+    for (auto _ : state) {
+        mController.getSupportedEffects();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getSupportedPrimitives();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
+    // First call to cache values.
+    mController.getSupportedPrimitives();
+
+    for (auto _ : state) {
+        mController.getSupportedPrimitives();
+    }
+});
+
+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"});
+        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:
+    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 capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.alwaysOnEnable(id, effect, strength);
+        state.PauseTiming();
+        mController.alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mController.alwaysOnEnable(id, effect, strength);
+        state.ResumeTiming();
+        mController.alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.performEffect(effect, strength, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+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"});
+        for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+            if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+                continue;
+            }
+            b->Args({static_cast<long>(primitive)});
+        }
+    }
+
+protected:
+    auto getPrimitive(const State& state) const {
+        return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::COMPOSE_EFFECTS)) {
+        return;
+    }
+
+    hardware::vibrator::CompositeEffect effect;
+    effect.primitive = getPrimitive(state);
+    effect.scale = 1.0f;
+    effect.delayMs = 0;
+
+    std::vector<hardware::vibrator::CompositeEffect> effects;
+    effects.push_back(effect);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.performComposedEffect(effects, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+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..3b61f42
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+    void init();
+
+    HalResult<void> ping() final override;
+    void tryReconnect() final 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
new file mode 100644
index 0000000..7b99bbb
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -0,0 +1,350 @@
+/*
+ * 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_VIBRATORHALWRAPPER_H
+#define ANDROID_OS_VIBRATORHALWRAPPER_H
+
+#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 {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+// Result of a call to the Vibrator HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+    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);
+
+    template <typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+    template <typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret,
+                                   hardware::vibrator::V1_0::Status status, T data);
+
+    // This will throw std::bad_optional_access if this result is not ok.
+    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)), 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() { 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);
+
+    template <typename R>
+    static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+    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 unsupported = false)
+          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+    explicit HalResult(std::string errorMessage)
+          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Vibrator HAL capabilities.
+enum class Capabilities : int32_t {
+    NONE = 0,
+    ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+    PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+    AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+    EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+    EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+    COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+    ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+};
+
+inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
+    using underlying = typename std::underlying_type<Capabilities>::type;
+    return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) {
+    return lhs = lhs | rhs;
+}
+
+inline Capabilities operator&(Capabilities lhs, Capabilities rhs) {
+    using underlying = typename std::underlying_type<Capabilities>::type;
+    return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) {
+    return lhs = lhs & rhs;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// 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;
+    virtual void tryReconnect() = 0;
+
+    virtual HalResult<void> on(std::chrono::milliseconds timeout,
+                               const std::function<void()>& completionCallback) = 0;
+    virtual HalResult<void> off() = 0;
+
+    virtual HalResult<void> setAmplitude(int32_t amplitude) = 0;
+    virtual HalResult<void> setExternalControl(bool enabled) = 0;
+
+    virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+                                           hardware::vibrator::EffectStrength strength) = 0;
+    virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
+
+    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,
+            const std::function<void()>& completionCallback) = 0;
+
+    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:
+    AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler,
+                   sp<hardware::vibrator::IVibrator> handle)
+          : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+    virtual ~AidlHalWrapper() = default;
+
+    HalResult<void> ping() override final;
+    void tryReconnect() override final;
+
+    HalResult<void> on(std::chrono::milliseconds timeout,
+                       const std::function<void()>& completionCallback) override final;
+    HalResult<void> off() override final;
+
+    HalResult<void> setAmplitude(int32_t amplitude) override final;
+    HalResult<void> setExternalControl(bool enabled) override final;
+
+    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+                                   hardware::vibrator::EffectStrength strength) override final;
+    HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+    HalResult<Capabilities> getCapabilities() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+            override final;
+
+    HalResult<std::chrono::milliseconds> performEffect(
+            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+            const std::function<void()>& completionCallback) override final;
+
+    HalResult<void> performComposedEffect(
+            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+            const std::function<void()>& completionCallback) override final;
+
+private:
+    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 HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
+public:
+    HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+          : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+    virtual ~HidlHalWrapper() = default;
+
+    HalResult<void> ping() override final;
+    void tryReconnect() override final;
+
+    HalResult<void> on(std::chrono::milliseconds timeout,
+                       const std::function<void()>& completionCallback) override final;
+    HalResult<void> off() override final;
+
+    HalResult<void> setAmplitude(int32_t amplitude) override final;
+    virtual HalResult<void> setExternalControl(bool enabled) 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;
+
+    HalResult<Capabilities> getCapabilities() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+            override final;
+
+    HalResult<void> performComposedEffect(
+            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+            const std::function<void()>& completionCallback) override final;
+
+protected:
+    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 HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
+public:
+    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;
+
+    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.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
+public:
+    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;
+
+    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.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
+public:
+    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;
+
+    HalResult<void> setExternalControl(bool enabled) override final;
+
+    HalResult<std::chrono::milliseconds> performEffect(
+            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+            const std::function<void()>& completionCallback) override final;
+
+protected:
+    HalResult<Capabilities> getCapabilitiesInternal() override final;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALWRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
new file mode 100644
index 0000000..9033124
--- /dev/null
+++ b/services/vibratorservice/test/Android.bp
@@ -0,0 +1,48 @@
+// 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: "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",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libvibratorservice",
+        "libutils",
+        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
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..f04e016
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,392 @@
+/*
+ * 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) {
+    mController->init();
+    ASSERT_EQ(1, mConnectCounter);
+
+    // Noop when wrapper was already initialized.
+    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_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(12, 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
new file mode 100644
index 0000000..96b76ba
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -0,0 +1,532 @@
+/*
+ * 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/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockBinder : public BBinder {
+public:
+    MOCK_METHOD(status_t, linkToDeath,
+                (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+    MOCK_METHOD(status_t, unlinkToDeath,
+                (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+                 wp<DeathRecipient>* outRecipient),
+                (override));
+    MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+    MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+    MOCK_METHOD(Status, off, (), (override));
+    MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+    MOCK_METHOD(Status, perform,
+                (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+                (override));
+    MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+    MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+    MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+    MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+                (override));
+    MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+    MOCK_METHOD(Status, compose,
+                (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+                (override));
+    MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+    MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+    MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperAidlTest : public Test {
+public:
+    void SetUp() override {
+        mMockBinder = new StrictMock<MockBinder>();
+        mMockHal = new StrictMock<MockIVibrator>();
+        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;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+ACTION(TriggerCallbackInArg1) {
+    if (arg1 != nullptr) {
+        arg1->onComplete();
+    }
+}
+
+ACTION(TriggerCallbackInArg2) {
+    if (arg2 != nullptr) {
+        arg2->onComplete();
+    }
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPing) {
+    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, 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())));
+        EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
+                .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(100ms, callback).isUnsupported());
+    // Callback not triggered for unsupported
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
+    // 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());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOff) {
+    EXPECT_CALL(*mMockHal.get(), off())
+            .Times(Exactly(3))
+            .WillOnce(Return(Status()))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+
+    ASSERT_TRUE(mWrapper->off().isOk());
+    ASSERT_TRUE(mWrapper->off().isUnsupported());
+    ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
+    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
+    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+                .Times(Exactly(2))
+                .WillOnce(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+    ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+    ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+                .Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(),
+                    alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(),
+                    alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
+    ASSERT_TRUE(result.isOk());
+    result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM);
+    ASSERT_TRUE(result.isUnsupported());
+    result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG);
+    ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+    }
+
+    ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
+    ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported());
+    ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+    EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+    EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+        }));
+    }
+    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) {
+    std::vector<Effect> supportedEffects;
+    supportedEffects.push_back(Effect::CLICK);
+    supportedEffects.push_back(Effect::TICK);
+
+    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+    ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
+
+    auto result = mWrapper->getSupportedEffects();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
+    std::vector<Effect> supportedEffects;
+    supportedEffects.push_back(Effect::CLICK);
+    supportedEffects.push_back(Effect::TICK);
+
+    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getSupportedEffects();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(supportedEffects, result.value());
+        }));
+    }
+    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, 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(
+                        DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+        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(1000ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+    // 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 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());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+    std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+    singleEffect.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+                .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->performComposedEffect(emptyEffects, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performComposedEffect(singleEffect, callback);
+    ASSERT_TRUE(result.isUnsupported());
+    // Callback not triggered for unsupported
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performComposedEffect(multipleEffects, callback);
+    ASSERT_TRUE(result.isFailed());
+    // 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
new file mode 100644
index 0000000..06aa36f
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_0Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_0 : public V1_0::IVibrator {
+public:
+    MOCK_METHOD(hardware::Return<void>, ping, (), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+    MOCK_METHOD(hardware::Return<void>, perform,
+                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_0Test : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIVibratorV1_0>();
+        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;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
+    EXPECT_CALL(*mMockHal.get(), ping())
+            .Times(Exactly(2))
+            .WillOnce([]() { return hardware::Return<void>(); })
+            .WillRepeatedly([]() {
+                return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+            });
+
+    ASSERT_TRUE(mWrapper->ping().isOk());
+    ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
+                .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>(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>(12))))
+                .Times(Exactly(1))
+                .WillRepeatedly([](uint32_t) {
+                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+                });
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+    ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
+    EXPECT_CALL(*mMockHal.get(), off())
+            .Times(Exactly(4))
+            .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+            .WillOnce([]() {
+                return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+            })
+            .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+            .WillRepeatedly([]() {
+                return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+            });
+
+    ASSERT_TRUE(mWrapper->off().isOk());
+    ASSERT_TRUE(mWrapper->off().isUnsupported());
+    ASSERT_TRUE(mWrapper->off().isFailed());
+    ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
+                .Times(Exactly(1))
+                .WillRepeatedly([](uint8_t) {
+                    return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+                });
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
+                .Times(Exactly(1))
+                .WillRepeatedly([](uint8_t) {
+                    return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+                });
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
+                .Times(Exactly(1))
+                .WillRepeatedly([](uint8_t) {
+                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+                });
+    }
+
+    ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
+    ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
+    ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
+    ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
+    ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+    ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
+    ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
+    ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+            .Times(Exactly(2))
+            .WillOnce([]() {
+                return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+            })
+            .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+        return hardware::Return<bool>(false);
+    });
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+        return hardware::Return<bool>(true);
+    });
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+        }));
+    }
+    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;
+        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_0::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());
+        EXPECT_CALL(*mMockHal.get(),
+                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockHal.get(),
+                    perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
+                .Times(Exactly(2))
+                .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+                    cb(V1_0::Status::BAD_VALUE, 10);
+                    return hardware::Return<void>();
+                })
+                .WillRepeatedly(
+                        [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
+                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+                        });
+    }
+
+    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::CLICK, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+
+    result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+    // 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());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
+    std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+    singleEffect.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).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
new file mode 100644
index 0000000..d887efc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_1Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_1 : public V1_1::IVibrator {
+public:
+    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+    MOCK_METHOD(hardware::Return<void>, perform,
+                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_1,
+                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+                (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_1Test : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIVibratorV1_1>();
+        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;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
+    {
+        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_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());
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+                .Times(Exactly(1))
+                .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+                                   MockIVibratorV1_1::perform_cb cb) {
+                    cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+                    return hardware::Return<void>();
+                });
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
+                .Times(Exactly(2))
+                .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
+                             MockIVibratorV1_1::perform_cb cb) {
+                    cb(V1_0::Status::BAD_VALUE, 0);
+                    return hardware::Return<void>();
+                })
+                .WillRepeatedly(
+                        [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
+                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+                        });
+    }
+
+    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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+
+    result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+    // 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
new file mode 100644
index 0000000..26d9350
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_2Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_2 : public V1_2::IVibrator {
+public:
+    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+    MOCK_METHOD(hardware::Return<void>, perform,
+                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_1,
+                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+                (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_2,
+                (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_2Test : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIVibratorV1_2>();
+        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;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
+    {
+        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_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());
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
+                .Times(Exactly(2))
+                .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+                    cb(V1_0::Status::BAD_VALUE, 10);
+                    return hardware::Return<void>();
+                })
+                .WillRepeatedly(
+                        [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
+                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+                        });
+    }
+
+    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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+
+    result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    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());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+    // 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
new file mode 100644
index 0000000..08652f4
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_3Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+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;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_3 : public V1_3::IVibrator {
+public:
+    MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+    MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+    MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+    MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
+    MOCK_METHOD(hardware::Return<void>, perform,
+                (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_1,
+                (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+                (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_2,
+                (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+    MOCK_METHOD(hardware::Return<void>, perform_1_3,
+                (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_3Test : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIVibratorV1_3>();
+        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;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+                .Times(Exactly(2))
+                .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+                .WillRepeatedly([]() {
+                    return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+                });
+        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+                .Times(Exactly(2))
+                .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+                .WillRepeatedly([]() {
+                    return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+                });
+    }
+
+    ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+    ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+    ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+    ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(true);
+        });
+    }
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
+                      vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
+              result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(true);
+        });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+    }
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(true);
+        });
+    }
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+    }
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+    }
+
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+    }
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+        }));
+    }
+    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) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+    }
+
+    // Call to supportsAmplitudeControl failed.
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isFailed());
+
+    // Call to supportsExternalControl failed.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isFailed());
+
+    // Returns successful result from third call.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+
+    // Returns cached successful result.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
+    {
+        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(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_3::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());
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
+                                _))
+                .Times(Exactly(1))
+                .WillRepeatedly(
+                        [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+                            cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+                            return hardware::Return<void>();
+                        });
+        EXPECT_CALL(*mMockHal.get(),
+                    perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
+                                _))
+                .Times(Exactly(2))
+                .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+                    cb(V1_0::Status::BAD_VALUE, 0);
+                    return hardware::Return<void>();
+                })
+                .WillRepeatedly(
+                        [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
+                            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+                        });
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(10ms, result.value());
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
+    ASSERT_TRUE(result.isUnsupported());
+
+    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+    ASSERT_TRUE(result.isFailed());
+
+    // Callback not triggered for unsupported and on failure
+    ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
new file mode 100644
index 0000000..8d0b22e
--- /dev/null
+++ b/services/vibratorservice/test/test_utils.h
@@ -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.
+ */
+
+#ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_
+#define VIBRATORSERVICE_UNITTEST_UTIL_H_
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+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,
+                                                 std::chrono::milliseconds delay, float scale) {
+        CompositeEffect effect;
+        effect.primitive = primitive;
+        effect.delayMs = delay.count();
+        effect.scale = scale;
+        return effect;
+    }
+
+    static std::function<void()> createCountingCallback(int32_t* counter) {
+        return [counter]() { *counter += 1; };
+    }
+
+private:
+    TestFactory() = delete;
+    ~TestFactory() = delete;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index dcaa663..9cf4905 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -14,6 +14,7 @@
 ]
 
 header_libraries = [
+    "jni_headers",
     "libdvr_headers",
 ]
 
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/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 37b5368..d9a9427 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -621,6 +621,7 @@
     // global functions
     if (instance == VK_NULL_HANDLE) {
         if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+        if (strcmp(pName, "vkGetInstanceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
         if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
         if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties);
         if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties);
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7bcb2c1..4068a16 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -84,6 +84,8 @@
     Hal(const Hal&) = delete;
     Hal& operator=(const Hal&) = delete;
 
+    bool ShouldUnloadBuiltinDriver();
+    void UnloadBuiltinDriver();
     bool InitDebugReportIndex();
 
     static Hal hal_;
@@ -95,15 +97,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 +120,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 +137,8 @@
 
     const bool is_instance_;
     const VkAllocationCallbacks& allocator_;
+    const uint32_t loader_api_version_;
+    const uint32_t icd_api_version_;
 
     VkPhysicalDevice physical_dev_;
 
@@ -161,7 +167,7 @@
 }
 
 const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
-    "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
+    "ro.hardware.vulkan",
     "ro.board.platform",
 }};
 
@@ -241,7 +247,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 +297,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
+    dlclose(hal_.dev_->common.module->dso);
+
+    hal_.dev_ = nullptr;
+    hal_.debug_report_index_ = -1;
+}
+
 bool Hal::InitDebugReportIndex() {
     ATRACE_CALL();
 
@@ -324,32 +367,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 +395,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 +424,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 +487,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 +521,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 +595,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 +640,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;
@@ -591,6 +700,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:
@@ -636,40 +749,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 +980,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 +998,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 +1020,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 +1029,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 +1090,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 +1179,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 +1200,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 +1296,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 +1329,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 +1378,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 +1396,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 +1407,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..1aba674 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,6 +50,10 @@
         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,
@@ -74,9 +80,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 +134,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/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d3ed88d..0f791c9 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -881,11 +881,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());
@@ -970,7 +969,6 @@
                   strerror(-err), err);
         }
 
-        // TODO(b/143294545): Return something better than "whole window"
         pRects[0].offset.x = 0;
         pRects[0].offset.y = 0;
         pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
index 7c39075..be24172 100644
--- a/vulkan/scripts/api_generator.py
+++ b/vulkan/scripts/api_generator.py
@@ -152,7 +152,9 @@
     if (instance == VK_NULL_HANDLE) {\n""")
 
   for cmd in gencom.command_list:
-    if gencom.is_globally_dispatched(cmd):
+    # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively
+    # globally dispatched
+    if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr':
       f.write(gencom.indent(2) +
               'if (strcmp(pName, \"' + cmd +
               '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
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..ef021f2 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -22,7 +22,7 @@
 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_display_control',
@@ -97,6 +97,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 +186,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 +204,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
 
@@ -327,6 +339,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')
@@ -379,6 +392,10 @@
       apiversion = ''
       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')
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..ace713b 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;
@@ -189,135 +153,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 +297,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 +313,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 +340,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);