Merge "Add OWNERS to libinput"
diff --git a/.clang-format b/.clang-format
index 6725a1f..f63f670 100644
--- a/.clang-format
+++ b/.clang-format
@@ -12,3 +12,6 @@
 PenaltyBreakBeforeFirstCallParameter: 100000
 SpacesBeforeTrailingComments: 1
 IncludeBlocks: Preserve
+
+DerivePointerAlignment: false
+PointerAlignment: Left
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
 
 cc_library_headers {
     name: "libandroid_sensor_headers",
-    vendor: true,
+    vendor_available: true,
+    host_supported: true,
     export_include_dirs: ["include_sensor"],
 }
 
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3480d63..2ce3fb0 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,6 +7,7 @@
 rustfmt = --config-path=rustfmt.toml
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+               cmds/dumpstate/
                cmds/idlcli/
                cmds/installd/
                cmds/servicemanager/
@@ -14,11 +15,13 @@
                include/powermanager/
                libs/binder/fuzzer/
                libs/binder/
+               libs/binderdebug/
                libs/binderthreadstate/
                libs/graphicsenv/
                libs/gui/
                libs/input/
                libs/nativedisplay/
+               libs/nativewindow/
                libs/renderengine/
                libs/ui/
                libs/vr/
@@ -39,3 +42,4 @@
 dumpsys_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpsys/"
 # bugreports matches both cmds/bugreport and cmds/bugreportz
 bugreports_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/bugreport"
+binder_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^libs/binder/"
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index 7e89220..bb3faaf 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
 
 package android.view;
 
-parcelable Surface cpp_header "gui/view/Surface.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h";
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index aa0ef25..1c4e63e 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -38,6 +38,7 @@
     ],
 
     init_rc: ["atrace.rc"],
+    required: ["ftrace_synthetic_events.conf"],
 
     product_variables: {
         debuggable: {
@@ -45,3 +46,8 @@
         },
     },
 }
+
+prebuilt_etc {
+    name: "ftrace_synthetic_events.conf",
+    src: "ftrace_synthetic_events.conf",
+}
diff --git a/cmds/atrace/README.md b/cmds/atrace/README.md
new file mode 100644
index 0000000..faa43b2
--- /dev/null
+++ b/cmds/atrace/README.md
@@ -0,0 +1,48 @@
+# Atrace categories
+
+The atrace command (and the perfetto configuration) allow listing **categories**
+to select subsets of events to be traced.
+
+Each category can include some userspace events and some ftrace events.
+
+## Vendor categories
+
+It's possible to extend exiting categories (or to define new categories) from
+the /vendor partition in order to add hardware specific ftrace events.
+
+Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt`, atrace
+and perfetto will consider the categories and ftrace events listed there.
+
+The file contains a list of categories, and for each category (on the following
+lines, indented with one or more spaces of time), a list of ftrace events that
+should be enabled when the category is enabled.
+
+Each ftrace event should be a subdirectory in `/sys/kernel/tracing/events/` and
+should be of the form `group/event`. Listing a whole group is not supported,
+each event needs to be listed explicitly.
+
+It is not an error if an ftrace event is listed in the file, but not present on
+the tracing file system.
+
+Example:
+
+```
+gfx
+ mali/gpu_power_state
+ mali/mali_pm_status
+thermal_tj
+ thermal_exynos/thermal_cpu_pressure
+ thermal_exynos/thermal_exynos_arm_update
+```
+
+The file lists two categories (`gfx` and `thermal_tj`). When the `gfx` category
+is enabled, atrace (or perfetto) will enable
+`/sys/kernel/tracing/events/mali/gpu_power_state` and
+`/sys/kernel/tracing/events/mali/mali_pm_status`. When the `thermal_tj` category
+is enabled, atrace (or perfetto) will enable
+`/sys/kernel/tracing/events/thermal_exynos/thermal_cpu_pressure` and
+`/sys/kernel/tracing/events/thermal_exynos/thermal_exynos_arm_update`.
+
+Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt` exists
+on the file system, perfetto and atrace do not query the android.hardware.atrace
+HAL (which is deprecated).
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 48d48ac..8105626 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -49,6 +49,7 @@
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 
 using namespace android;
@@ -62,7 +63,7 @@
 
 using std::string;
 
-#define MAX_SYS_FILES 12
+#define MAX_SYS_FILES 13
 
 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
 const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -73,7 +74,9 @@
 const char* k_pdxServiceCategory = "pdx";
 const char* k_coreServicesProp = "ro.atrace.core.services";
 
-typedef enum { OPT, REQ } requiredness  ;
+const char* kVendorCategoriesPath = "/vendor/etc/atrace/atrace_categories.txt";
+
+typedef enum { OPT, REQ } requiredness;
 
 struct TracingCategory {
     // The name identifying the category.
@@ -189,6 +192,8 @@
         { OPT,      "events/f2fs/f2fs_sync_file_exit/enable" },
         { OPT,      "events/f2fs/f2fs_write_begin/enable" },
         { OPT,      "events/f2fs/f2fs_write_end/enable" },
+        { OPT,      "events/f2fs/f2fs_iostat/enable" },
+        { OPT,      "events/f2fs/f2fs_iostat_latency/enable" },
         { OPT,      "events/ext4/ext4_da_write_begin/enable" },
         { OPT,      "events/ext4/ext4_da_write_end/enable" },
         { OPT,      "events/ext4/ext4_sync_file_enter/enable" },
@@ -253,7 +258,20 @@
     } },
 };
 
-struct TracingVendorCategory {
+// A category in the vendor categories file.
+struct TracingVendorFileCategory {
+    // The name identifying the category.
+    std::string name;
+
+    // If the category is enabled through command.
+    bool enabled = false;
+
+    // Paths to the ftrace enable files (relative to g_traceFolder).
+    std::vector<std::string> ftrace_enable_paths;
+};
+
+// A category in the vendor HIDL HAL.
+struct TracingVendorHalCategory {
     // The name identifying the category.
     std::string name;
 
@@ -263,11 +281,8 @@
     // If the category is enabled through command.
     bool enabled;
 
-    TracingVendorCategory(string &&name, string &&description, bool enabled)
-            : name(std::move(name))
-            , description(std::move(description))
-            , enabled(enabled)
-    {}
+    TracingVendorHalCategory(string&& name, string&& description, bool enabled)
+          : name(std::move(name)), description(std::move(description)), enabled(enabled) {}
 };
 
 /* Command line options */
@@ -287,8 +302,9 @@
 static bool g_traceAborted = false;
 static bool g_categoryEnables[arraysize(k_categories)] = {};
 static std::string g_traceFolder;
+static std::vector<TracingVendorFileCategory> g_vendorFileCategories;
 static sp<IAtraceDevice> g_atraceHal;
-static std::vector<TracingVendorCategory> g_vendorCategories;
+static std::vector<TracingVendorHalCategory> g_vendorHalCategories;
 
 /* Sys file paths */
 static const char* k_traceClockPath =
@@ -645,6 +661,13 @@
             }
         }
     }
+    for (const TracingVendorFileCategory& c : g_vendorFileCategories) {
+        for (const std::string& path : c.ftrace_enable_paths) {
+            if (fileIsWritable(path.c_str())) {
+                ok &= setKernelOptionEnable(path.c_str(), false);
+            }
+        }
+    }
     return ok;
 }
 
@@ -724,7 +747,13 @@
 static bool setCategoryEnable(const char* name)
 {
     bool vendor_found = false;
-    for (auto &c : g_vendorCategories) {
+    for (auto& c : g_vendorFileCategories) {
+        if (strcmp(name, c.name.c_str()) == 0) {
+            c.enabled = true;
+            vendor_found = true;
+        }
+    }
+    for (auto& c : g_vendorHalCategories) {
         if (strcmp(name, c.name.c_str()) == 0) {
             c.enabled = true;
             vendor_found = true;
@@ -870,6 +899,16 @@
         }
     }
 
+    for (const TracingVendorFileCategory& c : g_vendorFileCategories) {
+        if (c.enabled) {
+            for (const std::string& path : c.ftrace_enable_paths) {
+                if (fileIsWritable(path.c_str())) {
+                    ok &= setKernelOptionEnable(path.c_str(), true);
+                }
+            }
+        }
+    }
+
     return ok;
 }
 
@@ -1055,7 +1094,10 @@
             printf("  %10s - %s\n", c.name, c.longname);
         }
     }
-    for (const auto &c : g_vendorCategories) {
+    for (const auto& c : g_vendorFileCategories) {
+        printf("  %10s - (VENDOR)\n", c.name.c_str());
+    }
+    for (const auto& c : g_vendorHalCategories) {
         printf("  %10s - %s (HAL)\n", c.name.c_str(), c.description.c_str());
     }
 }
@@ -1114,8 +1156,38 @@
     return true;
 }
 
-void initVendorCategories()
-{
+void initVendorCategoriesFromFile() {
+    std::ifstream is(kVendorCategoriesPath);
+    for (std::string line; std::getline(is, line);) {
+        if (line.empty()) {
+            continue;
+        }
+        if (android::base::StartsWith(line, ' ') || android::base::StartsWith(line, '\t')) {
+            if (g_vendorFileCategories.empty()) {
+                fprintf(stderr, "Malformed vendor categories file\n");
+                exit(1);
+                return;
+            }
+            std::string_view path = std::string_view(line).substr(1);
+            while (android::base::StartsWith(path, ' ') || android::base::StartsWith(path, '\t')) {
+                path.remove_prefix(1);
+            }
+            if (path.empty()) {
+                continue;
+            }
+            std::string enable_path = "events/";
+            enable_path += path;
+            enable_path += "/enable";
+            g_vendorFileCategories.back().ftrace_enable_paths.push_back(std::move(enable_path));
+        } else {
+            TracingVendorFileCategory cat;
+            cat.name = line;
+            g_vendorFileCategories.push_back(std::move(cat));
+        }
+    }
+}
+
+void initVendorCategoriesFromHal() {
     g_atraceHal = IAtraceDevice::getService();
 
     if (g_atraceHal == nullptr) {
@@ -1123,27 +1195,34 @@
         return;
     }
 
-    Return<void> ret = g_atraceHal->listCategories(
-        [](const auto& list) {
-            g_vendorCategories.reserve(list.size());
-            for (const auto& category : list) {
-                g_vendorCategories.emplace_back(category.name, category.description, false);
-            }
-        });
+    Return<void> ret = g_atraceHal->listCategories([](const auto& list) {
+        g_vendorHalCategories.reserve(list.size());
+        for (const auto& category : list) {
+            g_vendorHalCategories.emplace_back(category.name, category.description, false);
+        }
+    });
     if (!ret.isOk()) {
         fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str());
     }
 }
 
-static bool setUpVendorTracing()
-{
+void initVendorCategories() {
+    // If kVendorCategoriesPath exists on the filesystem, do not use the HAL.
+    if (access(kVendorCategoriesPath, F_OK) != -1) {
+        initVendorCategoriesFromFile();
+    } else {
+        initVendorCategoriesFromHal();
+    }
+}
+
+static bool setUpVendorTracingWithHal() {
     if (g_atraceHal == nullptr) {
         // No atrace HAL
         return true;
     }
 
     std::vector<hidl_string> categories;
-    for (const auto &c : g_vendorCategories) {
+    for (const auto& c : g_vendorHalCategories) {
         if (c.enabled) {
             categories.emplace_back(c.name);
         }
@@ -1164,15 +1243,14 @@
     return true;
 }
 
-static bool cleanUpVendorTracing()
-{
+static bool cleanUpVendorTracingWithHal() {
     if (g_atraceHal == nullptr) {
         // No atrace HAL
         return true;
     }
 
-    if (!g_vendorCategories.size()) {
-        // No vendor categories
+    if (!g_vendorHalCategories.size()) {
+        // No vendor HAL categories
         return true;
     }
 
@@ -1326,7 +1404,7 @@
 
     if (ok && traceStart && !onlyUserspace) {
         ok &= setUpKernelTracing();
-        ok &= setUpVendorTracing();
+        ok &= setUpVendorTracingWithHal();
         ok &= startTrace();
     }
 
@@ -1397,7 +1475,7 @@
     if (traceStop) {
         cleanUpUserspaceTracing();
         if (!onlyUserspace) {
-            cleanUpVendorTracing();
+            cleanUpVendorTracingWithHal();
             cleanUpKernelTracing();
         }
     }
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 5267b02..c3cf2c2 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -181,6 +181,8 @@
     chmod 0666 /sys/kernel/tracing/events/clk/clk_enable/enable
     chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_set_rate/enable
     chmod 0666 /sys/kernel/tracing/events/clk/clk_set_rate/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/printk/console/enable
+    chmod 0666 /sys/kernel/tracing/events/printk/console/enable
 
     # disk
     chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
@@ -289,14 +291,30 @@
 # Setup synthetic events
     chmod 0666 /sys/kernel/tracing/synthetic_events
     chmod 0666 /sys/kernel/debug/tracing/synthetic_events
+    copy /system/etc/ftrace_synthetic_events.conf /sys/kernel/tracing/synthetic_events
+    copy /system/etc/ftrace_synthetic_events.conf /sys/kernel/debug/tracing/synthetic_events
 
-    # rss_stat_throttled
-    write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
-    write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
-
-    # allow creating event triggers
-    chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+    # allow creating rss_stat event triggers
     chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+    chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+
+    # allow enabling rss_stat_throttled
+    chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/enable
+
+    # allow creating suspend_resume triggers
+    chmod 0666 /sys/kernel/tracing/events/power/suspend_resume/trigger
+    chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/trigger
+
+    # allow enabling suspend_resume_minimal
+    chmod 0666 /sys/kernel/tracing/events/synthetic/suspend_resume_minimal/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/synthetic/suspend_resume_minimal/enable
+
+on late-init && property:ro.boot.fastboot.boottrace=enabled
+    setprop debug.atrace.tags.enableflags 802922
+    setprop persist.traced.enable 0
+    write /sys/kernel/debug/tracing/tracing_on 1
+    write /sys/kernel/tracing/tracing_on 1
 
 # Only create the tracing instance if persist.mm_events.enabled
 # Attempting to remove the tracing instance after it has been created
@@ -386,10 +404,142 @@
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
     chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
 
+# Handle hyp tracing instance
+on late-init && property:ro.boot.hypervisor.vm.supported=1
+
+# Hypervisor tracing instance doesn't support changing trace_clock
+    chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock
+    chmod 0440 /sys/kernel/tracing/hyp/trace_clock
+
+    chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb
+    chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb
+
+    chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on
+    chmod 0660 /sys/kernel/tracing/hyp/tracing_on
+
+# Tracing disabled by default
+    write /sys/kernel/debug/tracing/hyp/tracing_on 0
+    write /sys/kernel/tracing/hyp/tracing_on 0
+
+# Read and truncate the hyp trace.
+    chmod 0660 /sys/kernel/debug/tracing/hyp/trace
+    chmod 0660 /sys/kernel/tracing/hyp/trace
+
+# Read and truncate the per-CPU kernel trace.
+# Cannot use wildcards in .rc files. Update this if there is a phone with
+# TODO(b/249050813, ioffe): introduce per-cpu wildcard
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace
+
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page
+    chmod 0440 /sys/kernel/tracing/hyp/events/header_page
+
+# Hyp events start here
+
+# hyp_enter event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id
+
+# hyp_exit event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id
+
+# host_hcall event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_hcall/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/id
+
+# host_smc event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_smc/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/id
+
+# host_mem_abort event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id
+
+
 on property:persist.debug.atrace.boottrace=1
     start boottrace
 
 # Run atrace with the categories written in a file
 service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories
+    user root
     disabled
     oneshot
+
+on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled
+    setprop debug.atrace.tags.enableflags 0
+    setprop persist.traced.enable 1
+    write /sys/kernel/debug/tracing/tracing_on 0
+    write /sys/kernel/tracing/tracing_on 0
+
diff --git a/cmds/atrace/ftrace_synthetic_events.conf b/cmds/atrace/ftrace_synthetic_events.conf
new file mode 100644
index 0000000..e2257fe
--- /dev/null
+++ b/cmds/atrace/ftrace_synthetic_events.conf
@@ -0,0 +1,2 @@
+rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size
+suspend_resume_minimal bool start
diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
index a65f6de..faf461a 100644
--- a/cmds/cmd/fuzzer/Android.bp
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -42,5 +42,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 155276,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libcmd",
+        vector: "local_no_privileges_required",
+        service_privilege: "constrained",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 3091f6b..6f7fea3 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -162,17 +162,16 @@
         return 0;
     }
     bool newline = false;
+    int poll_timeout_ms = 30 * 1000;
     while (true) {
-        uint64_t start_time = Nanotime();
         pollfd fds[] = { { .fd = fd, .events = POLLIN } };
-        int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000));
+        int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms));
         if (ret == -1) {
             dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno));
             newline = true;
             break;
-        } else if (ret == 0) {
-            uint64_t elapsed = Nanotime() - start_time;
-            dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+        } else if (ret == 0 && poll_timeout_ms != 0) {
+            dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 );
             newline = true;
             break;
         } else {
@@ -189,6 +188,7 @@
                 break;
             }
         }
+        poll_timeout_ms = 0;
     }
 
     if (!newline) dprintf(out_fd, "\n");
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 5f56531..ab81ecf 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -3,3 +3,4 @@
 gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
+smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 12de33f..dc0e26b 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -185,6 +185,7 @@
 #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
 #define CGROUPFS_DIR "/sys/fs/cgroup"
 #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
+#define DROPBOX_DIR "/data/system/dropbox"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -524,6 +525,15 @@
     return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
 }
 
+static bool skip_wtf_strictmode(const char *path) {
+    if (strstr(path, "_wtf")) {
+        return true;
+    } else if (strstr(path, "_strictmode")) {
+        return true;
+    }
+    return false;
+}
+
 static bool skip_none(const char* path __attribute__((unused))) {
     return false;
 }
@@ -804,6 +814,8 @@
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+    printf("Bootconfig: ");
+    DumpFileToFd(STDOUT_FILENO, "", "/proc/bootconfig");
     printf("Uptime: ");
     RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
                    CommandOptions::WithTimeout(1).Always().Build());
@@ -1242,8 +1254,10 @@
              dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
              dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1));
         } else {
-            status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
-            if (status == OK) {
+             status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP | Dumpsys::TYPE_PID |
+                                                       Dumpsys::TYPE_CLIENTS | Dumpsys::TYPE_THREAD,
+                                                       service, args);
+             if (status == OK) {
                 dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
                 std::chrono::duration<double> elapsed_seconds;
                 if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
@@ -1260,6 +1274,9 @@
                 dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
                 bool dump_complete = (status == OK);
                 dumpsys.stopDumpThread(dump_complete);
+            } else {
+                MYLOGE("Failed to start dump thread for service: %s, status: %d",
+                       String8(service).c_str(), status);
             }
         }
 
@@ -1885,9 +1902,17 @@
     }
     ds.AddDir(PREREBOOT_DATA_DIR, false);
     add_mountinfo();
+    for (const char* path : {"/proc/cpuinfo", "/proc/meminfo"}) {
+        ds.AddZipEntry(ZIP_ROOT_DIR + path, path);
+    }
     DumpIpTablesAsRoot();
     DumpDynamicPartitionInfo();
     ds.AddDir(OTA_METADATA_DIR, true);
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Include dropbox entry files inside ZIP, but exclude
+        // noisy WTF and StrictMode entries
+        dump_files("", DROPBOX_DIR, skip_wtf_strictmode, _add_file_from_fd);
+    }
 
     // Capture any IPSec policies in play. No keys are exposed here.
     RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build());
@@ -2040,6 +2065,10 @@
                SEC_TO_MSEC(10));
     RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"isub"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
     if (include_sensitive_info) {
         // Contains raw IP addresses, omit from reports on user builds.
         RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
@@ -2170,6 +2199,16 @@
             continue;
         }
 
+        // Skip cached processes.
+        if (IsCached(pid)) {
+            // For consistency, the header and footer to this message match those
+            // dumped by debuggerd in the success case.
+            dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
+            dprintf(fd, "Dump skipped for cached process.\n");
+            dprintf(fd, "---- end %d ----", pid);
+            continue;
+        }
+
         const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
         std::string exe;
         if (!android::base::Readlink(link_name, &exe)) {
@@ -2200,8 +2239,7 @@
 
         const uint64_t start = Nanotime();
         const int ret = dump_backtrace_to_file_timeout(
-            pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
-            is_java_process ? 5 : 20, fd);
+            pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd);
 
         if (ret == -1) {
             // For consistency, the header and footer to this message match those
@@ -2772,6 +2810,7 @@
             options->do_screenshot = false;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+            options->do_vibrate = false;
             options->do_progress_updates = true;
             options->do_screenshot = is_screenshot_requested;
             break;
@@ -3244,6 +3283,15 @@
 }
 
 void Dumpstate::MaybeSnapshotWinTrace() {
+    // Include the proto logging from WMShell.
+    RunCommand(
+        // Empty name because it's not intended to be classified as a bugreport section.
+        // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope"
+        // in the bugreport.
+        "", {"dumpsys", "activity", "service", "SystemUIService",
+             "WMShell", "protolog", "save-for-bugreport"},
+        CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+
     // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
     for (const auto& service : {"window", "input_method"}) {
         RunCommand(
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index a80da4e..d0030dd 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -8,6 +8,7 @@
     socket dumpstate stream 0660 shell log
     disabled
     oneshot
+    user root
 
 # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
 # it is finished.
@@ -16,9 +17,11 @@
     class main
     disabled
     oneshot
+    user root
 
 # bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
 service bugreportd /system/bin/dumpstate -w
     class main
     disabled
     oneshot
+    user root
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 70b4e5c..0012177 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -285,8 +285,8 @@
 
 
     // Other options retain default values
-    EXPECT_TRUE(options_.do_vibrate);
     EXPECT_FALSE(options_.progress_updates_to_socket);
+    EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.stream_to_socket);
@@ -1023,7 +1023,8 @@
 };
 
 // Generate a quick LimitedOnly report redirected to a file, open it and verify entry exist.
-TEST_F(ZippedBugReportStreamTest, StreamLimitedOnlyReport) {
+// TODO: broken test tracked in b/249983726
+TEST_F(ZippedBugReportStreamTest, DISABLED_StreamLimitedOnlyReport) {
     std::string out_path = kTestDataPath + "StreamLimitedOnlyReportOut.zip";
     android::base::unique_fd out_fd;
     CreateFd(out_path, &out_fd);
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index faf67fd..c6132e8 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -99,6 +99,8 @@
 
 static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
 static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+static constexpr const char* kMiscMirrorCePath = "/data_mirror/misc_ce";
+static constexpr const char* kMiscMirrorDePath = "/data_mirror/misc_de";
 
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
@@ -414,10 +416,12 @@
  */
 static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
         bool existing) {
+    ScopedTrace tracer("restorecon-lazy");
     int res = 0;
     char* before = nullptr;
     char* after = nullptr;
     if (!existing) {
+        ScopedTrace tracer("new-path");
         if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
                 SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
             PLOG(ERROR) << "Failed recursive restorecon for " << path;
@@ -444,6 +448,7 @@
     // If the initial top-level restorecon above changed the label, then go
     // back and restorecon everything recursively
     if (strcmp(before, after)) {
+        ScopedTrace tracer("label-change");
         if (existing) {
             LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
                     << path << "; running recursive restorecon";
@@ -478,11 +483,15 @@
 
 static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid,
                            long project_id) {
-    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
-        PLOG(ERROR) << "Failed to prepare " << path;
-        return -1;
+    {
+        ScopedTrace tracer("prepare-dir");
+        if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+            PLOG(ERROR) << "Failed to prepare " << path;
+            return -1;
+        }
     }
     if (internal_storage_has_project_id()) {
+        ScopedTrace tracer("set-quota");
         return set_quota_project_id(path, project_id, true);
     }
     return 0;
@@ -491,18 +500,20 @@
 static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
                                  uid_t uid, gid_t gid, long project_id) {
     auto path = StringPrintf("%s/%s", parent.c_str(), name);
-    int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid);
+    int ret;
+    {
+        ScopedTrace tracer("prepare-cache-dir");
+        ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid);
+    }
     if (ret == 0 && internal_storage_has_project_id()) {
+        ScopedTrace tracer("set-quota-cache-dir");
         return set_quota_project_id(path, project_id, true);
     }
     return ret;
 }
 
 static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
-    if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
-        return true;
-    }
-
+    ScopedTrace tracer("prepare-app-profile");
     int32_t uid = multiuser_get_uid(userId, appId);
     int shared_app_gid = multiuser_get_shared_gid(userId, appId);
     if (shared_app_gid == -1) {
@@ -635,6 +646,7 @@
                                         int32_t previousUid, int32_t cacheGid,
                                         const std::string& seInfo, mode_t targetMode,
                                         long projectIdApp, long projectIdCache) {
+    ScopedTrace tracer("create-dirs");
     struct stat st{};
     bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
 
@@ -711,6 +723,7 @@
     long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START);
 
     if (flags & FLAG_STORAGE_CE) {
+        ScopedTrace tracer("ce");
         auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
 
         auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode,
@@ -737,6 +750,7 @@
         }
     }
     if (flags & FLAG_STORAGE_DE) {
+        ScopedTrace tracer("de");
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
 
         auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode,
@@ -754,13 +768,14 @@
     }
 
     if (flags & FLAG_STORAGE_SDK) {
+        ScopedTrace tracer("sdk");
         // Safe to ignore status since we can retry creating this by calling reconcileSdkData
         auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags);
         if (!ignore.isOk()) {
             PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
         }
-
     } else {
+        ScopedTrace tracer("destroy-sdk");
         // Package does not need sdk storage. Remove it.
         destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
     }
@@ -1852,6 +1867,8 @@
     CHECK_ARGUMENT_UUID(uuid);
     LOCK_USER();
 
+    ScopedTrace tracer("create-user-data");
+
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     if (flags & FLAG_STORAGE_DE) {
         if (uuid_ == nullptr) {
@@ -2357,11 +2374,15 @@
         p->fts_number = p->fts_parent->fts_number;
         switch (p->fts_info) {
         case FTS_D:
-            if (p->fts_level == 4
+            if (p->fts_level == 3
+                    && !strcmp(p->fts_parent->fts_name, "obb")
+                    && !strcmp(p->fts_parent->fts_parent->fts_name, "Android")) {
+                p->fts_number = 1;
+            } else if (p->fts_level == 4
                     && !strcmp(p->fts_name, "cache")
                     && !strcmp(p->fts_parent->fts_parent->fts_name, "data")
                     && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
-                p->fts_number = 1;
+                p->fts_number = 2;
             }
             [[fallthrough]]; // to count the directory
         case FTS_DEFAULT:
@@ -2370,9 +2391,13 @@
         case FTS_SLNONE:
             int64_t size = (p->fts_statp->st_blocks * 512);
             if (p->fts_number == 1) {
-                stats->cacheSize += size;
+                stats->codeSize += size;
+            } else {
+                if (p->fts_number == 2) {
+                    stats->cacheSize += size;
+                }
+                stats->dataSize += size;
             }
-            stats->dataSize += size;
             break;
         }
     }
@@ -2718,11 +2743,6 @@
         extStats.dataSize = dataSize;
         atrace_pm_end();
     } else {
-        atrace_pm_begin("obb");
-        auto obbPath = create_data_path(uuid_) + "/media/obb";
-        calculate_tree_size(obbPath, &extStats.codeSize);
-        atrace_pm_end();
-
         atrace_pm_begin("code");
         calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
         atrace_pm_end();
@@ -2753,9 +2773,10 @@
         atrace_pm_begin("external");
         auto dataMediaPath = create_data_media_path(uuid_, userId);
         collectManualExternalStatsForUser(dataMediaPath, &extStats);
+
 #if MEASURE_DEBUG
         LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache "
-                << extStats.cacheSize;
+                << extStats.cacheSize << " code " << extStats.codeSize;
 #endif
         atrace_pm_end();
 
@@ -3557,17 +3578,29 @@
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
 
     std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
-    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create CE mirror");
+    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create CE data mirror");
     }
 
     std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
-    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create DE mirror");
+    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE data mirror");
+    }
+
+    std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create CE misc mirror");
+    }
+
+    std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE misc mirror");
     }
 
     auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
     auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+    auto miscCePath = StringPrintf("%s/misc_ce", create_data_path(uuid_).c_str());
+    auto miscDePath = StringPrintf("%s/misc_de", create_data_path(uuid_).c_str());
 
     if (access(cePath.c_str(), F_OK) != 0) {
         return error("Cannot access CE path: " + cePath);
@@ -3575,6 +3608,12 @@
     if (access(dePath.c_str(), F_OK) != 0) {
         return error("Cannot access DE path: " + dePath);
     }
+    if (access(miscCePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc CE path: " + cePath);
+    }
+    if (access(miscDePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc DE path: " + dePath);
+    }
 
     struct stat ceStat, mirrorCeStat;
     if (stat(cePath.c_str(), &ceStat) != 0) {
@@ -3602,6 +3641,21 @@
             MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
         return error("Failed to mount " + mirrorVolDePath);
     }
+
+    // Mount misc CE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscCePath.c_str(), mirrorVolMiscCePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscCePath);
+    }
+
+    // Mount misc DE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscDePath.c_str(), mirrorVolMiscDePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscDePath);
+    }
+
     return ok();
 }
 
@@ -3624,6 +3678,8 @@
 
     std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    std::string mirrorMiscCeVolPath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    std::string mirrorMiscDeVolPath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
 
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
 
@@ -3648,6 +3704,29 @@
     if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
         res = error("Failed to delete " + mirrorDeVolPath);
     }
+
+    // Unmount misc CE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscCeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscCeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscCeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscCeVolPath);
+    }
+
+    // Unmount misc DE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscDeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscDeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscDeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscDeVolPath);
+    }
+
     return res;
 }
 
diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h
index 18506a9..0deaeb4 100644
--- a/cmds/installd/SysTrace.h
+++ b/cmds/installd/SysTrace.h
@@ -19,4 +19,16 @@
 namespace android::installd {
 void atrace_pm_begin(const char*);
 void atrace_pm_end();
+
+class ScopedTrace {
+public:
+    explicit ScopedTrace(const char* label) { atrace_pm_begin(label); }
+    ~ScopedTrace() { atrace_pm_end(); }
+
+private:
+    ScopedTrace(const ScopedTrace&) = delete;
+    ScopedTrace& operator=(const ScopedTrace&) = delete;
+    ScopedTrace(ScopedTrace&&) = delete;
+    ScopedTrace& operator=(ScopedTrace&&) = delete;
+};
 } /* namespace android::installd */
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 34ea759..1693ed5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -442,6 +442,16 @@
 static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
         const std::string& location, bool read_write, bool is_secondary_dex) {
     std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
+    if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) {
+        // ART Service doesn't use flock and instead assumes profile files are
+        // immutable, so ensure we don't open a file for writing when it's
+        // active.
+        // TODO(b/251921228): Normally installd isn't called at all in that
+        // case, but OTA is still an exception that uses the legacy code.
+        LOG(ERROR) << "Opening ref profile " << profile
+                   << " for writing is unsafe when ART Service is enabled.";
+        return invalid_unique_fd();
+    }
     return open_profile(
         uid,
         profile,
@@ -450,14 +460,13 @@
 }
 
 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) {
+                                                        const std::string& location,
+                                                        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,
-        S_IRUSR | S_IWUSR | S_IRGRP);  // so that ART can also read it when apps run.
+    unique_fd ufd = open_profile(uid, profile_path, O_RDONLY,
+                                 S_IRUSR | S_IWUSR |
+                                         S_IRGRP); // so that ART can also read it when apps run.
 
     return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
         clear_profile(path);
@@ -1104,8 +1113,7 @@
             location = profile_name;
         }
     }
-    return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
-                                                 is_secondary_dex);
+    return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex);
 }
 
 // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
@@ -1909,10 +1917,11 @@
     // Open the reference profile if needed.
     UniqueFile reference_profile = maybe_open_reference_profile(
             pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
-
-    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.
+    struct stat sbuf;
+    if (reference_profile.fd() == -1 ||
+        (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+        // We don't create an app image with empty or non existing reference profile since there
+        // is no speedup from loading it in that case and instead will be a small overhead.
         generate_app_image = false;
     }
 
@@ -1933,7 +1942,8 @@
         RUNTIME_NATIVE_BOOT_NAMESPACE,
         ENABLE_JITZYGOTE_IMAGE,
         /*default_value=*/ "");
-    bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+    bool compile_without_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable() ||
+            force_compile_without_image();
 
     // Decide whether to use dex2oat64.
     bool use_dex2oat64 = false;
@@ -1955,7 +1965,7 @@
                       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, target_sdk_version,
-                      enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
+                      enable_hidden_api_checks, generate_compact_dex, compile_without_image,
                       background_job_compile, compilation_reason);
 
     bool cancelled = false;
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index b5bc28c..4f691c9 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -65,6 +65,10 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
+bool force_compile_without_image() {
+    return false;
+}
+
 static bool initialize_globals() {
     return init_globals_from_data_and_root();
 }
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 240aa49..525f0c8 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,6 +1,8 @@
 
 service installd /system/bin/installd
     class main
+    user root
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL SETGID SETUID SYS_ADMIN
 
 on early-boot
     mkdir /config/sdcardfs/extensions/1055
diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h
index 5093178..0d0a7fa 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,6 +57,9 @@
                               const char *src,
                               const char *instruction_set);
 
+// If true, pass "--force-jit-zygote" to dex2oat (i.e., compile without a boot image).
+extern bool force_compile_without_image();
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..7cabdb0 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
         // This is different from the normal installd. We only do the base
         // directory, the rest will be created on demand when each app is compiled.
         if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
-            LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+            PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
             return false;
         }
 
@@ -460,7 +460,7 @@
         // this tool will wipe the OTA artifact cache and try again (for robustness after
         // a failed OTA with remaining cache artifacts).
         if (access(apk_path, F_OK) != 0) {
-            LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+            PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
             return true;
         }
 
@@ -711,6 +711,11 @@
     return true;
 }
 
+bool force_compile_without_image() {
+    // We don't have a boot image anyway. Compile without a boot image.
+    return true;
+}
+
 static int log_callback(int type, const char *fmt, ...) {
     va_list ap;
     int priority;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..c86993c 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
 namespace android {
 namespace installd {
 
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
 static void CloseDescriptor(int fd) {
     if (fd >= 0) {
         int result = close(fd);
@@ -82,6 +86,27 @@
     }
 }
 
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+    for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+        const char* fstype = kTryMountFsTypes[i];
+        int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+        if (mount_result == 0) {
+            return true;
+        }
+        if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+            // Only try the next fstype if mounting failed due to the current one
+            // being invalid.
+            LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+                         << fstype << " - trying " << kTryMountFsTypes[i + 1];
+        } else {
+            PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+                        << fstype;
+            return false;
+        }
+    }
+    __builtin_unreachable();
+}
+
 static void TryExtraMount(const char* name, const char* slot, const char* target) {
     std::string partition_name = StringPrintf("%s%s", name, slot);
 
@@ -91,12 +116,7 @@
         if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
             std::string path;
             if (dm.GetDmDevicePathByName(partition_name, &path)) {
-                int mount_result = mount(path.c_str(),
-                                         target,
-                                         "ext4",
-                                         MS_RDONLY,
-                                         /* data */ nullptr);
-                if (mount_result == 0) {
+                if (TryMountWithFstypes(path.c_str(), target)) {
                     return;
                 }
             }
@@ -105,12 +125,7 @@
 
     // Fall back and attempt a direct mount.
     std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
-    int mount_result = mount(block_device.c_str(),
-                             target,
-                             "ext4",
-                             MS_RDONLY,
-                             /* data */ nullptr);
-    UNUSED(mount_result);
+    (void)TryMountWithFstypes(block_device.c_str(), target);
 }
 
 // Entry for otapreopt_chroot. Expected parameters are:
@@ -150,7 +165,8 @@
 
     // Bind mount necessary directories.
     constexpr const char* kBindMounts[] = {
-            "/data", "/dev", "/proc", "/sys"
+            "/data", "/dev", "/proc", "/sys",
+            "/sys/fs/selinux" /* Required for apexd which includes libselinux */
     };
     for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
         std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
 
 i=0
 while ((i<MAXIMUM_PACKAGES)) ; do
+  DONE=$(cmd otadexopt done)
+  if [ "$DONE" = "OTA complete." ] ; then
+    break
+  fi
+
   DEXOPT_PARAMS=$(cmd otadexopt next)
 
   /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
   PROGRESS=$(cmd otadexopt progress)
   print -u${STATUS_FD} "global_progress $PROGRESS"
 
-  DONE=$(cmd otadexopt done)
-  if [ "$DONE" = "OTA incomplete." ] ; then
-    sleep 1
-    i=$((i+1))
-    continue
-  fi
-  break
+  sleep 1
+  i=$((i+1))
 done
 
 DONE=$(cmd otadexopt done)
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 07f73b9..61fe316 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -77,10 +77,8 @@
     },
 }
 
-cc_test {
-    name: "installd_service_test",
-    test_suites: ["device-tests"],
-    srcs: ["installd_service_test.cpp"],
+cc_defaults {
+    name: "installd_service_test_defaults",
     cflags: [
         "-Wall",
         "-Werror",
@@ -106,8 +104,6 @@
         "liblogwrap",
         "libc++fs",
     ],
-    test_config: "installd_service_test.xml",
-
     product_variables: {
         arc: {
             exclude_srcs: [
@@ -125,6 +121,14 @@
 }
 
 cc_test {
+    name: "installd_service_test",
+    test_suites: ["device-tests"],
+    srcs: ["installd_service_test.cpp"],
+    defaults: ["installd_service_test_defaults"],
+    test_config: "installd_service_test.xml",
+}
+
+cc_test {
     name: "installd_dexopt_test",
     test_suites: ["device-tests"],
     srcs: ["installd_dexopt_test.cpp"],
@@ -209,3 +213,19 @@
         "liblog",
     ],
 }
+
+cc_fuzz {
+    name: "installd_service_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+        "installd_service_test_defaults",
+    ],
+    srcs: ["fuzzers/InstalldServiceFuzzer.cpp"],
+    fuzz_config: {
+        cc: [
+            "android-package-manager-team@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+}
diff --git a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
new file mode 100644
index 0000000..b1c6940
--- /dev/null
+++ b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fuzzbinder/libbinder_driver.h>
+
+#include "InstalldNativeService.h"
+#include "dexopt.h"
+
+using ::android::fuzzService;
+using ::android::sp;
+using ::android::installd::InstalldNativeService;
+
+namespace android {
+namespace installd {
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char* oat_dir, const char* apk_path,
+                             const char* instruction_set) {
+    return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char* apk_path,
+                              const char* instruction_set) {
+    return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char* src, const char* instruction_set) {
+    return create_cache_path_default(path, src, instruction_set);
+}
+
+bool force_compile_without_image() {
+    return false;
+}
+
+} // namespace installd
+} // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto service = sp<InstalldNativeService>::make();
+    fuzzService(service, FuzzedDataProvider(data, size));
+    return 0;
+}
\ No newline at end of file
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 4976646..a00c2c7 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -67,6 +67,10 @@
     return false;
 }
 
+bool force_compile_without_image() {
+    return false;
+}
+
 static void mkdir(const char* path) {
     const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
     ::mkdir(fullPath.c_str(), 0755);
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 3b589dc..c4071c6 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -73,6 +73,10 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
+bool force_compile_without_image() {
+    return false;
+}
+
 static void run_cmd(const std::string& cmd) {
     system(cmd.c_str());
 }
@@ -185,7 +189,7 @@
     std::optional<std::string> volume_uuid_;
     std::string package_name_;
     std::string apk_path_;
-    std::string empty_dm_file_;
+    std::string dm_file_;
     std::string app_apk_dir_;
     std::string app_private_dir_ce_;
     std::string app_private_dir_de_;
@@ -199,6 +203,10 @@
     std::string secondary_dex_de_;
 
     virtual void SetUp() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+        }
+
         setenv("ANDROID_LOG_TAGS", "*:v", 1);
         android::base::InitLogging(nullptr);
         // Initialize the globals holding the file system main paths (/data/, /system/ etc..).
@@ -219,6 +227,10 @@
     }
 
     virtual void TearDown() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP();
+        }
+
         if (!kDebug) {
             service_->controlDexOptBlocking(false);
             service_->destroyAppData(
@@ -248,26 +260,6 @@
                                                  << " : " << error_msg;
         }
 
-        // Create an empty dm file.
-        empty_dm_file_ = apk_path_ + ".dm";
-        {
-            int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
-            if (fd < 0) {
-                return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
-            }
-            FILE* file = fdopen(fd, "wb");
-            if (file == nullptr) {
-                return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
-                         << " fd=" << fd;
-            }
-            ZipWriter writer(file);
-            // Add vdex to zip.
-            writer.StartEntry("primary.prof", ZipWriter::kCompress);
-            writer.FinishEntry();
-            writer.Finish();
-            fclose(file);
-          }
-
         // Create the app user data.
         binder::Status status = service_->createAppData(
                 volume_uuid_,
@@ -316,6 +308,46 @@
                                                  << secondary_dex_de_ << " : " << error_msg;
         }
 
+        // Create a non-empty dm file.
+        dm_file_ = apk_path_ + ".dm";
+        {
+            android::base::unique_fd fd(open(dm_file_.c_str(),
+                                          O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
+            if (fd.get() < 0) {
+                return ::testing::AssertionFailure() << "Could not open " << dm_file_;
+            }
+            FILE* file = fdopen(fd.release(), "wb");
+            if (file == nullptr) {
+                return ::testing::AssertionFailure() << "Null file for " << dm_file_
+                                  << " fd=" << fd.get();
+            }
+
+            // Create a profile file.
+            std::string profile_file = app_private_dir_ce_ + "/primary.prof";
+            run_cmd("profman --generate-test-profile=" + profile_file);
+
+            // Add profile to zip.
+            ZipWriter writer(file);
+            writer.StartEntry("primary.prof", ZipWriter::kCompress);
+            android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY));
+            if (profile_fd.get() < 0) {
+                return ::testing::AssertionFailure() << "Failed to open profile '"
+                                  << profile_file << "'";
+            }
+            std::string profile_content;
+            if (!android::base::ReadFdToString(profile_fd, &profile_content)) {
+                return ::testing::AssertionFailure() << "Failed to read profile "
+                                  << profile_file << "'";
+            }
+            writer.WriteBytes(profile_content.c_str(), profile_content.length());
+            writer.FinishEntry();
+            writer.Finish();
+            fclose(file);
+
+            // Delete the temp file.
+            unlink(profile_file.c_str());
+        }
+
         // Fix app data uid.
         status = service_->fixupAppData(volume_uuid_, kTestUserId);
         if (!status.isOk()) {
@@ -608,7 +640,7 @@
                 kTestAppGid,
                 DEX2OAT_FROM_SCRATCH,
                 /*binder_result=*/nullptr,
-                empty_dm_file_.c_str());
+                dm_file_.c_str());
 
 
         int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
@@ -657,13 +689,13 @@
                             DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
                                     DEXOPT_GENERATE_APP_IMAGE,
                             oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
-                            /*binder_result=*/nullptr, empty_dm_file_.c_str());
+                            /*binder_result=*/nullptr, dm_file_.c_str());
         checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
 
         CompilePrimaryDexOk("speed-profile",
                             DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
                             oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
-                            /*binder_result=*/nullptr, empty_dm_file_.c_str());
+                            /*binder_result=*/nullptr, dm_file_.c_str());
         checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
     }
 };
@@ -787,7 +819,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
 }
 
 TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -799,7 +831,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
 }
 
 TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -811,7 +843,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
 }
 
 TEST_F(DexoptTest, DexoptBlockPrimary) {
@@ -874,7 +906,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
     run_cmd_and_process_output(
             "oatdump --header-only --oat-file=" + odex,
             [&](const std::string& line) {
@@ -893,7 +925,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
     run_cmd_and_process_output(
             "oatdump --header-only --oat-file=" + odex,
             [&](const std::string& line) {
@@ -926,7 +958,7 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
     // Enable the property and use dex2oat64.
     ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
     CompilePrimaryDexOk("speed-profile",
@@ -936,12 +968,16 @@
                         kTestAppGid,
                         DEX2OAT_FROM_SCRATCH,
                         /*binder_result=*/nullptr,
-                        empty_dm_file_.c_str());
+                        dm_file_.c_str());
 }
 
 class PrimaryDexReCompilationTest : public DexoptTest {
   public:
     virtual void SetUp() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+        }
+
         DexoptTest::SetUp();
         CompilePrimaryDexOk("verify",
                             DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
@@ -956,6 +992,10 @@
     }
 
     virtual void TearDown() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP();
+        }
+
         first_compilation_odex_fd_.reset(-1);
         first_compilation_vdex_fd_.reset(-1);
         DexoptTest::TearDown();
@@ -978,6 +1018,10 @@
 
 class ReconcileTest : public DexoptTest {
     virtual void SetUp() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+        }
+
         DexoptTest::SetUp();
         CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
             /*binder_ok*/ true, /*compile_ok*/ true);
@@ -1043,6 +1087,10 @@
     static constexpr const char* kPrimaryProfile = "primary.prof";
 
     virtual void SetUp() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+        }
+
         DexoptTest::SetUp();
         cur_profile_ = create_current_profile_path(
                 kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/ false);
@@ -1143,7 +1191,7 @@
                 service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
                                             kTestAppId, profile_name, apk_path_,
                                             has_dex_metadata ? std::make_optional<std::string>(
-                                                                       empty_dm_file_)
+                                                                       dm_file_)
                                                              : std::nullopt,
                                             &result));
         ASSERT_EQ(expected_result, result);
@@ -1416,6 +1464,9 @@
     std::vector<int64_t> extra_ce_data_inodes_;
 
     virtual void SetUp() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP() << "Skipping legacy dexopt tests when ART Service is enabled";
+        }
 
         ProfileTest::SetUp();
         intial_android_profiles_dir = android_profiles_dir;
@@ -1429,6 +1480,10 @@
     }
 
     virtual void TearDown() {
+        if (base::GetBoolProperty("dalvik.vm.useartservice", false)) {
+            GTEST_SKIP();
+        }
+
         android_profiles_dir = intial_android_profiles_dir;
         deleteAppProfilesForBootMerge();
         ProfileTest::TearDown();
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index f86f1d5..858a92c 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -113,6 +113,10 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
+bool force_compile_without_image() {
+    return false;
+}
+
 static std::string get_full_path(const std::string& path) {
     return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str());
 }
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index ff73c94..e54f9d3 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -353,8 +353,16 @@
         return false;
     }
 
+    auto vintfFqInstance = vintf::FqInstance::from(fqInstance.string());
+    if (!vintfFqInstance.has_value()) {
+        err() << "Unable to convert " << fqInstance.string() << " to vintf::FqInstance"
+              << std::endl;
+        return false;
+    }
+
     std::string e;
-    if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) {
+    if (!manifest->insertInstance(*vintfFqInstance, entry.transport, arch, vintf::HalFormat::HIDL,
+                                  &e)) {
         err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl;
         return false;
     }
diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp
index af85666..d0e4b74 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -37,4 +37,8 @@
         "include",
     ],
     min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.neuralnetworks",
+    ],
 }
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index d5ca725..5e8ef5d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -75,7 +75,7 @@
     ProcessState::initWithDriver("/dev/vndbinder");
 #endif
 #ifndef __ANDROID__
-    setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+    setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
 #endif
     sp<IServiceManager> sm = defaultServiceManager();
     fflush(stdout);
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 1386660..fb69513 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -72,6 +72,7 @@
 
 cc_test {
     name: "servicemanager_test",
+    host_supported: true,
     test_suites: ["device-tests"],
     defaults: ["servicemanager_defaults"],
     srcs: [
@@ -92,9 +93,22 @@
         libfuzzer_options: [
             "max_len=50000",
         ],
-        cc: [
-            "smoreland@google.com",
-            "waghpawan@google.com",
+    },
+}
+
+// Adding this new fuzzer to test the corpus generated by record_binder
+cc_fuzz {
+    name: "servicemanager_test_fuzzer",
+    defaults: [
+        "servicemanager_defaults",
+        "service_fuzzer_defaults",
+    ],
+    host_supported: true,
+    srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"],
+    fuzz_config: {
+        libfuzzer_options: [
+            "max_len=50000",
         ],
     },
+    corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"],
 }
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index cc038ae..cae9684 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -222,6 +222,18 @@
 }
 #endif  // !VENDORSERVICEMANAGER
 
+ServiceManager::Service::~Service() {
+    if (hasClients) {
+        // only expected to happen on process death, we don't store the service
+        // name this late (it's in the map that holds this service), but if it
+        // is happening, we might want to change 'unlinkToDeath' to explicitly
+        // clear this bit so that we can abort in other cases, where it would
+        // mean inconsistent logic in servicemanager (unexpected and tested, but
+        // the original lazy service impl here had that bug).
+        LOG(WARNING) << "a service was removed when there are clients";
+    }
+}
+
 ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {
 // TODO(b/151696835): reenable performance hack when we solve bug, since with
 //     this hack and other fixes, it is unlikely we will see even an ephemeral
@@ -289,12 +301,17 @@
     }
 
     if (!out && startIfNotFound) {
-        tryStartService(name);
+        tryStartService(ctx, name);
     }
 
     if (out) {
-        // Setting this guarantee each time we hand out a binder ensures that the client-checking
-        // loop knows about the event even if the client immediately drops the service
+        // Force onClients to get sent, and then make sure the timerfd won't clear it
+        // by setting guaranteeClient again. This logic could be simplified by using
+        // a time-based guarantee. However, forcing onClients(true) to get sent
+        // right here is always going to be important for processes serving multiple
+        // lazy interfaces.
+        service->guaranteeClient = true;
+        CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
         service->guaranteeClient = true;
     }
 
@@ -320,39 +337,45 @@
     auto ctx = mAccess->getCallingContext();
 
     if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
-        return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services");
+        return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services.");
     }
 
     if (!mAccess->canAdd(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     if (binder == nullptr) {
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder");
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder.");
     }
 
     if (!isValidServiceName(name)) {
         ALOGE("Invalid service name: %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name");
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
     }
 
 #ifndef VENDORSERVICEMANAGER
     if (!meetsDeclarationRequirements(binder, name)) {
         // already logged
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error");
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error.");
     }
 #endif  // !VENDORSERVICEMANAGER
 
+    if ((dumpPriority & DUMP_FLAG_PRIORITY_ALL) == 0) {
+        ALOGW("Dump flag priority is not set when adding %s", name.c_str());
+    }
+
     // implicitly unlinked when the binder is removed
     if (binder->remoteBinder() != nullptr &&
         binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
         ALOGE("Could not linkToDeath when adding %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
     }
 
     auto it = mNameToService.find(name);
+    bool prevClients = false;
     if (it != mNameToService.end()) {
         const Service& existing = it->second;
+        prevClients = existing.hasClients;
 
         // We could do better than this because if the other service dies, it
         // may not have an entry here. However, this case is unlikely. We are
@@ -380,12 +403,19 @@
             .binder = binder,
             .allowIsolated = allowIsolated,
             .dumpPriority = dumpPriority,
+            .hasClients = prevClients, // see b/279898063, matters if existing callbacks
+            .guaranteeClient = false,
             .ctx = ctx,
     };
 
     if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
+        // If someone is currently waiting on the service, notify the service that
+        // we're waiting and flush it to the service.
+        mNameToService[name].guaranteeClient = true;
+        CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
+        mNameToService[name].guaranteeClient = true;
+
         for (const sp<IServiceCallback>& cb : it->second) {
-            mNameToService[name].guaranteeClient = true;
             // permission checked in registerForNotifications
             cb->onRegistration(name, binder);
         }
@@ -396,7 +426,7 @@
 
 Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
     if (!mAccess->canList(mAccess->getCallingContext())) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     size_t toReserve = 0;
@@ -430,18 +460,18 @@
 
     if (!isValidServiceName(name)) {
         ALOGE("Invalid service name: %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
     }
 
     if (callback == nullptr) {
-        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null callback.");
     }
 
     if (OK !=
         IInterface::asBinder(callback)->linkToDeath(
                 sp<ServiceManager>::fromExisting(this))) {
         ALOGE("Could not linkToDeath when adding %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't link to death.");
     }
 
     mNameToRegistrationCallback[name].push_back(callback);
@@ -461,7 +491,7 @@
     auto ctx = mAccess->getCallingContext();
 
     if (!mAccess->canFind(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     bool found = false;
@@ -473,7 +503,7 @@
 
     if (!found) {
         ALOGE("Trying to unregister callback, but none exists %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Nothing to unregister.");
     }
 
     return Status::ok();
@@ -483,7 +513,7 @@
     auto ctx = mAccess->getCallingContext();
 
     if (!mAccess->canFind(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     *outReturn = false;
@@ -511,7 +541,7 @@
     }
 
     if (outReturn->size() == 0 && allInstances.size() != 0) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     return Status::ok();
@@ -522,7 +552,7 @@
     auto ctx = mAccess->getCallingContext();
 
     if (!mAccess->canFind(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     *outReturn = std::nullopt;
@@ -551,7 +581,7 @@
     }
 
     if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) {
-        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     return Status::ok();
@@ -562,7 +592,7 @@
     auto ctx = mAccess->getCallingContext();
 
     if (!mAccess->canFind(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     *outReturn = std::nullopt;
@@ -597,6 +627,14 @@
 void ServiceManager::binderDied(const wp<IBinder>& who) {
     for (auto it = mNameToService.begin(); it != mNameToService.end();) {
         if (who == it->second.binder) {
+            // TODO: currently, this entry contains the state also
+            // associated with mNameToClientCallback. If we allowed
+            // other processes to register client callbacks, we
+            // would have to preserve hasClients (perhaps moving
+            // that state into mNameToClientCallback, which is complicated
+            // because those callbacks are associated w/ particular binder
+            // objects, though they are indexed by name now, they may
+            // need to be indexed by binder at that point).
             it = mNameToService.erase(it);
         } else {
             ++it;
@@ -612,10 +650,11 @@
     }
 }
 
-void ServiceManager::tryStartService(const std::string& name) {
-    ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not "
-          "configured to be a lazy service, it may be stuck starting or still starting).",
-          name.c_str());
+void ServiceManager::tryStartService(const Access::CallingContext& ctx, const std::string& name) {
+    ALOGI("Since '%s' could not be found (requested by debug pid %d), trying to start it as a lazy "
+          "AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or "
+          "still starting).",
+          name.c_str(), ctx.debugPid);
 
     std::thread([=] {
         if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
@@ -631,40 +670,54 @@
 Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
                                               const sp<IClientCallback>& cb) {
     if (cb == nullptr) {
-        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Callback null.");
     }
 
     auto ctx = mAccess->getCallingContext();
     if (!mAccess->canAdd(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     auto serviceIt = mNameToService.find(name);
     if (serviceIt == mNameToService.end()) {
         ALOGE("Could not add callback for nonexistent service: %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Service doesn't exist.");
     }
 
     if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         ALOGW("Only a server can register for client callbacks (for %s)", name.c_str());
-        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+                                         "Only service can register client callback for itself.");
     }
 
     if (serviceIt->second.binder != service) {
         ALOGW("Tried to register client callback for %s but a different service is registered "
               "under this name.",
               name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Service mismatch.");
     }
 
     if (OK !=
         IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
         ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
+    }
+
+    // WARNING: binderDied makes an assumption about this. If we open up client
+    // callbacks to other services, certain race conditions may lead to services
+    // getting extra client callback notifications.
+    // Make sure all callbacks have been told about a consistent state - b/278038751
+    if (serviceIt->second.hasClients) {
+        cb->onClients(service, true);
     }
 
     mNameToClientCallback[name].push_back(cb);
 
+    // Flush updated info to client callbacks (especially if guaranteeClient
+    // and !hasClient, see b/285202885). We may or may not have clients at
+    // this point, so ignore the return value.
+    (void)handleServiceClientCallback(2 /* sm + transaction */, name, false);
+
     return Status::ok();
 }
 
@@ -696,67 +749,74 @@
 
 void ServiceManager::handleClientCallbacks() {
     for (const auto& [name, service] : mNameToService) {
-        handleServiceClientCallback(name, true);
+        handleServiceClientCallback(1 /* sm has one refcount */, name, true);
     }
 }
 
-ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName,
-                                                    bool isCalledOnInterval) {
+bool ServiceManager::handleServiceClientCallback(size_t knownClients,
+                                                 const std::string& serviceName,
+                                                 bool isCalledOnInterval) {
     auto serviceIt = mNameToService.find(serviceName);
     if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
-        return -1;
+        return true; // return we do have clients a.k.a. DON'T DO ANYTHING
     }
 
     Service& service = serviceIt->second;
     ssize_t count = service.getNodeStrongRefCount();
 
-    // binder driver doesn't support this feature
-    if (count == -1) return count;
+    // binder driver doesn't support this feature, consider we have clients
+    if (count == -1) return true;
 
-    bool hasClients = count > 1; // this process holds a strong count
+    bool hasKernelReportedClients = static_cast<size_t>(count) > knownClients;
 
     if (service.guaranteeClient) {
-        // we have no record of this client
-        if (!service.hasClients && !hasClients) {
-            sendClientCallbackNotifications(serviceName, true);
+        if (!service.hasClients && !hasKernelReportedClients) {
+            sendClientCallbackNotifications(serviceName, true,
+                                            "service is guaranteed to be in use");
         }
 
         // guarantee is temporary
         service.guaranteeClient = false;
     }
 
-    // only send notifications if this was called via the interval checking workflow
-    if (isCalledOnInterval) {
-        if (hasClients && !service.hasClients) {
-            // client was retrieved in some other way
-            sendClientCallbackNotifications(serviceName, true);
-        }
+    // Regardless of this situation, we want to give this notification as soon as possible.
+    // This way, we have a chance of preventing further thrashing.
+    if (hasKernelReportedClients && !service.hasClients) {
+        sendClientCallbackNotifications(serviceName, true, "we now have a record of a client");
+    }
 
-        // there are no more clients, but the callback has not been called yet
-        if (!hasClients && service.hasClients) {
-            sendClientCallbackNotifications(serviceName, false);
+    // But limit rate of shutting down service.
+    if (isCalledOnInterval) {
+        if (!hasKernelReportedClients && service.hasClients) {
+            sendClientCallbackNotifications(serviceName, false,
+                                            "we now have no record of a client");
         }
     }
 
-    return count;
+    // May be different than 'hasKernelReportedClients'. We intentionally delay
+    // information about clients going away to reduce thrashing.
+    return service.hasClients;
 }
 
-void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName,
+                                                     bool hasClients, const char* context) {
     auto serviceIt = mNameToService.find(serviceName);
     if (serviceIt == mNameToService.end()) {
-        ALOGW("sendClientCallbackNotifications could not find service %s", serviceName.c_str());
+        ALOGW("sendClientCallbackNotifications could not find service %s when %s",
+              serviceName.c_str(), context);
         return;
     }
     Service& service = serviceIt->second;
 
-    CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
-        << " so we can't tell clients again that we have client: " << hasClients;
+    CHECK_NE(hasClients, service.hasClients) << context;
 
-    ALOGI("Notifying %s they have clients: %d", serviceName.c_str(), hasClients);
+    ALOGI("Notifying %s they %s (previously: %s) have clients when %s", serviceName.c_str(),
+          hasClients ? "do" : "don't", service.hasClients ? "do" : "don't", context);
 
     auto ccIt = mNameToClientCallback.find(serviceName);
     CHECK(ccIt != mNameToClientCallback.end())
-        << "sendClientCallbackNotifications could not find callbacks for service ";
+            << "sendClientCallbackNotifications could not find callbacks for service when "
+            << context;
 
     for (const auto& callback : ccIt->second) {
         callback->onClients(service.binder, hasClients);
@@ -767,24 +827,25 @@
 
 Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
     if (binder == nullptr) {
-        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null service.");
     }
 
     auto ctx = mAccess->getCallingContext();
     if (!mAccess->canAdd(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     auto serviceIt = mNameToService.find(name);
     if (serviceIt == mNameToService.end()) {
         ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.",
               name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Service not registered.");
     }
 
     if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         ALOGW("Only a server can unregister itself (for %s)", name.c_str());
-        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+                                         "Service can only unregister itself.");
     }
 
     sp<IBinder> storedBinder = serviceIt->second.binder;
@@ -792,29 +853,35 @@
     if (binder != storedBinder) {
         ALOGW("Tried to unregister %s, but a different service is registered under this name.",
               name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+                                         "Different service registered under this name.");
     }
 
+    // important because we don't have timer-based guarantees, we don't want to clear
+    // this
     if (serviceIt->second.guaranteeClient) {
         ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+                                         "Can't unregister, pending client.");
     }
 
-    int clients = handleServiceClientCallback(name, false);
-
-    // clients < 0: feature not implemented or other error. Assume clients.
-    // Otherwise:
     // - kernel driver will hold onto one refcount (during this transaction)
     // - servicemanager has a refcount (guaranteed by this transaction)
-    // So, if clients > 2, then at least one other service on the system must hold a refcount.
-    if (clients < 0 || clients > 2) {
-        // client callbacks are either disabled or there are other clients
-        ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients);
-        // Set this flag to ensure the clients are acknowledged in the next callback
+    constexpr size_t kKnownClients = 2;
+
+    if (handleServiceClientCallback(kKnownClients, name, false)) {
+        ALOGI("Tried to unregister %s, but there are clients.", name.c_str());
+
+        // Since we had a failed registration attempt, and the HIDL implementation of
+        // delaying service shutdown for multiple periods wasn't ported here... this may
+        // help reduce thrashing, but we should be able to remove it.
         serviceIt->second.guaranteeClient = true;
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+                                         "Can't unregister, known client.");
     }
 
+    ALOGI("Unregistering %s", name.c_str());
     mNameToService.erase(name);
 
     return Status::ok();
@@ -822,7 +889,7 @@
 
 Status ServiceManager::getServiceDebugInfo(std::vector<ServiceDebugInfo>* outReturn) {
     if (!mAccess->canList(mAccess->getCallingContext())) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
     outReturn->reserve(mNameToService.size());
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index b24c11c..3b925a4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -67,7 +67,7 @@
     void clear();
 
 protected:
-    virtual void tryStartService(const std::string& name);
+    virtual void tryStartService(const Access::CallingContext& ctx, const std::string& name);
 
 private:
     struct Service {
@@ -80,6 +80,8 @@
 
         // the number of clients of the service, including servicemanager itself
         ssize_t getNodeStrongRefCount();
+
+        ~Service();
     };
 
     using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
@@ -91,9 +93,12 @@
     void removeRegistrationCallback(const wp<IBinder>& who,
                         ServiceCallbackMap::iterator* it,
                         bool* found);
-    ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
-     // Also updates mHasClients (of what the last callback was)
-    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+    // returns whether there are known clients in addition to the count provided
+    bool handleServiceClientCallback(size_t knownClients, const std::string& serviceName,
+                                     bool isCalledOnInterval);
+    // Also updates mHasClients (of what the last callback was)
+    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients,
+                                         const char* context);
     // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
     // this updates the iterator to the next location
     void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
new file mode 100644
index 0000000..e19b6eb
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+#include <utils/StrongPointer.h>
+
+#include "Access.h"
+#include "ServiceManager.h"
+
+using ::android::Access;
+using ::android::Parcel;
+using ::android::ServiceManager;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider(data, size);
+    auto accessPtr = std::make_unique<Access>();
+    auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
+
+    // Reserved bytes
+    provider.ConsumeBytes<uint8_t>(8);
+    uint32_t code = provider.ConsumeIntegral<uint32_t>();
+    uint32_t flag = provider.ConsumeIntegral<uint32_t>();
+    std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>();
+
+    Parcel inputParcel;
+    inputParcel.setData(parcelData.data(), parcelData.size());
+
+    Parcel reply;
+    serviceManager->transact(code, inputParcel, &reply, flag);
+
+    serviceManager->clear();
+
+    return 0;
+}
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
new file mode 100644
index 0000000..e69ab49
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
new file mode 100644
index 0000000..05b27bf
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
new file mode 100644
index 0000000..ff0941b
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
new file mode 100644
index 0000000..7e5f948
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
Binary files differ
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index c1a04dd..86a45e61 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -131,7 +131,9 @@
     }
 
     IPCThreadState::self()->setTheContextObject(manager);
-    ps->becomeContextManager();
+    if (!ps->becomeContextManager()) {
+        LOG(FATAL) << "Could not become context manager";
+    }
 
     sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 3bd6db5..4f92b3a 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -5,7 +5,7 @@
     critical
     file /dev/kmsg w
     onrestart setprop servicemanager.ready false
-    onrestart restart apexd
+    onrestart restart --only-if-running apexd
     onrestart restart audioserver
     onrestart restart gatekeeperd
     onrestart class_restart --only-enabled main
diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc
index b927c01..6354fd7 100644
--- a/cmds/servicemanager/servicemanager.recovery.rc
+++ b/cmds/servicemanager/servicemanager.recovery.rc
@@ -1,5 +1,6 @@
 service servicemanager /system/bin/servicemanager
     disabled
     group system readproc
+    user root
     onrestart setprop servicemanager.ready false
     seclabel u:r:servicemanager:s0
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 0fd8d8e..9e5b8a4 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -27,11 +27,14 @@
 #include "Access.h"
 #include "ServiceManager.h"
 
-using android::sp;
 using android::Access;
 using android::BBinder;
 using android::IBinder;
 using android::ServiceManager;
+using android::sp;
+using android::base::EndsWith;
+using android::base::GetProperty;
+using android::base::StartsWith;
 using android::binder::Status;
 using android::os::BnServiceCallback;
 using android::os::IServiceManager;
@@ -62,7 +65,7 @@
 class MockServiceManager : public ServiceManager {
  public:
     MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {}
-    MOCK_METHOD1(tryStartService, void(const std::string& name));
+    MOCK_METHOD2(tryStartService, void(const Access::CallingContext&, const std::string& name));
 };
 
 static sp<ServiceManager> getPermissiveServiceManager() {
@@ -77,9 +80,11 @@
     return sm;
 }
 
-static bool isCuttlefish() {
-    return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""),
-                                     "vsoc_");
+// Determines if test device is a cuttlefish phone device
+static bool isCuttlefishPhone() {
+    auto device = GetProperty("ro.product.vendor.device", "");
+    auto product = GetProperty("ro.product.vendor.name", "");
+    return StartsWith(device, "vsoc_") && EndsWith(product, "_phone");
 }
 
 TEST(AddService, HappyHappy) {
@@ -314,7 +319,7 @@
 }
 
 TEST(Vintf, UpdatableViaApex) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::optional<std::string> updatableViaApex;
@@ -326,7 +331,7 @@
 }
 
 TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::optional<std::string> updatableViaApex;
@@ -337,7 +342,7 @@
 }
 
 TEST(Vintf, GetUpdatableNames) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::vector<std::string> names;
@@ -348,7 +353,7 @@
 }
 
 TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) {
-    if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
 
     auto sm = getPermissiveServiceManager();
     std::vector<std::string> names;
diff --git a/data/etc/android.software.ipsec_tunnel_migration.xml b/data/etc/android.software.ipsec_tunnel_migration.xml
new file mode 100644
index 0000000..c405e1e
--- /dev/null
+++ b/data/etc/android.software.ipsec_tunnel_migration.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+     This is the feature indicating that the device has support for updating
+     source and destination addresses of IPsec tunnels
+-->
+
+<permissions>
+    <feature name="android.software.ipsec_tunnel_migration" />
+</permissions>
diff --git a/include/android/OWNERS b/include/android/OWNERS
new file mode 100644
index 0000000..38f9c55
--- /dev/null
+++ b/include/android/OWNERS
@@ -0,0 +1 @@
+per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 63aa7ff..cd8e63d 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -16,6 +16,28 @@
 
 /**
  * @addtogroup Choreographer
+ *
+ * Choreographer coordinates the timing of frame rendering. This is the C version of the
+ * android.view.Choreographer object in Java.
+ *
+ * As of API level 33, apps can follow proper frame pacing and even choose a future frame to render.
+ * The API is used as follows:
+ * 1. The app posts an {@link AChoreographer_vsyncCallback} to Choreographer to run on the next
+ * frame.
+ * 2. The callback is called when it is the time to start the frame with an {@link
+ * AChoreographerFrameCallbackData} payload: information about multiple possible frame
+ * timelines.
+ * 3. Apps can choose a frame timeline from the {@link
+ * AChoreographerFrameCallbackData} payload, depending on the frame deadline they can meet when
+ * rendering the frame and their desired presentation time, and subsequently
+ * {@link ASurfaceTransaction_setFrameTimeline notify SurfaceFlinger}
+ * of the choice. Alternatively, for apps that do not choose a frame timeline, their frame would be
+ * presented at the earliest possible timeline.
+ *   - The preferred frame timeline is the default frame
+ * timeline that the platform scheduled for the app, based on device configuration.
+ * 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or
+ * latching buffers before the desired presentation time.
+ *
  * @{
  */
 
@@ -47,7 +69,8 @@
 
 struct AChoreographerFrameCallbackData;
 /**
- * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object, which contains
+ * various methods to extract frame information.
  */
 typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
 
@@ -73,8 +96,9 @@
 
 /**
  * Prototype of the function that is called when a new frame is being rendered.
- * It's passed the frame data that should not outlive the callback, as well as the data pointer
- * provided by the application that registered a callback.
+ * It is called with \c callbackData describing multiple frame timelines, as well as the \c data
+ * pointer provided by the application that registered a callback. The \c callbackData does not
+ * outlive the callback.
  */
 typedef void (*AChoreographer_vsyncCallback)(
         const AChoreographerFrameCallbackData* callbackData, void* data);
@@ -110,7 +134,7 @@
         __DEPRECATED_IN(29);
 
 /**
- * Power a callback to be run on the next frame.  The data pointer provided will
+ * Post a callback to be run on the next frame.  The data pointer provided will
  * be passed to the callback function when it's called.
  *
  * Available since API level 29.
@@ -131,8 +155,10 @@
                                                uint32_t delayMillis) __INTRODUCED_IN(29);
 
 /**
- * Posts a callback to run on the next frame. The data pointer provided will
+ * Posts a callback to be run on the next frame. The data pointer provided will
  * be passed to the callback function when it's called.
+ *
+ * Available since API level 33.
  */
 void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
                                         AChoreographer_vsyncCallback callback, void* data)
@@ -189,7 +215,10 @@
         __INTRODUCED_IN(30);
 
 /**
- * The time in nanoseconds when the frame started being rendered.
+ * The time in nanoseconds at which the frame started being rendered.
+ *
+ * Note that this time should \b not be used to advance animation clocks.
+ * Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos().
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
@@ -201,25 +230,38 @@
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
 
 /**
- * Get index of the platform-preferred FrameTimeline.
+ * Gets the index of the platform-preferred frame timeline.
+ * The preferred frame timeline is the default
+ * by which the platform scheduled the app, based on the device configuration.
  */
 size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
 
 /**
- * The vsync ID token used to map Choreographer data.
+ * Gets the token used by the platform to identify the frame timeline at the given \c index.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
- * The time in nanoseconds which the frame at given index is expected to be presented.
+ * Gets the time in nanoseconds at which the frame described at the given \c index is expected to
+ * be presented. This time should be used to advance any animation clocks.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
- * The time in nanoseconds which the frame at given index needs to be ready by.
+ * Gets the time in nanoseconds at which the frame described at the given \c index needs to be
+ * ready by in order to be presented on time.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
diff --git a/include/android/input.h b/include/android/input.h
index 38b27bc..2a7cea6 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -249,13 +249,13 @@
     AKEY_EVENT_FLAG_LONG_PRESS = 0x80,
 
     /**
-     * Set when a key event has AKEY_EVENT_FLAG_CANCELED set because a long
+     * Set when a key event has #AKEY_EVENT_FLAG_CANCELED set because a long
      * press action was executed while it was down.
      */
     AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100,
 
     /**
-     * Set for AKEY_EVENT_ACTION_UP when this event's key code is still being
+     * Set for #AKEY_EVENT_ACTION_UP when this event's key code is still being
      * tracked from its initial down.  That is, somebody requested that tracking
      * started on the key down and a long press has not caused
      * the tracking to be canceled.
@@ -275,7 +275,7 @@
 
 /**
  * Bit shift for the action bits holding the pointer index as
- * defined by AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
+ * defined by #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
  */
 #define AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8
 
@@ -286,8 +286,8 @@
 
     /**
      * Bits in the action code that represent a pointer index, used with
-     * AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP.  Shifting
-     * down by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
+     * #AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP.  Shifting
+     * down by #AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
      * index where the data for the pointer going up or down can be found.
      */
     AMOTION_EVENT_ACTION_POINTER_INDEX_MASK  = 0xff00,
@@ -302,8 +302,8 @@
     AMOTION_EVENT_ACTION_UP = 1,
 
     /**
-     * A change has happened during a press gesture (between AMOTION_EVENT_ACTION_DOWN and
-     * AMOTION_EVENT_ACTION_UP).  The motion contains the most recent point, as well as
+     * A change has happened during a press gesture (between #AMOTION_EVENT_ACTION_DOWN and
+     * #AMOTION_EVENT_ACTION_UP).  The motion contains the most recent point, as well as
      * any intermediate points since the last down or move event.
      */
     AMOTION_EVENT_ACTION_MOVE = 2,
@@ -323,18 +323,18 @@
 
     /**
      * A non-primary pointer has gone down.
-     * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
+     * The bits in #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
      */
     AMOTION_EVENT_ACTION_POINTER_DOWN = 5,
 
     /**
      * A non-primary pointer has gone up.
-     * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
+     * The bits in #AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
      */
     AMOTION_EVENT_ACTION_POINTER_UP = 6,
 
     /**
-     * A change happened but the pointer is not down (unlike AMOTION_EVENT_ACTION_MOVE).
+     * A change happened but the pointer is not down (unlike #AMOTION_EVENT_ACTION_MOVE).
      * The motion contains the most recent point, as well as any intermediate points since
      * the last hover move event.
      */
@@ -342,8 +342,8 @@
 
     /**
      * The motion event contains relative vertical and/or horizontal scroll offsets.
-     * Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL
-     * and AMOTION_EVENT_AXIS_HSCROLL.
+     * Use {@link AMotionEvent_getAxisValue} to retrieve the information from
+     * #AMOTION_EVENT_AXIS_VSCROLL and #AMOTION_EVENT_AXIS_HSCROLL.
      * The pointer may or may not be down when this event is dispatched.
      * This action is always delivered to the winder under the pointer, which
      * may not be the window currently touched.
@@ -528,7 +528,7 @@
      * is pointing in relation to the vertical axis of the current orientation of the screen.
      * The range is from -PI radians to PI radians, where 0 is pointing up,
      * -PI/2 radians is pointing left, -PI or PI radians is pointing down, and PI/2 radians
-     * is pointing right.  See also {@link AMOTION_EVENT_AXIS_TILT}.
+     * is pointing right.  See also #AMOTION_EVENT_AXIS_TILT.
      */
     AMOTION_EVENT_AXIS_ORIENTATION = 8,
     /**
@@ -681,7 +681,7 @@
     /**
      * Axis constant: The movement of y position of a motion event.
      *
-     * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position.
+     * Same as #AMOTION_EVENT_AXIS_RELATIVE_X, but for y position.
      */
     AMOTION_EVENT_AXIS_RELATIVE_Y = 28,
     /**
@@ -821,7 +821,8 @@
      * Classification constant: Ambiguous gesture.
      *
      * The user's intent with respect to the current event stream is not yet determined. Events
-     * starting in AMBIGUOUS_GESTURE will eventually resolve into either DEEP_PRESS or NONE.
+     * starting in #AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE will eventually resolve into
+     * either #AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS or #AMOTION_EVENT_CLASSIFICATION_NONE.
      * Gestural actions, such as scrolling, should be inhibited until the classification resolves
      * to another value or the event stream ends.
      */
@@ -923,7 +924,8 @@
  * Refer to the documentation on android.view.InputDevice for more details about input sources
  * and their correct interpretation.
  *
- * @deprecated These constants are deprecated. Use {@link AMOTION_EVENT_AXIS AMOTION_EVENT_AXIS_*} constants instead.
+ * @deprecated These constants are deprecated. Use {@link AMOTION_EVENT_AXIS AMOTION_EVENT_AXIS_*}
+ * constants instead.
  */
 enum {
     /** x */
@@ -977,8 +979,8 @@
 /**
  * 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.
+ * After returning, the specified {@link AInputEvent}* object becomes invalid and should no longer
+ * be used. The underlying Java object remains valid and does not change its state.
  *
  * Available since API level 31.
  */
@@ -1031,9 +1033,10 @@
 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()}.
+ * Creates a native {@link 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()}.
  *
  * Available since API level 31.
  */
@@ -1221,7 +1224,7 @@
 /**
  * Get the number of historical points in this event.  These are movements that
  * have occurred between this event and the previous event.  This only applies
- * to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
+ * to #AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
  * Historical samples are indexed from oldest to newest.
  */
 size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event);
@@ -1382,7 +1385,7 @@
         __INTRODUCED_IN(__ANDROID_API_T__);
 
 /**
- * Creates a native AInputEvent* object that is a copy of the specified Java
+ * Creates a native {@link 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()}.
@@ -1402,7 +1405,7 @@
 
 /**
  * Add this input queue to a looper for processing.  See
- * ALooper_addFd() for information on the ident, callback, and data params.
+ * {@link ALooper_addFd()} for information on the ident, callback, and data params.
  */
 void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
         int ident, ALooper_callbackFunc callback, void* data);
@@ -1437,12 +1440,12 @@
 
 /**
  * Report that dispatching has finished with the given event.
- * This must be called after receiving an event with AInputQueue_get_event().
+ * This must be called after receiving an event with {@link AInputQueue_getEvent()}.
  */
 void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
 
 /**
- * Returns the AInputQueue* object associated with the supplied Java InputQueue
+ * Returns the {@link AInputQueue}* object associated with the supplied Java InputQueue
  * object. The returned native object holds a weak reference to the Java object,
  * and is only valid as long as the Java object has not yet been disposed. You
  * should ensure that there is a strong reference to the Java object and that it
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 214559d..3357660 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -776,7 +776,39 @@
     AKEYCODE_THUMBS_DOWN = 287,
     /** Used to switch current account that is consuming content.
      * May be consumed by system to switch current viewer profile. */
-    AKEYCODE_PROFILE_SWITCH = 288
+    AKEYCODE_PROFILE_SWITCH = 288,
+    /** Video Application key #1. */
+    AKEYCODE_VIDEO_APP_1 = 289,
+    /** Video Application key #2. */
+    AKEYCODE_VIDEO_APP_2 = 290,
+    /** Video Application key #3. */
+    AKEYCODE_VIDEO_APP_3 = 291,
+    /** Video Application key #4. */
+    AKEYCODE_VIDEO_APP_4 = 292,
+    /** Video Application key #5. */
+    AKEYCODE_VIDEO_APP_5 = 293,
+    /** Video Application key #6. */
+    AKEYCODE_VIDEO_APP_6 = 294,
+    /** Video Application key #7. */
+    AKEYCODE_VIDEO_APP_7 = 295,
+    /** Video Application key #8. */
+    AKEYCODE_VIDEO_APP_8 = 296,
+    /** Featured Application key #1. */
+    AKEYCODE_FEATURED_APP_1 = 297,
+    /** Featured Application key #2. */
+    AKEYCODE_FEATURED_APP_2 = 298,
+    /** Featured Application key #3. */
+    AKEYCODE_FEATURED_APP_3 = 299,
+    /** Featured Application key #4. */
+    AKEYCODE_FEATURED_APP_4 = 300,
+    /** Demo Application key #1. */
+    AKEYCODE_DEMO_APP_1 = 301,
+    /** Demo Application key #2. */
+    AKEYCODE_DEMO_APP_2 = 302,
+    /** Demo Application key #3. */
+    AKEYCODE_DEMO_APP_3 = 303,
+    /** Demo Application key #4. */
+    AKEYCODE_DEMO_APP_4 = 304,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index eef69f4..ba81bc8 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -596,12 +596,15 @@
     float accuracy;
 } AHeadingEvent;
 
+// LINT.IfChange
 /**
  * Information that describes a sensor event, refer to
  * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
  * documentation.
+ *
+ * NOTE: changes to this struct has to be backward compatible and reflected in
+ * sensors_event_t
  */
-/* NOTE: changes to this struct has to be backward compatible */
 typedef struct ASensorEvent {
     int32_t version; /* sizeof(struct ASensorEvent) */
     int32_t sensor;  /** The sensor that generates this event */
@@ -646,6 +649,7 @@
     uint32_t flags;
     int32_t reserved1[3];
 } ASensorEvent;
+// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
 
 struct ASensorManager;
 /**
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9a36ecb..6223ef7 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -597,20 +597,20 @@
                                                __INTRODUCED_IN(31);
 
 /**
- * Sets the frame timeline to use in Surface Flinger.
+ * Sets the frame timeline to use in SurfaceFlinger.
  *
- * A frame timeline should be chosen based on what frame deadline the application
- * can meet when rendering the frame and the application's desired present time.
- * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding
- * expected present time.
+ * A frame timeline should be chosen based on the frame deadline the application
+ * can meet when rendering the frame and the application's desired presentation time.
+ * By setting a frame timeline, SurfaceFlinger tries to present the frame at the corresponding
+ * expected presentation time.
  *
  * To receive frame timelines, a callback must be posted to Choreographer using
- * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the
+ * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the
  * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
  *
- * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
- * the corresponding expected present time and deadline from the frame to be rendered. A stale or
- * invalid value will be ignored.
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target
+ * to the corresponding expected presentation time and deadline from the frame to be rendered. A
+ * stale or invalid value will be ignored.
  */
 void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
                                           AVsyncId vsyncId) __INTRODUCED_IN(33);
diff --git a/include/input/Input.h b/include/input/Input.h
index e7d68fc..1eda981 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -297,6 +297,8 @@
  */
 const char* motionClassificationToString(MotionClassification classification);
 
+const char* motionToolTypeToString(int32_t toolType);
+
 /**
  * Portion of FrameMetrics timeline of interest to input code.
  */
@@ -816,6 +818,8 @@
                                                const PointerCoords&);
     static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
                                                     const PointerCoords&);
+    // The rounding precision for transformed motion events.
+    static constexpr float ROUNDING_PRECISION = 0.001f;
 
 protected:
     int32_t mAction;
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 1da78aa..d36d28c 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -73,6 +73,7 @@
             int32_t* outKeyCode, uint32_t* outFlags) const;
     std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const;
     std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const;
+    std::vector<int32_t> findUsageCodesForKey(int32_t keyCode) const;
     std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const;
 
     std::optional<AxisInfo> mapAxis(int32_t scanCode) const;
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0a75278..55f730b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <map>
+#include <optional>
 #include <set>
 #include <string>
 
@@ -28,6 +29,15 @@
 }
 
 /**
+ * Convert an optional type to string.
+ */
+template <typename T>
+std::string toString(const std::optional<T>& optional,
+                     std::string (*toString)(const T&) = constToString) {
+    return optional ? toString(*optional) : "<not set>";
+}
+
+/**
  * Convert a set of integral types to string.
  */
 template <typename T>
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 5e539f2..1a9766d 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -72,6 +72,7 @@
         "//apex_available:platform",
         "com.android.media",
         "com.android.media.swcodec",
+        "com.android.neuralnetworks",
     ],
 
 }
diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h
index b36728e..d52861a 100644
--- a/libs/arect/include/android/rect.h
+++ b/libs/arect/include/android/rect.h
@@ -57,7 +57,7 @@
 } ARect;
 
 #ifdef __cplusplus
-};
+}
 #endif
 
 #endif // ANDROID_RECT_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 28369d6..deff76b 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -76,7 +76,6 @@
 
     srcs: [
         "Binder.cpp",
-        "BinderRecordReplay.cpp",
         "BpBinder.cpp",
         "Debug.cpp",
         "FdTrigger.cpp",
@@ -84,6 +83,7 @@
         "IResultReceiver.cpp",
         "Parcel.cpp",
         "ParcelFileDescriptor.cpp",
+        "RecordedTransaction.cpp",
         "RpcSession.cpp",
         "RpcServer.cpp",
         "RpcState.cpp",
@@ -144,10 +144,6 @@
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
     product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
-        },
-
         debuggable: {
             cflags: [
                 "-DBINDER_RPC_DEV_SERVERS",
@@ -191,22 +187,31 @@
         "google-*",
         "misc-*",
         "performance*",
+        "-performance-move-const-arg", // b/273486801
         "portability*",
     ],
 }
 
-cc_library_shared {
-    name: "libbinder_on_trusty_mock",
-    defaults: ["libbinder_common_defaults"],
+cc_library_headers {
+    name: "trusty_mock_headers",
+    host_supported: true,
 
-    srcs: [
-        // Trusty-specific files
-        "trusty/logging.cpp",
-        "trusty/OS.cpp",
-        "trusty/RpcServerTrusty.cpp",
-        "trusty/RpcTransportTipcTrusty.cpp",
-        "trusty/TrustyStatus.cpp",
-        "trusty/socket.cpp",
+    export_include_dirs: [
+        "trusty/include",
+        "trusty/include_mock",
+    ],
+
+    visibility: [
+        ":__subpackages__",
+    ],
+}
+
+cc_defaults {
+    name: "trusty_mock_defaults",
+    host_supported: true,
+
+    header_libs: [
+        "trusty_mock_headers",
     ],
 
     cflags: [
@@ -227,16 +232,29 @@
     ],
     rtti: false,
 
-    local_include_dirs: [
-        "trusty/include",
-        "trusty/include_mock",
-    ],
-
     visibility: [
         ":__subpackages__",
     ],
 }
 
+cc_library_shared {
+    name: "libbinder_on_trusty_mock",
+    defaults: [
+        "libbinder_common_defaults",
+        "trusty_mock_defaults",
+    ],
+
+    srcs: [
+        // Trusty-specific files
+        "trusty/logging.cpp",
+        "trusty/OS.cpp",
+        "trusty/RpcServerTrusty.cpp",
+        "trusty/RpcTransportTipcTrusty.cpp",
+        "trusty/TrustyStatus.cpp",
+        "trusty/socket.cpp",
+    ],
+}
+
 cc_defaults {
     name: "libbinder_kernel_defaults",
     srcs: [
@@ -329,6 +347,7 @@
 
 cc_library_static {
     name: "libbinder_rpc_no_kernel",
+    vendor_available: true,
     defaults: [
         "libbinder_common_defaults",
         "libbinder_android_defaults",
@@ -381,6 +400,7 @@
 cc_defaults {
     name: "libbinder_tls_defaults",
     defaults: ["libbinder_tls_shared_deps"],
+    vendor_available: true,
     host_supported: true,
 
     header_libs: [
@@ -407,7 +427,7 @@
     defaults: ["libbinder_tls_defaults"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libbinder_trusty",
     vendor: true,
     srcs: [
@@ -491,6 +511,10 @@
             enabled: false,
         },
     },
+    visibility: [
+        ":__subpackages__",
+        "//system/tools/aidl:__subpackages__",
+    ],
 }
 
 // TODO(b/184872979): remove once the Rust API is created.
@@ -501,7 +525,6 @@
         "libbase",
         "libbinder",
         "libbinder_ndk",
-        "libcutils_sockets",
         "liblog",
         "libutils",
     ],
@@ -516,7 +539,11 @@
     // Do not expand the visibility.
     visibility: [
         ":__subpackages__",
-        "//packages/modules/Virtualization:__subpackages__",
+        "//packages/modules/Virtualization/javalib/jni",
+        "//packages/modules/Virtualization/vm_payload",
+        "//packages/modules/Virtualization/demo_native",
+        "//device/google/cuttlefish/shared/minidroid:__subpackages__",
+        "//system/software_defined_vehicle:__subpackages__",
     ],
 }
 
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 5e725a9..0f4a6ca 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -21,13 +21,13 @@
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <binder/BinderRecordReplay.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
 #include <binder/Parcel.h>
+#include <binder/RecordedTransaction.h>
 #include <binder/RpcServer.h>
 #include <cutils/compiler.h>
 #include <private/android_filesystem_config.h>
@@ -58,15 +58,15 @@
 
 // global b/c b/230079120 - consistent symbol table
 #ifdef BINDER_RPC_DEV_SERVERS
-bool kEnableRpcDevServers = true;
+constexpr bool kEnableRpcDevServers = true;
 #else
-bool kEnableRpcDevServers = false;
+constexpr bool kEnableRpcDevServers = false;
 #endif
 
 #ifdef BINDER_ENABLE_RECORDING
-bool kEnableRecording = true;
+constexpr bool kEnableRecording = true;
 #else
-bool kEnableRecording = false;
+constexpr bool kEnableRecording = false;
 #endif
 
 // Log any reply transactions for which the data exceeds this size
@@ -407,11 +407,11 @@
         AutoMutex lock(e->mLock);
         if (mRecordingOn) {
             Parcel emptyReply;
-            auto transaction =
-                    android::binder::debug::RecordedTransaction::fromDetails(code, flags, data,
-                                                                             reply ? *reply
-                                                                                   : emptyReply,
-                                                                             err);
+            timespec ts;
+            timespec_get(&ts, TIME_UTC);
+            auto transaction = android::binder::debug::RecordedTransaction::
+                    fromDetails(getInterfaceDescriptor(), code, flags, ts, data,
+                                reply ? *reply : emptyReply, err);
             if (transaction) {
                 if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) {
                     LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err;
diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp
deleted file mode 100644
index 90c02a8..0000000
--- a/libs/binder/BinderRecordReplay.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2022, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <binder/BinderRecordReplay.h>
-#include <algorithm>
-
-using android::Parcel;
-using android::base::unique_fd;
-using android::binder::debug::RecordedTransaction;
-
-#define PADDING8(s) ((8 - (s) % 8) % 8)
-
-static_assert(PADDING8(0) == 0);
-static_assert(PADDING8(1) == 7);
-static_assert(PADDING8(7) == 1);
-static_assert(PADDING8(8) == 0);
-
-// Transactions are sequentially recorded to the file descriptor in the following format:
-//
-// RecordedTransaction.TransactionHeader  (32 bytes)
-// Sent Parcel data                       (getDataSize() bytes)
-// padding                                (enough bytes to align the reply Parcel data to 8 bytes)
-// Reply Parcel data                      (getReplySize() bytes)
-// padding                                (enough bytes to align the next header to 8 bytes)
-// [repeats with next transaction]
-//
-// Warning: This format is non-stable
-
-RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
-    mHeader = {t.getCode(),      t.getFlags(),          t.getDataSize(),
-               t.getReplySize(), t.getReturnedStatus(), t.getVersion()};
-    mSent.setData(t.getDataParcel().data(), t.getDataSize());
-    mReply.setData(t.getReplyParcel().data(), t.getReplySize());
-}
-
-std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags,
-                                                                    const Parcel& dataParcel,
-                                                                    const Parcel& replyParcel,
-                                                                    status_t err) {
-    RecordedTransaction t;
-    t.mHeader = {code,
-                 flags,
-                 static_cast<uint64_t>(dataParcel.dataSize()),
-                 static_cast<uint64_t>(replyParcel.dataSize()),
-                 static_cast<int32_t>(err),
-                 dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)};
-
-    if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set sent parcel data.";
-        return std::nullopt;
-    }
-
-    if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set reply parcel data.";
-        return std::nullopt;
-    }
-
-    return std::optional<RecordedTransaction>(std::move(t));
-}
-
-std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
-    RecordedTransaction t;
-    if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) {
-        LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.getVersion() != 0) {
-        LOG(INFO) << "File corrupted: transaction version is not 0.";
-        return std::nullopt;
-    }
-
-    std::vector<uint8_t> bytes;
-    bytes.resize(t.getDataSize());
-    if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) {
-        LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set sent parcel data.";
-        return std::nullopt;
-    }
-
-    uint8_t padding[7];
-    if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) {
-        LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
-        LOG(INFO) << "File corrupted: padding isn't 0.";
-        return std::nullopt;
-    }
-
-    bytes.resize(t.getReplySize());
-    if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) {
-        LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) {
-        LOG(INFO) << "Failed to set reply parcel data.";
-        return std::nullopt;
-    }
-
-    if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) {
-        LOG(INFO) << "Failed to read parcel padding from fd " << fd.get();
-        return std::nullopt;
-    }
-    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
-        LOG(INFO) << "File corrupted: padding isn't 0.";
-        return std::nullopt;
-    }
-
-    return std::optional<RecordedTransaction>(std::move(t));
-}
-
-android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
-    if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) {
-        LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) {
-        LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    const uint8_t zeros[7] = {0};
-    if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) {
-        LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) {
-        LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) {
-        LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get();
-        return UNKNOWN_ERROR;
-    }
-    return NO_ERROR;
-}
-
-uint32_t RecordedTransaction::getCode() const {
-    return mHeader.code;
-}
-
-uint32_t RecordedTransaction::getFlags() const {
-    return mHeader.flags;
-}
-
-uint64_t RecordedTransaction::getDataSize() const {
-    return mHeader.dataSize;
-}
-
-uint64_t RecordedTransaction::getReplySize() const {
-    return mHeader.replySize;
-}
-
-int32_t RecordedTransaction::getReturnedStatus() const {
-    return mHeader.statusReturned;
-}
-
-uint32_t RecordedTransaction::getVersion() const {
-    return mHeader.version;
-}
-
-const Parcel& RecordedTransaction::getDataParcel() const {
-    return mSent;
-}
-
-const Parcel& RecordedTransaction::getReplyParcel() const {
-    return mReply;
-}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 54d2445..8d9955d 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -47,6 +47,8 @@
 binder_proxy_limit_callback BpBinder::sLimitCallback;
 bool BpBinder::sBinderProxyThrottleCreate = false;
 
+static StaticString16 kDescriptorUninit(u"");
+
 // Arbitrarily high value that probably distinguishes a bad behaving app
 uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500;
 // Another arbitrary value a binder count needs to drop below before another callback will be called
@@ -211,6 +213,7 @@
         mAlive(true),
         mObitsSent(false),
         mObituaries(nullptr),
+        mDescriptorCache(kDescriptorUninit),
         mTrackedUid(-1) {
     extendObjectLifetime(OBJECT_LIFETIME_WEAK);
 }
@@ -258,12 +261,12 @@
 
 bool BpBinder::isDescriptorCached() const {
     Mutex::Autolock _l(mLock);
-    return mDescriptorCache.size() ? true : false;
+    return mDescriptorCache.string() != kDescriptorUninit.string();
 }
 
 const String16& BpBinder::getInterfaceDescriptor() const
 {
-    if (isDescriptorCached() == false) {
+    if (!isDescriptorCached()) {
         sp<BpBinder> thiz = sp<BpBinder>::fromExisting(const_cast<BpBinder*>(this));
 
         Parcel data;
@@ -276,8 +279,7 @@
             Mutex::Autolock _l(mLock);
             // mDescriptorCache could have been assigned while the lock was
             // released.
-            if (mDescriptorCache.size() == 0)
-                mDescriptorCache = res;
+            if (mDescriptorCache.string() == kDescriptorUninit.string()) mDescriptorCache = res;
         }
     }
 
@@ -369,10 +371,7 @@
         if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
             Mutex::Autolock _l(mLock);
             ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d",
-                  data.dataSize(),
-                  mDescriptorCache.size() ? String8(mDescriptorCache).c_str()
-                                          : "<uncached descriptor>",
-                  code);
+                  data.dataSize(), String8(mDescriptorCache).c_str(), code);
         }
 
         if (status == DEAD_OBJECT) mAlive = 0;
@@ -389,7 +388,8 @@
 {
     if (isRpcBinder()) {
         if (rpcSession()->getMaxIncomingThreads() < 1) {
-            LOG_ALWAYS_FATAL("Cannot register a DeathRecipient without any incoming connections.");
+            ALOGE("Cannot register a DeathRecipient without any incoming threads. Need to set max "
+                  "incoming threads to a value greater than 0 before calling linkToDeath.");
             return INVALID_OPERATION;
         }
     } else if constexpr (!kEnableKernelIpc) {
@@ -647,7 +647,7 @@
     if(obits != nullptr) {
         if (!obits->isEmpty()) {
             ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
-                  mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
+                  String8(mDescriptorCache).c_str());
         }
 
         if (ipc) ipc->clearDeathNotification(binderHandle(), this);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 7770374..da58251 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -132,12 +132,21 @@
     } else {
         out << "\ttarget.ptr=" << btd->target.ptr;
     }
-    out << "\t (cookie " << btd->cookie << ")"
-        << "\n"
+    out << "\t (cookie " << btd->cookie << ")\n"
         << "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
-        << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
-        << "\n"
-        << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
+        << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)\n"
+        << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)\n";
+    return btd + 1;
+}
+
+static const void* printBinderTransactionDataSecCtx(std::ostream& out, const void* data) {
+    const binder_transaction_data_secctx* btd = (const binder_transaction_data_secctx*)data;
+
+    printBinderTransactionData(out, &btd->transaction_data);
+
+    char* secctx = (char*)btd->secctx;
+    out << "\tsecctx=" << secctx << "\n";
+
     return btd+1;
 }
 
@@ -156,6 +165,11 @@
     out << "\t" << kReturnStrings[cmdIndex];
 
     switch (code) {
+        case BR_TRANSACTION_SEC_CTX: {
+            out << ": ";
+            cmd = (const int32_t*)printBinderTransactionDataSecCtx(out, cmd);
+        } break;
+
         case BR_TRANSACTION:
         case BR_REPLY: {
             out << ": ";
@@ -1017,6 +1031,10 @@
             if (!reply && !acquireResult) goto finish;
             break;
 
+        case BR_TRANSACTION_PENDING_FROZEN:
+            ALOGW("Sending oneway calls to frozen process.");
+            goto finish;
+
         case BR_DEAD_REPLY:
             err = DEAD_OBJECT;
             goto finish;
@@ -1203,6 +1221,10 @@
         return NO_ERROR;
     }
 
+    ALOGE_IF(mProcess->mDriverFD >= 0,
+             "Driver returned error (%s). This is a bug in either libbinder or the driver. This "
+             "thread's connection to %s will no longer work.",
+             statusToString(err).c_str(), mProcess->mDriverName.c_str());
     return err;
 }
 
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a0c4334..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -444,7 +444,7 @@
     bool declared;
     if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
         !status.isOk()) {
-        ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+        ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
               status.toString8().c_str());
         return false;
     }
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f66993f..7644806 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -324,6 +324,10 @@
     return *registrarInstance;
 }
 
+LazyServiceRegistrar LazyServiceRegistrar::createExtraTestInstance() {
+    return LazyServiceRegistrar();
+}
+
 status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
                                                bool allowIsolated, int dumpFlags) {
     if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 8fe1d2b..3da06ba 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -78,7 +78,7 @@
         if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
             ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error  %s", name,
                   SEAL_FLAGS, strerror(errno));
-            munmap(mBase, mSize);
+            if (mNeedUnmap) munmap(mBase, mSize);
             mBase = nullptr;
             mSize = 0;
             close(fd);
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index f954e74..bb17683 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,6 +1,4 @@
 # Bug component: 32456
-ctate@google.com
-hackbod@google.com
 maco@google.com
 smoreland@google.com
 tkjos@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index ee081c4..9b685f9 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -375,6 +375,10 @@
     return (mDataSize > mDataPos ? mDataSize : mDataPos);
 }
 
+size_t Parcel::dataBufferSize() const {
+    return mDataSize;
+}
+
 size_t Parcel::dataAvail() const
 {
     size_t result = dataSize() - dataPosition();
@@ -943,7 +947,10 @@
         threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
         // vendor header
         int32_t header = readInt32();
-        if (header != kHeader) {
+
+        // fuzzers skip this check, because it is for protecting the underlying ABI, but
+        // we don't want it to reduce our coverage
+        if (header != kHeader && !mServiceFuzzing) {
             ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader,
                   header);
             return false;
@@ -962,10 +969,18 @@
             (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) {
         return true;
     } else {
-        ALOGW("**** enforceInterface() expected '%s' but read '%s'",
-              String8(interface, len).string(),
-              String8(parcel_interface, parcel_interface_len).string());
-        return false;
+        if (mServiceFuzzing) {
+            // ignore. Theoretically, this could cause a few false positives, because
+            // people could assume things about getInterfaceDescriptor if they pass
+            // this point, but it would be extremely fragile. It's more important that
+            // we fuzz with the above things read from the Parcel.
+            return true;
+        } else {
+            ALOGW("**** enforceInterface() expected '%s' but read '%s'",
+                  String8(interface, len).string(),
+                  String8(parcel_interface, parcel_interface_len).string());
+            return false;
+        }
     }
 }
 
@@ -973,6 +988,14 @@
     mEnforceNoDataAvail = enforceNoDataAvail;
 }
 
+void Parcel::setServiceFuzzing() {
+    mServiceFuzzing = true;
+}
+
+bool Parcel::isServiceFuzzing() const {
+    return mServiceFuzzing;
+}
+
 binder::Status Parcel::enforceNoDataAvail() const {
     if (!mEnforceNoDataAvail) {
         return binder::Status::ok();
@@ -1475,7 +1498,7 @@
 #ifdef BINDER_WITH_KERNEL_IPC
     flat_binder_object obj;
     obj.hdr.type = BINDER_TYPE_FD;
-    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+    obj.flags = 0;
     obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
     obj.handle = fd;
     obj.cookie = takeOwnership ? 1 : 0;
@@ -1718,7 +1741,9 @@
             do {
                 if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) {
                     // Requested info overlaps with an object
-                    ALOGE("Attempt to read from protected data in Parcel %p", this);
+                    if (!mServiceFuzzing) {
+                        ALOGE("Attempt to read from protected data in Parcel %p", this);
+                    }
                     return PERMISSION_DENIED;
                 }
                 nextObject++;
@@ -2088,7 +2113,11 @@
     size_t len;
     const char* str = readString8Inplace(&len);
     if (str) return String8(str, len);
-    ALOGE("Reading a NULL string not supported here.");
+
+    if (!mServiceFuzzing) {
+        ALOGE("Reading a NULL string not supported here.");
+    }
+
     return String8();
 }
 
@@ -2128,7 +2157,11 @@
     size_t len;
     const char16_t* str = readString16Inplace(&len);
     if (str) return String16(str, len);
-    ALOGE("Reading a NULL string not supported here.");
+
+    if (!mServiceFuzzing) {
+        ALOGE("Reading a NULL string not supported here.");
+    }
+
     return String16();
 }
 
@@ -2168,7 +2201,9 @@
 {
     status_t status = readNullableStrongBinder(val);
     if (status == OK && !val->get()) {
-        ALOGW("Expecting binder but got null!");
+        if (!mServiceFuzzing) {
+            ALOGW("Expecting binder but got null!");
+        }
         status = UNEXPECTED_NULL;
     }
     return status;
@@ -2233,9 +2268,11 @@
     if (const auto* rpcFields = maybeRpcFields()) {
         if (!std::binary_search(rpcFields->mObjectPositions.begin(),
                                 rpcFields->mObjectPositions.end(), mDataPos)) {
-            ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the "
-                  "object list",
-                  this, mDataPos);
+            if (!mServiceFuzzing) {
+                ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in "
+                      "the object list",
+                      this, mDataPos);
+            }
             return BAD_TYPE;
         }
 
@@ -2493,8 +2530,11 @@
                 return obj;
             }
         }
-        ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list",
-             this, DPOS);
+        if (!mServiceFuzzing) {
+            ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object "
+                  "list",
+                  this, DPOS);
+        }
     }
     return nullptr;
 }
@@ -3089,6 +3129,7 @@
     mDeallocZero = false;
     mOwner = nullptr;
     mEnforceNoDataAvail = true;
+    mServiceFuzzing = false;
 }
 
 void Parcel::scanForFds() const {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..02b0447 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -100,14 +100,11 @@
     LOG_ALWAYS_FATAL_IF(forked, "libbinder ProcessState can not be used after fork");
 }
 
-sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
-{
-#ifdef BINDER_IPC_32BIT
-    LOG_ALWAYS_FATAL("32-bit binder IPC is not supported for new devices starting in Android P. If "
-                     "you do need to use this mode, please see b/232423610 or file an issue with "
-                     "AOSP upstream as otherwise this will be removed soon.");
-#endif
+bool ProcessState::isVndservicemanagerEnabled() {
+    return access("/vendor/bin/vndservicemanager", R_OK) == 0;
+}
 
+sp<ProcessState> ProcessState::init(const char* driver, bool requireDefault) {
     if (driver == nullptr) {
         std::lock_guard<std::mutex> l(gProcessMutex);
         if (gProcess) {
@@ -123,6 +120,11 @@
             driver = "/dev/binder";
         }
 
+        if (0 == strcmp(driver, "/dev/vndbinder") && !isVndservicemanagerEnabled()) {
+            ALOGE("vndservicemanager is not started on this device, you can save resources/threads "
+                  "by not initializing ProcessState with /dev/vndbinder.");
+        }
+
         // we must install these before instantiating the gProcess object,
         // otherwise this would race with creating it, and there could be the
         // possibility of an invalid gProcess object forked by another thread
@@ -190,6 +192,7 @@
     AutoMutex _l(mLock);
     if (!mThreadPoolStarted) {
         if (mMaxThreads == 0) {
+            // see also getThreadPoolMaxTotalThreadCount
             ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
                   "*startThreadPool when zero threads are requested.");
         }
@@ -405,6 +408,11 @@
         mKernelStartedThreads++;
         pthread_mutex_unlock(&mThreadCountLock);
     }
+    // TODO: if startThreadPool is called on another thread after the process
+    // starts up, the kernel might think that it already requested those
+    // binder threads, and additional won't be started. This is likely to
+    // cause deadlocks, and it will also cause getThreadPoolMaxTotalThreadCount
+    // to return too high of a value.
 }
 
 status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
@@ -424,12 +432,32 @@
     pthread_mutex_lock(&mThreadCountLock);
     base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
 
-    // may actually be one more than this, if join is called
     if (mThreadPoolStarted) {
-        return mCurrentThreads < mKernelStartedThreads
-                ? mMaxThreads
-                : mMaxThreads + mCurrentThreads - mKernelStartedThreads;
+        LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1,
+                            "too many kernel-started threads: %zu > %zu + 1", mKernelStartedThreads,
+                            mMaxThreads);
+
+        // calling startThreadPool starts a thread
+        size_t threads = 1;
+
+        // the kernel is configured to start up to mMaxThreads more threads
+        threads += mMaxThreads;
+
+        // Users may call IPCThreadState::joinThreadPool directly. We don't
+        // currently have a way to count this directly (it could be added by
+        // adding a separate private joinKernelThread method in IPCThreadState).
+        // So, if we are in a race between the kernel thread variable being
+        // incremented in this file and mCurrentThreads being incremented
+        // in IPCThreadState, temporarily forget about the extra join threads.
+        // This is okay, because most callers of this method only care about
+        // having 0, 1, or more threads.
+        if (mCurrentThreads > mKernelStartedThreads) {
+            threads += mCurrentThreads - mKernelStartedThreads;
+        }
+
+        return threads;
     }
+
     // must not be initialized or maybe has poll thread setup, we
     // currently don't track this in libbinder
     LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0,
@@ -439,6 +467,10 @@
     return mCurrentThreads;
 }
 
+bool ProcessState::isThreadPoolStarted() const {
+    return mThreadPoolStarted;
+}
+
 #define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
 bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
     static const char* const names[] = {
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
new file mode 100644
index 0000000..44a9e3b
--- /dev/null
+++ b/libs/binder/RecordedTransaction.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/unique_fd.h>
+#include <binder/RecordedTransaction.h>
+#include <sys/mman.h>
+#include <algorithm>
+
+using android::Parcel;
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+#define PADDING8(s) ((8 - (s) % 8) % 8)
+
+static_assert(PADDING8(0) == 0);
+static_assert(PADDING8(1) == 7);
+static_assert(PADDING8(7) == 1);
+static_assert(PADDING8(8) == 0);
+
+// Transactions are sequentially recorded to a file descriptor.
+//
+// An individual RecordedTransaction is written with the following format:
+//
+// WARNING: Though the following format is designed to be stable and
+// extensible, it is under active development and should be considered
+// unstable until this warning is removed.
+//
+// A RecordedTransaction is written to a file as a sequence of Chunks.
+//
+// A Chunk consists of a ChunkDescriptor, Data, Padding, and a Checksum.
+//
+// The ChunkDescriptor identifies the type of Data in the chunk, and the size
+// of the Data.
+//
+// The Data may be any uint32 number of bytes in length in [0-0xfffffff0].
+//
+// Padding is between [0-7] zero-bytes after the Data such that the Chunk ends
+// on an 8-byte boundary. The ChunkDescriptor's dataSize does not include the
+// size of Padding.
+//
+// The checksum is a 64-bit wide XOR of all previous data from the start of the
+// ChunkDescriptor to the end of Padding.
+//
+// ┌───────────────────────────┐
+// │Chunk                      │
+// │┌────────────────────────┐ │
+// ││ChunkDescriptor         │ │
+// ││┌───────────┬──────────┐│ │
+// │││chunkType  │dataSize  ├┼─┼─┐
+// │││uint32_t   │uint32_t  ││ │ │
+// ││└───────────┴──────────┘│ │ │
+// │└────────────────────────┘ │ │
+// │┌─────────────────────────┐│ │
+// ││Data                     ││ │
+// ││bytes * dataSize         │◀─┘
+// ││   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤│
+// ││           Padding       ││
+// │└───┴─────────────────────┘│
+// │┌─────────────────────────┐│
+// ││checksum                 ││
+// ││uint64_t                 ││
+// │└─────────────────────────┘│
+// └───────────────────────────┘
+//
+// A RecordedTransaction is written as a Header Chunk with fields about the
+// transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk.
+// ┌──────────────────────┐
+// │     Header Chunk     │
+// ├──────────────────────┤
+// │  Sent Parcel Chunk   │
+// ├──────────────────────┤
+// │  Reply Parcel Chunk  │
+// ├──────────────────────┤
+// ║      End Chunk       ║
+// ╚══════════════════════╝
+//
+// On reading a RecordedTransaction, an unrecognized chunk is checksummed
+// then skipped according to size information in the ChunkDescriptor. Chunks
+// are read and either assimilated or skipped until an End Chunk is
+// encountered. This has three notable implications:
+//
+// 1. Older and newer implementations should be able to read one another's
+//    Transactions, though there will be loss of information.
+// 2. With the exception of the End Chunk, Chunks can appear in any order
+//    and even repeat, though this is not recommended.
+// 3. If any Chunk is repeated, old values will be overwritten by versions
+//    encountered later in the file.
+//
+// No effort is made to ensure the expected chunks are present. A single
+// End Chunk may therefore produce an empty, meaningless RecordedTransaction.
+
+RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
+    mData = t.mData;
+    mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
+    mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
+}
+
+std::optional<RecordedTransaction> RecordedTransaction::fromDetails(
+        const String16& interfaceName, uint32_t code, uint32_t flags, timespec timestamp,
+        const Parcel& dataParcel, const Parcel& replyParcel, status_t err) {
+    RecordedTransaction t;
+    t.mData.mHeader = {code,
+                       flags,
+                       static_cast<int32_t>(err),
+                       dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0),
+                       static_cast<int64_t>(timestamp.tv_sec),
+                       static_cast<int32_t>(timestamp.tv_nsec),
+                       0};
+
+    t.mData.mInterfaceName = std::string(String8(interfaceName).string());
+    if (interfaceName.size() != t.mData.mInterfaceName.size()) {
+        LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
+                      "utf-8.";
+        return std::nullopt;
+    }
+
+    if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
+        LOG(ERROR) << "Failed to set sent parcel data.";
+        return std::nullopt;
+    }
+
+    if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
+        LOG(ERROR) << "Failed to set reply parcel data.";
+        return std::nullopt;
+    }
+
+    return std::optional<RecordedTransaction>(std::move(t));
+}
+
+enum {
+    HEADER_CHUNK = 1,
+    DATA_PARCEL_CHUNK = 2,
+    REPLY_PARCEL_CHUNK = 3,
+    INTERFACE_NAME_CHUNK = 4,
+    END_CHUNK = 0x00ffffff,
+};
+
+struct ChunkDescriptor {
+    uint32_t chunkType = 0;
+    uint32_t dataSize = 0;
+};
+static_assert(sizeof(ChunkDescriptor) % 8 == 0);
+
+constexpr uint32_t kMaxChunkDataSize = 0xfffffff0;
+typedef uint64_t transaction_checksum_t;
+
+std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
+    RecordedTransaction t;
+    ChunkDescriptor chunk;
+    const long pageSize = sysconf(_SC_PAGE_SIZE);
+    struct stat fileStat;
+    if (fstat(fd.get(), &fileStat) != 0) {
+        LOG(ERROR) << "Unable to get file information";
+        return std::nullopt;
+    }
+
+    off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+    if (fdCurrentPosition == -1) {
+        LOG(ERROR) << "Invalid offset in file descriptor.";
+        return std::nullopt;
+    }
+    do {
+        if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
+            LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+            return std::nullopt;
+        }
+
+        if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
+            LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
+                       << strerror(errno);
+            return std::nullopt;
+        }
+        transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);
+
+        fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+        if (fdCurrentPosition == -1) {
+            LOG(ERROR) << "Invalid offset in file descriptor.";
+            return std::nullopt;
+        }
+        off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
+        off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
+
+        if (chunk.dataSize > kMaxChunkDataSize) {
+            LOG(ERROR) << "Chunk data exceeds maximum size.";
+            return std::nullopt;
+        }
+
+        size_t chunkPayloadSize =
+                chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
+
+        if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
+            LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+            return std::nullopt;
+        }
+
+        if (PADDING8(chunkPayloadSize) != 0) {
+            LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+            return std::nullopt;
+        }
+
+        size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
+        void* mappedMemory =
+                mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
+        auto mmap_guard = android::base::make_scope_guard(
+                [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
+
+        transaction_checksum_t* payloadMap =
+                reinterpret_cast<transaction_checksum_t*>(mappedMemory);
+        payloadMap += mmapPayloadStartOffset /
+                sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
+                                                // page-alignment
+        if (payloadMap == MAP_FAILED) {
+            LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
+                       << strerror(errno);
+            return std::nullopt;
+        }
+        for (size_t checksumIndex = 0;
+             checksumIndex < chunkPayloadSize / sizeof(transaction_checksum_t); checksumIndex++) {
+            checksum ^= payloadMap[checksumIndex];
+        }
+        if (checksum != 0) {
+            LOG(ERROR) << "Checksum failed.";
+            return std::nullopt;
+        }
+
+        fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+        if (fdCurrentPosition == -1) {
+            LOG(ERROR) << "Invalid offset in file descriptor.";
+            return std::nullopt;
+        }
+
+        switch (chunk.chunkType) {
+            case HEADER_CHUNK: {
+                if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
+                    LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
+                               << sizeof(TransactionHeader) << ".";
+                    return std::nullopt;
+                }
+                t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
+                break;
+            }
+            case INTERFACE_NAME_CHUNK: {
+                t.mData.mInterfaceName =
+                        std::string(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
+                break;
+            }
+            case DATA_PARCEL_CHUNK: {
+                if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
+                                    chunk.dataSize) != android::NO_ERROR) {
+                    LOG(ERROR) << "Failed to set sent parcel data.";
+                    return std::nullopt;
+                }
+                break;
+            }
+            case REPLY_PARCEL_CHUNK: {
+                if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
+                                     chunk.dataSize) != android::NO_ERROR) {
+                    LOG(ERROR) << "Failed to set reply parcel data.";
+                    return std::nullopt;
+                }
+                break;
+            }
+            case END_CHUNK:
+                break;
+            default:
+                LOG(INFO) << "Unrecognized chunk.";
+                break;
+        }
+    } while (chunk.chunkType != END_CHUNK);
+
+    return std::optional<RecordedTransaction>(std::move(t));
+}
+
+android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
+                                                  size_t byteCount, const uint8_t* data) const {
+    if (byteCount > kMaxChunkDataSize) {
+        LOG(ERROR) << "Chunk data exceeds maximum size";
+        return BAD_VALUE;
+    }
+    ChunkDescriptor descriptor = {.chunkType = chunkType,
+                                  .dataSize = static_cast<uint32_t>(byteCount)};
+    // Prepare Chunk content as byte *
+    const std::byte* descriptorBytes = reinterpret_cast<const std::byte*>(&descriptor);
+    const std::byte* dataBytes = reinterpret_cast<const std::byte*>(data);
+
+    // Add Chunk to intermediate buffer, except checksum
+    std::vector<std::byte> buffer;
+    buffer.insert(buffer.end(), descriptorBytes, descriptorBytes + sizeof(ChunkDescriptor));
+    buffer.insert(buffer.end(), dataBytes, dataBytes + byteCount);
+    std::byte zero{0};
+    buffer.insert(buffer.end(), PADDING8(byteCount), zero);
+
+    // Calculate checksum from buffer
+    transaction_checksum_t* checksumData = reinterpret_cast<transaction_checksum_t*>(buffer.data());
+    transaction_checksum_t checksumValue = 0;
+    for (size_t idx = 0; idx < (buffer.size() / sizeof(transaction_checksum_t)); idx++) {
+        checksumValue ^= checksumData[idx];
+    }
+
+    // Write checksum to buffer
+    std::byte* checksumBytes = reinterpret_cast<std::byte*>(&checksumValue);
+    buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t));
+
+    // Write buffer to file
+    if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
+        LOG(ERROR) << "Failed to write chunk fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
+    if (NO_ERROR !=
+        writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
+                   reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) {
+        LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (NO_ERROR !=
+        writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
+                   reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
+        LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+
+    if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
+        LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
+        LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
+        LOG(ERROR) << "Failed to write end chunk to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+const std::string& RecordedTransaction::getInterfaceName() const {
+    return mData.mInterfaceName;
+}
+
+uint32_t RecordedTransaction::getCode() const {
+    return mData.mHeader.code;
+}
+
+uint32_t RecordedTransaction::getFlags() const {
+    return mData.mHeader.flags;
+}
+
+int32_t RecordedTransaction::getReturnedStatus() const {
+    return mData.mHeader.statusReturned;
+}
+
+timespec RecordedTransaction::getTimestamp() const {
+    time_t sec = mData.mHeader.timestampSeconds;
+    int32_t nsec = mData.mHeader.timestampNanoseconds;
+    return (timespec){.tv_sec = sec, .tv_nsec = nsec};
+}
+
+uint32_t RecordedTransaction::getVersion() const {
+    return mData.mHeader.version;
+}
+
+const Parcel& RecordedTransaction::getDataParcel() const {
+    return mSent;
+}
+
+const Parcel& RecordedTransaction::getReplyParcel() const {
+    return mReply;
+}
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 0820cd1..55fc16d 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -50,7 +50,8 @@
 
 RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
 RpcServer::~RpcServer() {
-    (void)shutdown();
+    RpcMutexUniqueLock _l(mLock);
+    LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Must call shutdown() before destructor");
 }
 
 sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
@@ -70,11 +71,8 @@
     return setupSocketServer(UnixSocketAddress(path));
 }
 
-status_t RpcServer::setupVsockServer(unsigned int port) {
-    // realizing value w/ this type at compile time to avoid ubsan abort
-    constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
-    return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) {
+    return setupSocketServer(VsockSocketAddress(bindCid, port));
 }
 
 status_t RpcServer::setupInetServer(const char* address, unsigned int port,
@@ -83,6 +81,7 @@
     auto aiStart = InetSocketAddress::getAddrInfo(address, port);
     if (aiStart == nullptr) return UNKNOWN_ERROR;
     for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        if (ai->ai_addr == nullptr) continue;
         InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port);
         if (status_t status = setupSocketServer(socketAddress); status != OK) {
             continue;
@@ -125,8 +124,13 @@
     return mMaxThreads;
 }
 
-void RpcServer::setProtocolVersion(uint32_t version) {
+bool RpcServer::setProtocolVersion(uint32_t version) {
+    if (!RpcState::validateProtocolVersion(version)) {
+        return false;
+    }
+
     mProtocolVersion = version;
+    return true;
 }
 
 void RpcServer::setSupportedFileDescriptorTransportModes(
@@ -150,13 +154,25 @@
     mRootObjectWeak = binder;
 }
 void RpcServer::setPerSessionRootObject(
-        std::function<sp<IBinder>(const void*, size_t)>&& makeObject) {
+        std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& makeObject) {
     RpcMutexLockGuard _l(mLock);
     mRootObject.clear();
     mRootObjectWeak.clear();
     mRootObjectFactory = std::move(makeObject);
 }
 
+void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) {
+    RpcMutexLockGuard _l(mLock);
+    LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+    mConnectionFilter = std::move(filter);
+}
+
+void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) {
+    RpcMutexLockGuard _l(mLock);
+    LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started");
+    mServerSocketModifier = std::move(modifier);
+}
+
 sp<IBinder> RpcServer::getRootObject() {
     RpcMutexLockGuard _l(mLock);
     bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -200,11 +216,15 @@
     iovec iov{&zero, sizeof(zero)};
     std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
 
-    if (receiveMessageFromSocket(server.mServer, &iov, 1, &fds) < 0) {
+    ssize_t num_bytes = receiveMessageFromSocket(server.mServer, &iov, 1, &fds);
+    if (num_bytes < 0) {
         int savedErrno = errno;
         ALOGE("Failed recvmsg: %s", strerror(savedErrno));
         return -savedErrno;
     }
+    if (num_bytes == 0) {
+        return DEAD_OBJECT;
+    }
     if (fds.size() != 1) {
         ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size());
         return -EINVAL;
@@ -239,16 +259,25 @@
         socklen_t addrLen = addr.size();
 
         RpcTransportFd clientSocket;
-        if (mAcceptFn(*this, &clientSocket) != OK) {
-            continue;
+        if ((status = mAcceptFn(*this, &clientSocket)) != OK) {
+            if (status == DEAD_OBJECT)
+                break;
+            else
+                continue;
         }
+
+        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+
         if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
                         &addrLen)) {
             ALOGE("Could not getpeername socket: %s", strerror(errno));
             continue;
         }
 
-        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+        if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
+            ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
+            continue;
+        }
 
         {
             RpcMutexLockGuard _l(mLock);
@@ -278,7 +307,8 @@
 bool RpcServer::shutdown() {
     RpcMutexUniqueLock _l(mLock);
     if (mShutdownTrigger == nullptr) {
-        LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)");
+        LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown, or not "
+                       "joined yet?)");
         return false;
     }
 
@@ -317,6 +347,8 @@
         mJoinThread.reset();
     }
 
+    mServer = RpcTransportFd();
+
     LOG_RPC_DETAIL("Finished waiting on shutdown.");
 
     mShutdownTrigger = nullptr;
@@ -483,7 +515,8 @@
             // if null, falls back to server root
             sp<IBinder> sessionSpecificRoot;
             if (server->mRootObjectFactory != nullptr) {
-                sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen);
+                sessionSpecificRoot =
+                        server->mRootObjectFactory(wp<RpcSession>(session), addr.data(), addrLen);
                 if (sessionSpecificRoot == nullptr) {
                     ALOGE("Warning: server returned null from root object factory");
                 }
@@ -535,9 +568,17 @@
             socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
     if (!socket_fd.ok()) {
         int savedErrno = errno;
-        ALOGE("Could not create socket: %s", strerror(savedErrno));
+        ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
         return -savedErrno;
     }
+
+    {
+        RpcMutexLockGuard _l(mLock);
+        if (mServerSocketModifier != nullptr) {
+            mServerSocketModifier(socket_fd);
+        }
+    }
+
     if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
         int savedErrno = errno;
         ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ce6ef2b..c3dee16 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -20,6 +20,7 @@
 
 #include <dlfcn.h>
 #include <inttypes.h>
+#include <netinet/tcp.h>
 #include <poll.h>
 #include <unistd.h>
 
@@ -90,24 +91,20 @@
     return mMaxIncomingThreads;
 }
 
-void RpcSession::setMaxOutgoingThreads(size_t threads) {
+void RpcSession::setMaxOutgoingConnections(size_t connections) {
     RpcMutexLockGuard _l(mMutex);
     LOG_ALWAYS_FATAL_IF(mStartedSetup,
                         "Must set max outgoing threads before setting up connections");
-    mMaxOutgoingThreads = threads;
+    mMaxOutgoingConnections = connections;
 }
 
 size_t RpcSession::getMaxOutgoingThreads() {
     RpcMutexLockGuard _l(mMutex);
-    return mMaxOutgoingThreads;
+    return mMaxOutgoingConnections;
 }
 
 bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
-    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
-        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
-        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
-              "is %u).",
-              version, RPC_WIRE_PROTOCOL_VERSION);
+    if (!RpcState::validateProtocolVersion(version)) {
         return false;
     }
 
@@ -558,11 +555,11 @@
         return status;
     }
 
-    size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
-    ALOGI_IF(outgoingThreads != numThreadsAvailable,
+    size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections);
+    ALOGI_IF(outgoingConnections != numThreadsAvailable,
              "Server hints client to start %zu outgoing threads, but client will only start %zu "
              "because it is preconfigured to start at most %zu outgoing threads.",
-             numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+             numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections);
 
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once - the other side should be responsible for setting
@@ -571,10 +568,10 @@
     // any requests at all.
 
     // we've already setup one client
-    LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
-                   "incoming threads",
-                   outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
-    for (size_t i = 0; i + 1 < outgoingThreads; i++) {
+    LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: "
+                   "%zu) and %zu incoming threads",
+                   outgoingConnections, numThreadsAvailable, mMaxIncomingThreads);
+    for (size_t i = 0; i + 1 < outgoingConnections; i++) {
         if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
     }
 
@@ -608,6 +605,18 @@
             return -savedErrno;
         }
 
+        if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+            int noDelay = 1;
+            int result =
+                    setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+            if (result < 0) {
+                int savedErrno = errno;
+                ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(),
+                      strerror(savedErrno));
+                return -savedErrno;
+            }
+        }
+
         RpcTransportFd transportFd(std::move(serverFd));
 
         if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
@@ -932,7 +941,8 @@
                   (session->server()
                            ? "This is a server session, so see RpcSession::setMaxIncomingThreads "
                              "for the corresponding client"
-                           : "This is a client session, so see RpcSession::setMaxOutgoingThreads "
+                           : "This is a client session, so see "
+                             "RpcSession::setMaxOutgoingConnections "
                              "for this client or RpcServer::setMaxThreads for the corresponding "
                              "server"));
             return WOULD_BLOCK;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b27f102..bac2808 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -34,6 +34,10 @@
 
 #include <inttypes.h>
 
+#ifdef __ANDROID__
+#include <cutils/properties.h>
+#endif
+
 namespace android {
 
 using base::StringPrintf;
@@ -59,6 +63,7 @@
         case RpcSession::FileDescriptorTransportMode::TRUSTY:
             return true;
     }
+    LOG_ALWAYS_FATAL("Invalid FileDescriptorTransportMode: %d", static_cast<int>(mode));
 }
 
 RpcState::RpcState() {}
@@ -262,8 +267,10 @@
 }
 
 void RpcState::clear() {
-    RpcMutexUniqueLock _l(mNodeMutex);
+    return clear(RpcMutexUniqueLock(mNodeMutex));
+}
 
+void RpcState::clear(RpcMutexUniqueLock nodeLock) {
     if (mTerminated) {
         LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(),
                             "New state should be impossible after terminating!");
@@ -292,7 +299,7 @@
     auto temp = std::move(mNodeForAddress);
     mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit
 
-    _l.unlock();
+    nodeLock.unlock();
     temp.clear(); // explicit
 }
 
@@ -396,6 +403,31 @@
     return OK;
 }
 
+bool RpcState::validateProtocolVersion(uint32_t version) {
+    if (version == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+#if defined(__ANDROID__)
+        char codename[PROPERTY_VALUE_MAX];
+        property_get("ro.build.version.codename", codename, "");
+        if (!strcmp(codename, "REL")) {
+            ALOGE("Cannot use experimental RPC binder protocol on a release branch.");
+            return false;
+        }
+#else
+        // don't restrict on other platforms, though experimental should
+        // only really be used for testing, we don't have a good way to see
+        // what is shipping outside of Android
+#endif
+    } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) {
+        ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
+              "version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+
+    return true;
+}
+
 status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                           const sp<RpcSession>& session, uint32_t* version) {
     RpcNewSessionResponse response;
@@ -557,13 +589,12 @@
             .parcelDataSize = static_cast<uint32_t>(data.dataSize()),
     };
 
-    constexpr size_t kWaitMaxUs = 1000000;
-    constexpr size_t kWaitLogUs = 10000;
-    size_t waitUs = 0;
-
     // Oneway calls have no sync point, so if many are sent before, whether this
     // is a twoway or oneway transaction, they may have filled up the socket.
     // So, make sure we drain them before polling
+    constexpr size_t kWaitMaxUs = 1000000;
+    constexpr size_t kWaitLogUs = 10000;
+    size_t waitUs = 0;
 
     iovec iovs[]{
             {&command, sizeof(RpcWireHeader)},
@@ -591,8 +622,9 @@
                 },
                 rpcFields->mFds.get());
         status != OK) {
-        // TODO(b/167966510): need to undo onBinderLeaving - we know the
-        // refcount isn't successfully transferred.
+        // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
+        // errors here, then we may need to undo the binder-sent counts for the transaction as
+        // well as for the binder objects in the Parcel
         return status;
     }
 
@@ -704,7 +736,7 @@
     };
 
     {
-        RpcMutexLockGuard _l(mNodeMutex);
+        RpcMutexUniqueLock _l(mNodeMutex);
         if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
         auto it = mNodeForAddress.find(addr);
         LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -720,8 +752,9 @@
         body.amount = it->second.timesRecd - target;
         it->second.timesRecd = target;
 
-        LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
+        LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(session, std::move(_l), it),
                             "Bad state. RpcState shouldn't own received binder");
+        // LOCK ALREADY RELEASED
     }
 
     RpcWireHeader cmd = {
@@ -925,7 +958,7 @@
                                           transactionData.size() -
                                                   offsetof(RpcWireTransaction, data)};
         Span<const uint32_t> objectTableSpan;
-        if (session->getProtocolVersion().value() >
+        if (session->getProtocolVersion().value() >=
             RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
             std::optional<Span<const uint8_t>> objectTableBytes =
                     parcelSpan.splitOff(transaction->parcelDataSize);
@@ -1036,8 +1069,8 @@
                 return DEAD_OBJECT;
             }
 
-            if (it->second.asyncTodo.size() == 0) return OK;
-            if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+            if (it->second.asyncTodo.size() != 0 &&
+                it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
                 LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
                                it->second.asyncNumber, addr);
 
@@ -1164,8 +1197,8 @@
                    it->second.timesSent);
 
     it->second.timesSent -= body.amount;
-    sp<IBinder> tempHold = tryEraseNode(it);
-    _l.unlock();
+    sp<IBinder> tempHold = tryEraseNode(session, std::move(_l), it);
+    // LOCK ALREADY RELEASED
     tempHold = nullptr; // destructor may make binder calls on this session
 
     return OK;
@@ -1229,7 +1262,10 @@
     return OK;
 }
 
-sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
+sp<IBinder> RpcState::tryEraseNode(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+                                   std::map<uint64_t, BinderNode>::iterator& it) {
+    bool shouldShutdown = false;
+
     sp<IBinder> ref;
 
     if (it->second.timesSent == 0) {
@@ -1239,9 +1275,27 @@
             LOG_ALWAYS_FATAL_IF(!it->second.asyncTodo.empty(),
                                 "Can't delete binder w/ pending async transactions");
             mNodeForAddress.erase(it);
+
+            if (mNodeForAddress.size() == 0) {
+                shouldShutdown = true;
+            }
         }
     }
 
+    // If we shutdown, prevent RpcState from being re-used. This prevents another
+    // thread from getting the root object again.
+    if (shouldShutdown) {
+        clear(std::move(nodeLock));
+    } else {
+        nodeLock.unlock(); // explicit
+    }
+    // LOCK IS RELEASED
+
+    if (shouldShutdown) {
+        ALOGI("RpcState has no binders left, so triggering shutdown...");
+        (void)session->shutdownAndWait(false);
+    }
+
     return ref;
 }
 
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index ac86585..1fe71a5 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -63,6 +63,8 @@
     RpcState();
     ~RpcState();
 
+    [[nodiscard]] static bool validateProtocolVersion(uint32_t version);
+
     [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                                   const sp<RpcSession>& session, uint32_t* version);
     [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
@@ -168,6 +170,7 @@
     void clear();
 
 private:
+    void clear(RpcMutexUniqueLock nodeLock);
     void dumpLocked();
 
     // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
@@ -268,11 +271,20 @@
         std::string toString() const;
     };
 
-    // checks if there is any reference left to a node and erases it. If erase
-    // happens, and there is a strong reference to the binder kept by
-    // binderNode, this returns that strong reference, so that it can be
-    // dropped after any locks are removed.
-    sp<IBinder> tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it);
+    // Checks if there is any reference left to a node and erases it. If this
+    // is the last node, shuts down the session.
+    //
+    // Node lock is passed here for convenience, so that we can release it
+    // and terminate the session, but we could leave it up to the caller
+    // by returning a continuation if we needed to erase multiple specific
+    // nodes. It may be tempting to allow the client to keep on holding the
+    // lock and instead just return whether or not we should shutdown, but
+    // this introduces the posssibility that another thread calls
+    // getRootBinder and thinks it is valid, rather than immediately getting
+    // an error.
+    sp<IBinder> tryEraseNode(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+                             std::map<uint64_t, BinderNode>::iterator& it);
+
     // true - success
     // false - session shutdown, halt
     [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index cd067bf..f3575cc 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -29,8 +29,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport with TLS disabled.
 class RpcTransportRaw : public RpcTransport {
 public:
@@ -96,8 +94,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newServerCtx() const {
     return std::make_unique<RpcTransportCtxRaw>();
 }
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 453279c..0c81d83 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -31,8 +31,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport for writing Trusty IPC clients in Android.
 class RpcTransportTipcAndroid : public RpcTransport {
 public:
@@ -54,7 +52,7 @@
             }
 
             LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno));
-            return -savedErrno;
+            return adjustStatus(-savedErrno);
         }
 
         if (pfd.revents & POLLNVAL) {
@@ -63,12 +61,14 @@
         if (pfd.revents & POLLERR) {
             return DEAD_OBJECT;
         }
+        if (pfd.revents & POLLIN) {
+            // Copied from FdTrigger.cpp: Even though POLLHUP may also be set,
+            // treat it as a success condition to ensure data is drained.
+            return OK;
+        }
         if (pfd.revents & POLLHUP) {
             return DEAD_OBJECT;
         }
-        if (pfd.revents & POLLIN) {
-            return OK;
-        }
 
         return WOULD_BLOCK;
     }
@@ -85,8 +85,10 @@
                                 "File descriptors are not supported on Trusty yet");
             return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0));
         };
-        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, "tipc_send",
-                                        POLLOUT, altPoll);
+
+        status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn,
+                                                   "tipc_send", POLLOUT, altPoll);
+        return adjustStatus(status);
     }
 
     status_t interruptableReadFully(
@@ -119,13 +121,26 @@
 
             return processSize;
         };
-        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read", POLLIN,
-                                        altPoll);
+
+        status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read",
+                                                   POLLIN, altPoll);
+        return adjustStatus(status);
     }
 
     bool isWaiting() override { return mSocket.isInPollingState(); }
 
 private:
+    status_t adjustStatus(status_t status) {
+        if (status == -ENOTCONN) {
+            // TIPC returns ENOTCONN on disconnect, but that's basically
+            // the same as DEAD_OBJECT and the latter is the common libbinder
+            // error code for dead connections
+            return DEAD_OBJECT;
+        }
+
+        return status;
+    }
+
     status_t fillReadBuffer() {
         if (mReadBufferPos < mReadBufferSize) {
             return OK;
@@ -165,7 +180,7 @@
                     continue;
                 } else {
                     LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno));
-                    return -savedErrno;
+                    return adjustStatus(-savedErrno);
                 }
             } else {
                 mReadBufferSize = static_cast<size_t>(processSize);
@@ -200,8 +215,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const {
     return std::make_unique<RpcTransportCtxTipcAndroid>();
 }
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 3e98ecc..785f6ce 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -275,6 +275,8 @@
     bssl::UniquePtr<SSL> mSsl;
 };
 
+} // namespace
+
 class RpcTransportTls : public RpcTransport {
 public:
     RpcTransportTls(RpcTransportFd socket, Ssl ssl)
@@ -411,7 +413,8 @@
 }
 
 // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|.
-bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) {
+static bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket,
+                                FdTrigger* fdTrigger) {
     bssl::UniquePtr<BIO> bio = newSocketBio(socket.fd);
     TEST_AND_RETURN(false, bio != nullptr);
     auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get());
@@ -540,8 +543,6 @@
     }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
     return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier,
                                                                          mAuth.get());
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 194254a..2b67f03 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -159,8 +159,8 @@
     LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
 
     auto rpcSession = RpcSession::make();
-    if (options.maxOutgoingThreads.has_value()) {
-        rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+    if (options.maxOutgoingConnections.has_value()) {
+        rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections);
     }
 
     if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 342e4a3..2b3ff44 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,9 +16,15 @@
       "name": "binderDriverInterfaceTest"
     },
     {
+      "name": "binderRecordReplayTest"
+    },
+    {
       "name": "binderHostDeviceTest"
     },
     {
+      "name": "binderParcelBenchmark"
+    },
+    {
       "name": "binderTextOutputTest"
     },
     {
@@ -52,9 +58,15 @@
       "name": "memunreachable_binder_test"
     },
     {
+      "name": "resolv_integration_test"
+    },
+    {
       "name": "libbinderthreadstateutils_test"
     },
     {
+      "name": "fuzz_service_test"
+    },
+    {
       "name": "CtsOsTestCases",
       "options": [
         {
@@ -76,6 +88,12 @@
     },
     {
       "name": "rustBinderSerializationTest"
+    },
+    {
+      "name": "libbinder_ndk_bindgen_test"
+    },
+    {
+      "name": "libbinder_rpc_unstable_bindgen_test"
     }
   ],
   "presubmit-large": [
@@ -96,5 +114,24 @@
     {
       "name": "binderLibTest"
     }
+  ],
+  "kernel-presubmit": [
+    {
+      "name": "binderDriverInterfaceTest"
+    },
+    {
+      "name": "binderLibTest"
+    },
+    {
+      "name": "binderSafeInterfaceTest"
+    },
+    {
+      "name": "memunreachable_binder_test"
+    }
+  ],
+  "imports": [
+    {
+      "path": "packages/modules/Virtualization"
+    }
   ]
 }
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
 #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
 #endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
 
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
 #endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 08dbd13..744da0f 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -105,12 +105,6 @@
     [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
                                              const sp<IBinder>& keepAliveBinder);
 
-    // Start recording transactions to the unique_fd in data.
-    // See BinderRecordReplay.h for more details.
-    [[nodiscard]] status_t startRecordingTransactions(const Parcel& data);
-    // Stop the current recording.
-    [[nodiscard]] status_t stopRecordingTransactions();
-
 protected:
     virtual             ~BBinder();
 
@@ -131,6 +125,8 @@
 
     [[nodiscard]] status_t setRpcClientDebug(const Parcel& data);
     void removeRpcServerLink(const sp<RpcServerLink>& link);
+    [[nodiscard]] status_t startRecordingTransactions(const Parcel& data);
+    [[nodiscard]] status_t stopRecordingTransactions();
 
     std::atomic<Extras*> mExtras;
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 57e103d..5496d61 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -91,7 +91,7 @@
     std::optional<int32_t> getDebugBinderHandle() const;
 
     // Start recording transactions to the unique_fd.
-    // See BinderRecordReplay.h for more details.
+    // See RecordedTransaction.h for more details.
     status_t startRecordingBinder(const android::base::unique_fd& fd);
     // Stop the current recording.
     status_t stopRecordingBinder();
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 65b77c6..9347ce4 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -141,11 +141,18 @@
             void                restoreCallingIdentity(int64_t token);
             bool hasExplicitIdentity();
 
+            // For main functions - dangerous for libraries to use
             status_t            setupPolling(int* fd);
             status_t            handlePolledCommands();
             void                flushCommands();
             bool                flushIfNeeded();
 
+            // Adds the current thread into the binder threadpool.
+            //
+            // This is in addition to any threads which are started
+            // with startThreadPool. Libraries should not call this
+            // function, as they may be loaded into processes which
+            // try to configure the threadpool differently.
             void                joinThreadPool(bool isMain = true);
             
             // Stop the local process.
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 79e771f..55167a7 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -67,7 +67,8 @@
      * a system property, or in the case of services in the VINTF manifest, it can be checked
      * with isDeclared).
      */
-    virtual sp<IBinder>         getService( const String16& name) const = 0;
+    [[deprecated("this polls for 5s, prefer waitForService or checkService")]]
+    virtual sp<IBinder> getService(const String16& name) const = 0;
 
     /**
      * Retrieve an existing service, non-blocking.
@@ -197,7 +198,10 @@
 {
     const sp<IServiceManager> sm = defaultServiceManager();
     if (sm != nullptr) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         *outService = interface_cast<INTERFACE>(sm->getService(name));
+#pragma clang diagnostic pop // getService deprecation
         if ((*outService) != nullptr) return NO_ERROR;
     }
     return NAME_NOT_FOUND;
@@ -220,12 +224,12 @@
 //    }
 // Resources are cleaned up when the object is destroyed.
 //
-// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
-// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
-// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
-// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are
+// instantiated, depending on how many the service on the device is configured with.
+// Hence, only |maxOutgoingConnections| calls can be made simultaneously.
+// See also RpcSession::setMaxOutgoingConnections.
 struct RpcDelegateServiceManagerOptions {
-    std::optional<size_t> maxOutgoingThreads;
+    std::optional<size_t> maxOutgoingConnections;
 };
 sp<IServiceManager> createRpcDelegateServiceManager(
         const RpcDelegateServiceManagerOptions& options);
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 2e22b84..bda3d19 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -93,7 +93,17 @@
       */
      void reRegister();
 
-   private:
+     /**
+      * Create a second instance of lazy service registrar.
+      *
+      * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar
+      * should be single-instanced, so that the service will only
+      * shut down when all services are unused. A separate instance
+      * is only used to test race conditions.
+      */
+     static LazyServiceRegistrar createExtraTestInstance();
+
+ private:
      std::shared_ptr<internal::ClientCounterCallback> mClientCC;
      LazyServiceRegistrar();
 };
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index f730acb..4e231ed 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -34,13 +34,8 @@
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
 
-#ifdef BINDER_IPC_32BIT
-//NOLINTNEXTLINE(google-runtime-int) b/173188702
-typedef unsigned int binder_size_t;
-#else
 //NOLINTNEXTLINE(google-runtime-int) b/173188702
 typedef unsigned long long binder_size_t;
-#endif
 
 struct flat_binder_object;
 
@@ -75,6 +70,7 @@
     size_t              dataAvail() const;
     size_t              dataPosition() const;
     size_t              dataCapacity() const;
+    size_t dataBufferSize() const;
 
     status_t            setDataSize(size_t size);
 
@@ -153,6 +149,11 @@
     // This Api is used by fuzzers to skip dataAvail checks.
     void setEnforceNoDataAvail(bool enforceNoDataAvail);
 
+    // When fuzzing, we want to remove certain ABI checks that cause significant
+    // lost coverage, and we also want to avoid logs that cost too much to write.
+    void setServiceFuzzing();
+    bool isServiceFuzzing() const;
+
     void                freeData();
 
     size_t              objectsCount() const;
@@ -265,7 +266,8 @@
     status_t            writeEnumVector(const std::optional<std::vector<T>>& val)
             { return writeData(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) __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val)
             { return writeData(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>
@@ -275,17 +277,20 @@
     status_t            writeEnumVector(const std::optional<std::vector<T>>& val)
             { return writeData(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) __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val)
             { return writeData(val); }
 
     template<typename T>
     status_t            writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val)
             { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val)
             { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val)
             { return writeData(val); }
     template<typename T>
     status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val)
@@ -421,7 +426,8 @@
     status_t            readEnumVector(std::vector<T>* val) const
             { return readData(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            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const
             { return readData(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            readEnumVector(std::optional<std::vector<T>>* val) const
@@ -431,7 +437,8 @@
     status_t            readEnumVector(std::vector<T>* val) const
             { return readData(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            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+    [[deprecated("use std::optional version instead")]] //
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const
             { return readData(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            readEnumVector(std::optional<std::vector<T>>* val) const
@@ -442,8 +449,9 @@
                             std::optional<std::vector<std::optional<T>>>* val) const
             { return readData(val); }
     template<typename T>
+    [[deprecated("use std::optional version instead")]] //
     status_t            readParcelableVector(
-                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")))
+                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const
             { return readData(val); }
     template<typename T>
     status_t            readParcelableVector(std::vector<T>* val) const
@@ -1334,6 +1342,7 @@
 
     // Set this to false to skip dataAvail checks.
     bool mEnforceNoDataAvail;
+    bool mServiceFuzzing;
 
     release_func        mOwner;
 
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 9896fd7..08d8e43 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -42,6 +42,7 @@
     android::status_t writeToParcel(android::Parcel* parcel) const override;
     android::status_t readFromParcel(const android::Parcel* parcel) override;
 
+    inline std::string toString() const { return "ParcelFileDescriptor:" + std::to_string(get()); }
     inline bool operator!=(const ParcelFileDescriptor& rhs) const {
         return mFd.get() != rhs.mFd.get();
     }
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 88790a8..40fd30a 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -111,6 +111,11 @@
 
     Stability getStability() const override { return mStability; }
 
+    inline std::string toString() const {
+        return "ParcelableHolder:" +
+                (mParcelableName ? std::string(String8(mParcelableName.value()).c_str())
+                                 : "<parceled>");
+    }
     inline bool operator!=(const ParcelableHolder& rhs) const {
         return this != &rhs;
     }
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..9dc370b 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -38,6 +38,8 @@
     static sp<ProcessState> self();
     static sp<ProcessState> selfOrNull();
 
+    static bool isVndservicemanagerEnabled();
+
     /* initWithDriver() can be used to configure libbinder to use
      * a different binder driver dev node. It must be called *before*
      * any call to ProcessState::self(). The default is /dev/vndbinder
@@ -50,17 +52,42 @@
 
     sp<IBinder> getContextObject(const sp<IBinder>& caller);
 
+    // This should be called before startThreadPool at the beginning
+    // of a program, and libraries should never call it because programs
+    // should configure their own threadpools. The threadpool size can
+    // never be decreased.
+    //
+    // The 'maxThreads' value refers to the total number of threads
+    // that will be started by the kernel. This is in addition to any
+    // threads started by 'startThreadPool' or 'joinRpcThreadpool'.
+    status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+
+    // Libraries should not call this, as processes should configure
+    // threadpools themselves. Should be called in the main function
+    // directly before any code executes or joins the threadpool.
+    //
+    // Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+    // PLUS those manually requested in joinThreadPool.
+    //
+    // For instance, if setThreadPoolMaxCount(3) is called and
+    // startThreadpPool (+1 thread) and joinThreadPool (+1 thread)
+    // are all called, then up to 5 threads can be started.
     void startThreadPool();
 
-    bool becomeContextManager();
+    [[nodiscard]] bool becomeContextManager();
 
     sp<IBinder> getStrongProxyForHandle(int32_t handle);
     void expungeHandle(int32_t handle, IBinder* binder);
 
+    // TODO: deprecate.
     void spawnPooledThread(bool isMain);
 
-    status_t setThreadPoolMaxThreadCount(size_t maxThreads);
     status_t enableOnewaySpamDetection(bool enable);
+
+    // Set the name of the current thread to look like a threadpool
+    // thread. Typically this is called before joinThreadPool.
+    //
+    // TODO: remove this API, and automatically set it intelligently.
     void giveThreadPoolName();
 
     String8 getDriverName();
@@ -94,6 +121,11 @@
      */
     size_t getThreadPoolMaxTotalThreadCount() const;
 
+    /**
+     * Check to see if the thread pool has started.
+     */
+    bool isThreadPoolStarted() const;
+
     enum class DriverFeature {
         ONEWAY_SPAM_DETECTION,
         EXTENDED_ERROR,
diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/RecordedTransaction.h
similarity index 72%
rename from libs/binder/include/binder/BinderRecordReplay.h
rename to libs/binder/include/binder/RecordedTransaction.h
index 25ed5e5..eb765fe 100644
--- a/libs/binder/include/binder/BinderRecordReplay.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -26,25 +26,26 @@
 
 // Warning: Transactions are sequentially recorded to the file descriptor in a
 // non-stable format. A detailed description of the recording format can be found in
-// BinderRecordReplay.cpp.
+// RecordedTransaction.cpp.
 
 class RecordedTransaction {
 public:
     // Filled with the first transaction from fd.
     static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd);
     // Filled with the arguments.
-    static std::optional<RecordedTransaction> fromDetails(uint32_t code, uint32_t flags,
-                                                          const Parcel& data, const Parcel& reply,
-                                                          status_t err);
+    static std::optional<RecordedTransaction> fromDetails(const String16& interfaceName,
+                                                          uint32_t code, uint32_t flags,
+                                                          timespec timestamp, const Parcel& data,
+                                                          const Parcel& reply, status_t err);
     RecordedTransaction(RecordedTransaction&& t) noexcept;
 
     [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
 
+    const std::string& getInterfaceName() const;
     uint32_t getCode() const;
     uint32_t getFlags() const;
-    uint64_t getDataSize() const;
-    uint64_t getReplySize() const;
     int32_t getReturnedStatus() const;
+    timespec getTimestamp() const;
     uint32_t getVersion() const;
     const Parcel& getDataParcel() const;
     const Parcel& getReplyParcel() const;
@@ -52,27 +53,31 @@
 private:
     RecordedTransaction() = default;
 
+    android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType,
+                                 size_t byteCount, const uint8_t* data) const;
+
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wpadded"
     struct TransactionHeader {
         uint32_t code = 0;
         uint32_t flags = 0;
-        uint64_t dataSize = 0;
-        uint64_t replySize = 0;
         int32_t statusReturned = 0;
         uint32_t version = 0; // !0 iff Rpc
+        int64_t timestampSeconds = 0;
+        int32_t timestampNanoseconds = 0;
+        int32_t reserved = 0;
     };
 #pragma clang diagnostic pop
     static_assert(sizeof(TransactionHeader) == 32);
     static_assert(sizeof(TransactionHeader) % 8 == 0);
 
-    TransactionHeader mHeader;
+    struct MovableData { // movable
+        TransactionHeader mHeader;
+        std::string mInterfaceName;
+    };
+    MovableData mData;
     Parcel mSent;
     Parcel mReply;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
-    uint8_t mReserved[40];
-#pragma clang diagnostic pop
 };
 
 } // namespace binder::debug
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 4ad0a47..b804f7b 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -81,9 +81,9 @@
     [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
 
     /**
-     * Creates an RPC server at the current port.
+     * Creates an RPC server binding to the given CID at the given port.
      */
-    [[nodiscard]] status_t setupVsockServer(unsigned int port);
+    [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);
 
     /**
      * Creates an RPC server at the current port using IPv4.
@@ -119,7 +119,10 @@
     [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
 
     /**
-     * This must be called before adding a client session.
+     * This must be called before adding a client session. This corresponds
+     * to the number of incoming connections to RpcSession objects in the
+     * server, which will correspond to the number of outgoing connections
+     * in client RpcSession objects.
      *
      * If this is not specified, this will be a single-threaded server.
      *
@@ -134,7 +137,7 @@
      * used. However, this can be used in order to prevent newer protocol
      * versions from ever being used. This is expected to be useful for testing.
      */
-    void setProtocolVersion(uint32_t version);
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
 
     /**
      * Set the supported transports for sending and receiving file descriptors.
@@ -160,17 +163,38 @@
      * Allows a root object to be created for each session.
      *
      * Takes one argument: a callable that is invoked once per new session.
-     * The callable takes two arguments: a type-erased pointer to an OS- and
-     * transport-specific address structure, e.g., sockaddr_vm for vsock, and
-     * an integer representing the size in bytes of that structure. The
-     * callable should validate the size, then cast the type-erased pointer
-     * to a pointer to the actual type of the address, e.g., const void* to
-     * const sockaddr_vm*.
+     * The callable takes three arguments:
+     * - a weak pointer to the session. If you want to hold onto this in the root object, then
+     *   you should keep a weak pointer, and promote it when needed. For instance, if you refer
+     *   to this from the root object, then you could get ahold of transport-specific information.
+     * - a type-erased pointer to an OS- and transport-specific address structure, e.g.,
+     *   sockaddr_vm for vsock
+     * - an integer representing the size in bytes of that structure. The callable should
+     *   validate the size, then cast the type-erased pointer to a pointer to the actual type of the
+     *   address, e.g., const void* to const sockaddr_vm*.
      */
-    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object);
+    void setPerSessionRootObject(
+            std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object);
     sp<IBinder> getRootObject();
 
     /**
+     * Set optional filter of incoming connections based on the peer's address.
+     *
+     * Takes one argument: a callable that is invoked on each accept()-ed
+     * connection and returns false if the connection should be dropped.
+     * See the description of setPerSessionRootObject() for details about
+     * the callable's arguments.
+     */
+    void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
+
+    /**
+     * Set optional modifier of each newly created server socket.
+     *
+     * The only argument is a successfully created file descriptor, not bound to an address yet.
+     */
+    void setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier);
+
+    /**
      * See RpcTransportCtx::getCertificate
      */
     std::vector<uint8_t> getCertificate(RpcCertificateFormat);
@@ -252,7 +276,9 @@
 
     sp<IBinder> mRootObject;
     wp<IBinder> mRootObjectWeak;
-    std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
+    std::function<sp<IBinder>(wp<RpcSession>, const void*, size_t)> mRootObjectFactory;
+    std::function<bool(const void*, size_t)> mConnectionFilter;
+    std::function<void(base::borrowed_fd)> mServerSocketModifier;
     std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
     std::unique_ptr<FdTrigger> mShutdownTrigger;
     RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 40faf2c..cb64603 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,9 +37,9 @@
 class RpcTransport;
 class FdTrigger;
 
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 2;
 constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 1;
 
 // Starting with this version:
 //
@@ -51,11 +51,12 @@
  * This represents a session (group of connections) between a client
  * and a server. Multiple connections are needed for multiple parallel "binder"
  * calls which may also have nested calls.
+ *
+ * Once a binder exists in the session, if all references to all binders are dropped,
+ * the session shuts down.
  */
 class RpcSession final : public virtual RefBase {
 public:
-    static constexpr size_t kDefaultMaxOutgoingThreads = 10;
-
     // Create an RpcSession with default configuration (raw sockets).
     static sp<RpcSession> make();
 
@@ -67,26 +68,30 @@
     /**
      * Set the maximum number of incoming threads allowed to be made (for things like callbacks).
      * By default, this is 0. This must be called before setting up this connection as a client.
-     * Server sessions will inherits this value from RpcServer.
+     * Server sessions will inherits this value from RpcServer. Each thread will serve a
+     * connection to the remote RpcSession.
      *
      * If this is called, 'shutdown' on this session must also be called.
      * Otherwise, a threadpool will leak.
      *
-     * TODO(b/189955605): start these dynamically
+     * TODO(b/189955605): start these lazily - currently all are started
      */
     void setMaxIncomingThreads(size_t threads);
     size_t getMaxIncomingThreads();
 
     /**
-     * Set the maximum number of outgoing threads allowed to be made.
-     * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
-     * connection as a client.
+     * Set the maximum number of outgoing connections allowed to be made.
+     * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up
+     * this connection as a client.
      *
-     * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
-     * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
-     * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+     * For an RpcSession client, if you are connecting to a server which starts N threads,
+     * then this must be set to >= N. If you set the maximum number of outgoing connections
+     * to 1, but the server requests 10, then it would be considered an error. If you set a
+     * maximum number of connections to 10, and the server requests 1, then only 1 will be
+     * created. This API is used to limit the amount of resources a server can request you
+     * create.
      */
-    void setMaxOutgoingThreads(size_t threads);
+    void setMaxOutgoingConnections(size_t connections);
     size_t getMaxOutgoingThreads();
 
     /**
@@ -219,6 +224,8 @@
     friend RpcState;
     explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
 
+    static constexpr size_t kDefaultMaxOutgoingConnections = 10;
+
     // internal version of setProtocolVersion that
     // optionally skips the mStartedSetup check
     [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted);
@@ -368,7 +375,7 @@
 
     bool mStartedSetup = false;
     size_t mMaxIncomingThreads = 0;
-    size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
+    size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections;
     std::optional<uint32_t> mProtocolVersion;
     FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
 
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index fd52a3a..6db9ad9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -39,6 +39,16 @@
 class FdTrigger;
 struct RpcTransportFd;
 
+// for 'friend'
+class RpcTransportRaw;
+class RpcTransportTls;
+class RpcTransportTipcAndroid;
+class RpcTransportTipcTrusty;
+class RpcTransportCtxRaw;
+class RpcTransportCtxTls;
+class RpcTransportCtxTipcAndroid;
+class RpcTransportCtxTipcTrusty;
+
 // Represents a socket connection.
 // No thread-safety is guaranteed for these APIs.
 class RpcTransport {
@@ -92,7 +102,21 @@
      */
     [[nodiscard]] virtual bool isWaiting() = 0;
 
-protected:
+private:
+    // limit the classes which can implement RpcTransport. Being able to change this
+    // interface is important to allow development of RPC binder. In the past, we
+    // changed this interface to use iovec for efficiency, and we added FDs to the
+    // interface. If another transport is needed, it should be added directly here.
+    // non-socket FDs likely also need changes in RpcSession in order to get
+    // connected, and similarly to how addrinfo was type-erased from RPC binder
+    // interfaces when RpcTransportTipc* was added, other changes may be needed
+    // to add more transports.
+
+    friend class ::android::RpcTransportRaw;
+    friend class ::android::RpcTransportTls;
+    friend class ::android::RpcTransportTipcAndroid;
+    friend class ::android::RpcTransportTipcTrusty;
+
     RpcTransport() = default;
 };
 
@@ -117,7 +141,13 @@
     [[nodiscard]] virtual std::vector<uint8_t> getCertificate(
             RpcCertificateFormat format) const = 0;
 
-protected:
+private:
+    // see comment on RpcTransport
+    friend class ::android::RpcTransportCtxRaw;
+    friend class ::android::RpcTransportCtxTls;
+    friend class ::android::RpcTransportCtxTipcAndroid;
+    friend class ::android::RpcTransportCtxTipcTrusty;
+
     RpcTransportCtx() = default;
 };
 
@@ -140,7 +170,7 @@
     RpcTransportCtxFactory() = default;
 };
 
-struct RpcTransportFd {
+struct RpcTransportFd final {
 private:
     mutable bool isPolling{false};
 
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index f08bde8..7d0acd1 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -23,18 +23,55 @@
 
 struct AIBinder;
 struct ARpcServer;
+struct ARpcSession;
+
+enum class ARpcSession_FileDescriptorTransportMode {
+    None,
+    Unix,
+    Trusty,
+};
 
 // Starts an RPC server on a given port and a given root IBinder object.
+// The server will only accept connections from the given CID.
+// Set `cid` to VMADDR_CID_ANY to accept connections from any client.
+// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
 // Returns an opaque handle to the running server instance, or null if the server
 // could not be started.
-[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
+                                              unsigned int port);
 
-// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
+// Starts a Unix domain RPC server with an open raw socket file descriptor
 // and a given root IBinder object.
-// The socket should be created in init.rc with the same `name`.
+// The socket should be created and bound to an address.
 // Returns an opaque handle to the running server instance, or null if the server
 // could not be started.
-[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);
+// The socket will be closed by the server once the server goes out of scope.
+[[nodiscard]] ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd);
+
+// Starts an RPC server that bootstraps sessions using an existing Unix domain
+// socket pair, with a given root IBinder object.
+// Callers should create a pair of SOCK_STREAM Unix domain sockets, pass one to
+// this function and the other to UnixDomainBootstrapClient(). Multiple client
+// session can be created from the client end of the pair.
+// Does not take ownership of `service`.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd);
+
+// Starts an RPC server on a given IP address+port and a given IBinder object.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+// Does not take ownership of `service`.
+// Returns an opaque handle to the running service instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address,
+                                             unsigned int port);
+
+// Sets the list of supported file descriptor transport modes of this RPC server.
+void ARpcServer_setSupportedFileDescriptorTransportModes(
+        ARpcServer* handle,
+        const ARpcSession_FileDescriptorTransportMode modes[],
+        size_t modes_len);
 
 // Runs ARpcServer_join() in a background thread. Immediately returns.
 void ARpcServer_start(ARpcServer* server);
@@ -46,34 +83,56 @@
 void ARpcServer_join(ARpcServer* server);
 
 // Shuts down any running ARpcServer_join().
-void ARpcServer_shutdown(ARpcServer* server);
+[[nodiscard]] bool ARpcServer_shutdown(ARpcServer* server);
 
 // Frees the ARpcServer handle and drops the reference count on the underlying
 // RpcServer instance. The handle must not be reused afterwards.
 // This automatically calls ARpcServer_shutdown().
 void ARpcServer_free(ARpcServer* server);
 
-// Starts an RPC server on a given port and a given root IBinder factory.
-// RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
-// assigning single root IBinder object to all connections, factory is called
-// whenever a client connects, making it possible to assign unique IBinder
-// object to each client.
-bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
-                                  void* factoryContext, unsigned int port);
+// Allocates a new RpcSession object and returns an opaque handle to it.
+[[nodiscard]] ARpcSession* ARpcSession_new();
 
-AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
+// Connects to an RPC server over vsock at a given CID on a given port.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupVsockClient(ARpcSession* session, unsigned int cid,
+                                       unsigned int port);
 
-// Gets the service via the RPC binder with Unix domain socket with the given
-// Unix socket `name`.
-// The final Unix domain socket path name is /dev/socket/`name`.
-AIBinder* UnixDomainRpcClient(const char* name);
+// Connects to an RPC server over a Unix Domain Socket of the given name.
+// The final Unix Domain Socket path name is /dev/socket/`name`.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* session, const char* name);
 
-// Connect to an RPC server with preconnected file descriptors.
+// Connects to an RPC server over the given bootstrap Unix domain socket.
+// Does NOT take ownership of `bootstrapFd`.
+AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* session,
+                                                     int bootstrapFd);
+
+// Connects to an RPC server over an INET socket at a given IP address on a given port.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsigned int port);
+
+// Connects to an RPC server with preconnected file descriptors.
 //
 // requestFd should connect to the server and return a valid file descriptor, or
 // -1 if connection fails.
 //
 // param will be passed to requestFd. Callers can use param to pass contexts to
 // the requestFd function.
-AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
+AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
+                                              int (*requestFd)(void* param),
+                                              void* param);
+
+// Sets the file descriptor transport mode for this session.
+void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
+                                                ARpcSession_FileDescriptorTransportMode mode);
+
+// Sets the maximum number of incoming threads, to service connections.
+void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
+
+// Sets the maximum number of outgoing connections.
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections);
+
+// Decrements the refcount of the underlying RpcSession object.
+void ARpcSession_free(ARpcSession* session);
 }
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f55c779..f51cd9b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -35,91 +35,161 @@
 // Opaque handle for RpcServer.
 struct ARpcServer {};
 
-static sp<RpcServer> toRpcServer(ARpcServer* handle) {
-    auto ref = reinterpret_cast<RpcServer*>(handle);
-    return sp<RpcServer>::fromExisting(ref);
-}
+// Opaque handle for RpcSession.
+struct ARpcSession {};
 
-static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) {
+template <typename A, typename T>
+static A* createObjectHandle(sp<T>& server) {
     auto ref = server.get();
     ref->incStrong(ref);
-    return reinterpret_cast<ARpcServer*>(ref);
+    return reinterpret_cast<A*>(ref);
 }
 
-static void freeRpcServerHandle(ARpcServer* handle) {
-    auto ref = reinterpret_cast<RpcServer*>(handle);
+template <typename T, typename A>
+static void freeObjectHandle(A* handle) {
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
+    auto ref = reinterpret_cast<T*>(handle);
     ref->decStrong(ref);
 }
 
+template <typename T, typename A>
+static sp<T> handleToStrongPointer(A* handle) {
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
+    auto ref = reinterpret_cast<T*>(handle);
+    return sp<T>::fromExisting(ref);
+}
+
+RpcSession::FileDescriptorTransportMode toTransportMode(
+        ARpcSession_FileDescriptorTransportMode mode) {
+    switch (mode) {
+        case ARpcSession_FileDescriptorTransportMode::None:
+            return RpcSession::FileDescriptorTransportMode::NONE;
+        case ARpcSession_FileDescriptorTransportMode::Unix:
+            return RpcSession::FileDescriptorTransportMode::UNIX;
+        case ARpcSession_FileDescriptorTransportMode::Trusty:
+            return RpcSession::FileDescriptorTransportMode::TRUSTY;
+        default:
+            return RpcSession::FileDescriptorTransportMode::NONE;
+    }
+}
+
 extern "C" {
 
-bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
-                                  void* factoryContext, unsigned int port) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
     auto server = RpcServer::make();
-    if (status_t status = server->setupVsockServer(port); status != OK) {
-        LOG(ERROR) << "Failed to set up vsock server with port " << port
-                   << " error: " << statusToString(status).c_str();
-        return false;
+
+    unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
+    if (cid == VMADDR_CID_LOCAL) {
+        bindCid = VMADDR_CID_LOCAL; // bind to the local interface
+        cid = VMADDR_CID_ANY;       // no need for a connection filter
     }
-    server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
-        LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
-        const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
-        LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
-        return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
-    });
 
-    server->join();
-
-    // Shutdown any open sessions since server failed.
-    (void)server->shutdown();
-    return true;
-}
-
-ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
-    auto server = RpcServer::make();
-    if (status_t status = server->setupVsockServer(port); status != OK) {
+    if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
         LOG(ERROR) << "Failed to set up vsock server with port " << port
                    << " error: " << statusToString(status).c_str();
         return nullptr;
     }
+    if (cid != VMADDR_CID_ANY) {
+        server->setConnectionFilter([=](const void* addr, size_t addrlen) {
+            LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+            const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+            LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
+            if (cid != vaddr->svm_cid) {
+                LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid;
+                return false;
+            }
+            return true;
+        });
+    }
     server->setRootObject(AIBinder_toPlatformBinder(service));
-    return createRpcServerHandle(server);
+    return createObjectHandle<ARpcServer>(server);
 }
 
-ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
+ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) {
     auto server = RpcServer::make();
-    auto fd = unique_fd(android_get_control_socket(name));
+    auto fd = unique_fd(socketFd);
     if (!fd.ok()) {
-        LOG(ERROR) << "Failed to get fd for the socket:" << name;
+        LOG(ERROR) << "Invalid socket fd " << socketFd;
         return nullptr;
     }
     if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
-        LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
+        LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd
                    << " error: " << statusToString(status).c_str();
         return nullptr;
     }
     server->setRootObject(AIBinder_toPlatformBinder(service));
-    return createRpcServerHandle(server);
+    return createObjectHandle<ARpcServer>(server);
+}
+
+ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd) {
+    auto server = RpcServer::make();
+    auto fd = unique_fd(bootstrapFd);
+    if (!fd.ok()) {
+        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+        return nullptr;
+    }
+    if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd));
+        status != OK) {
+        LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd
+                   << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    return createObjectHandle<ARpcServer>(server);
+}
+
+ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) {
+    auto server = RpcServer::make();
+    if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) {
+        LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port "
+                   << port << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    return createObjectHandle<ARpcServer>(server);
+}
+
+void ARpcServer_setSupportedFileDescriptorTransportModes(
+        ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
+        size_t modes_len) {
+    auto server = handleToStrongPointer<RpcServer>(handle);
+    std::vector<RpcSession::FileDescriptorTransportMode> modevec;
+    for (size_t i = 0; i < modes_len; i++) {
+        modevec.push_back(toTransportMode(modes[i]));
+    }
+    server->setSupportedFileDescriptorTransportModes(modevec);
 }
 
 void ARpcServer_start(ARpcServer* handle) {
-    toRpcServer(handle)->start();
+    handleToStrongPointer<RpcServer>(handle)->start();
 }
 
 void ARpcServer_join(ARpcServer* handle) {
-    toRpcServer(handle)->join();
+    handleToStrongPointer<RpcServer>(handle)->join();
 }
 
-void ARpcServer_shutdown(ARpcServer* handle) {
-    toRpcServer(handle)->shutdown();
+bool ARpcServer_shutdown(ARpcServer* handle) {
+    return handleToStrongPointer<RpcServer>(handle)->shutdown();
 }
 
 void ARpcServer_free(ARpcServer* handle) {
-    freeRpcServerHandle(handle);
+    // Ignore the result of ARpcServer_shutdown - either it had been called
+    // earlier, or the RpcServer destructor will panic.
+    (void)ARpcServer_shutdown(handle);
+    freeObjectHandle<RpcServer>(handle);
 }
 
-AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
+ARpcSession* ARpcSession_new() {
     auto session = RpcSession::make();
+    return createObjectHandle<ARpcSession>(session);
+}
+
+void ARpcSession_free(ARpcSession* handle) {
+    freeObjectHandle<RpcSession>(handle);
+}
+
+AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
     if (status_t status = session->setupVsockClient(cid, port); status != OK) {
         LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
                    << " error: " << statusToString(status).c_str();
@@ -128,10 +198,10 @@
     return AIBinder_fromPlatformBinder(session->getRootObject());
 }
 
-AIBinder* UnixDomainRpcClient(const char* name) {
+AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* name) {
     std::string pathname(name);
     pathname = ANDROID_SOCKET_DIR "/" + pathname;
-    auto session = RpcSession::make();
+    auto session = handleToStrongPointer<RpcSession>(handle);
     if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) {
         LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname
                    << " error: " << statusToString(status).c_str();
@@ -140,8 +210,35 @@
     return AIBinder_fromPlatformBinder(session->getRootObject());
 }
 
-AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
-    auto session = RpcSession::make();
+AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bootstrapFd) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    auto fd = unique_fd(dup(bootstrapFd));
+    if (!fd.ok()) {
+        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+        return nullptr;
+    }
+    if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd));
+        status != OK) {
+        LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd
+                   << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+
+AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    if (status_t status = session->setupInetClient(address, port); status != OK) {
+        LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port "
+                   << port << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+
+AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
+                                              void* param) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
     auto request = [=] { return unique_fd{requestFd(param)}; };
     if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
         LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
@@ -149,4 +246,20 @@
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
 }
+
+void ARpcSession_setFileDescriptorTransportMode(ARpcSession* handle,
+                                                ARpcSession_FileDescriptorTransportMode mode) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    session->setFileDescriptorTransportMode(toTransportMode(mode));
+}
+
+void ARpcSession_setMaxIncomingThreads(ARpcSession* handle, size_t threads) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    session->setMaxIncomingThreads(threads);
+}
+
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    session->setMaxOutgoingConnections(connections);
+}
 }
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index 1bc2416..50f7deb 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -2,7 +2,8 @@
   global:
     ARpcServer_free;
     ARpcServer_join;
-    ARpcServer_newInitUnixDomain;
+    ARpcServer_newInet;
+    ARpcServer_newBoundSocket;
     ARpcServer_newVsock;
     ARpcServer_shutdown;
     ARpcServer_start;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 8ae7537..58ed418 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -66,10 +66,14 @@
         "service_manager.cpp",
     ],
 
-    shared_libs: [
+    static_libs: [
         "libandroid_runtime_lazy",
         "libbase",
+    ],
+
+    shared_libs: [
         "libbinder",
+        "liblog",
         "libutils",
     ],
 
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 28d1f16..f7dd9c9 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -75,12 +75,48 @@
 AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
 AIBinder::~AIBinder() {}
 
-std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
-                                                     const String16& newDescriptor, bool set) {
+// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char
+static std::string SanitizeString(const String16& str) {
+    std::string sanitized{String8(str)};
+    for (auto& c : sanitized) {
+        if (!isprint(c)) {
+            c = '?';
+        }
+    }
+    return sanitized;
+}
+
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
+    if (clazz == nullptr) return false;
+
+    // If mClazz is non-null, this must have been called and cached
+    // already. So, we can safely call this first. Due to the implementation
+    // of getInterfaceDescriptor (at time of writing), two simultaneous calls
+    // may lead to extra binder transactions, but this is expected to be
+    // exceedingly rare. Once we have a binder, when we get it again later,
+    // we won't make another binder transaction here.
+    const String16& descriptor = getBinder()->getInterfaceDescriptor();
+    const String16& newDescriptor = clazz->getInterfaceDescriptor();
+
     std::lock_guard<std::mutex> lock(mClazzMutex);
     if (mClazz == clazz) return true;
 
-    if (mClazz != nullptr) {
+    // If this is an ABpBinder, the first class object becomes the canonical one. The implication
+    // of this is that no API can require a proxy information to get information on how to behave.
+    // from the class itself - which should only store the interface descriptor. The functionality
+    // should be implemented by adding AIBinder_* APIs to set values on binders themselves, by
+    // setting things on AIBinder_Class which get transferred along with the binder, so that they
+    // can be read along with the BpBinder, or by modifying APIs directly (e.g. an option in
+    // onTransact).
+    //
+    // While this check is required to support linkernamespaces, one downside of it is that
+    // you may parcel code to communicate between things in the same process. However, comms
+    // between linkernamespaces like this already happen for cross-language calls like Java<->C++
+    // or Rust<->Java, and there are good stability guarantees here. This interacts with
+    // binder Stability checks exactly like any other in-process call. The stability is known
+    // to the IBinder object, so that it doesn't matter if a class object comes from
+    // a different stability level.
+    if (mClazz != nullptr && !asABpBinder()) {
         const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
         if (newDescriptor == currentDescriptor) {
             LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
@@ -97,38 +133,11 @@
         return false;
     }
 
-    if (set) {
-        // if this is a local object, it's not one known to libbinder_ndk
-        mClazz = clazz;
-        return true;
-    }
-
-    return {};
-}
-
-// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char
-static std::string SanitizeString(const String16& str) {
-    std::string sanitized{String8(str)};
-    for (auto& c : sanitized) {
-        if (!isprint(c)) {
-            c = '?';
-        }
-    }
-    return sanitized;
-}
-
-bool AIBinder::associateClass(const AIBinder_Class* clazz) {
-    if (clazz == nullptr) return false;
-
-    const String16& newDescriptor = clazz->getInterfaceDescriptor();
-
-    auto result = associateClassInternal(clazz, newDescriptor, false);
-    if (result.has_value()) return *result;
-
-    CHECK(asABpBinder() != nullptr);  // ABBinder always has a descriptor
-
-    const String16& descriptor = getBinder()->getInterfaceDescriptor();
-    if (descriptor != newDescriptor) {
+    // This will always be an O(n) comparison, but it's expected to be extremely rare.
+    // since it's an error condition. Do the comparison after we take the lock and
+    // check the pointer equality fast path. By always taking the lock, it's also
+    // more flake-proof. However, the check is not dependent on the lock.
+    if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) {
         if (getBinder()->isBinderAlive()) {
             LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
                        << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
@@ -141,7 +150,14 @@
         return false;
     }
 
-    return associateClassInternal(clazz, newDescriptor, true).value();
+    // A local binder being set for the first time OR
+    // ignoring a proxy binder which is set multiple time, by considering the first
+    // associated class as the canonical one.
+    if (mClazz == nullptr) {
+        mClazz = clazz;
+    }
+
+    return true;
 }
 
 ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
@@ -325,6 +341,10 @@
     return lhs->binder < rhs->binder;
 }
 
+// WARNING: When multiple classes exist with the same interface descriptor in different
+// linkernamespaces, the first one to be associated with mClazz becomes the canonical one
+// and the only requirement on this is that the interface descriptors match. If this
+// is an ABpBinder, no other state can be referenced from mClazz.
 AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
                                AIBinder_Class_onDestroy onDestroy,
                                AIBinder_Class_onTransact onTransact)
@@ -632,6 +652,10 @@
     (*in)->get()->markForBinder(binder->getBinder());
 
     status_t status = android::OK;
+
+    // note - this is the only read of a value in clazz, and it comes with a warning
+    // on the API itself. Do not copy this design. Instead, attach data in a new
+    // version of the prepareTransaction function.
     if (clazz->writeHeader) {
         status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
     }
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index d7098e8..9d5368f 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -53,12 +53,14 @@
     }
 
    private:
-    std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
-                                               const ::android::String16& newDescriptor, bool set);
-
     // AIBinder instance is instance of this class for a local object. In order to transact on a
     // remote object, this also must be set for simplicity (although right now, only the
     // interfaceDescriptor from it is used).
+    //
+    // WARNING: When multiple classes exist with the same interface descriptor in different
+    // linkernamespaces, the first one to be associated with mClazz becomes the canonical one
+    // and the only requirement on this is that the interface descriptors match. If this
+    // is an ABpBinder, no other state can be referenced from mClazz.
     const AIBinder_Class* mClazz;
     std::mutex mClazzMutex;
 };
@@ -102,10 +104,14 @@
     ::android::sp<::android::IBinder> getBinder() override { return mRemote; }
     ABpBinder* asABpBinder() override { return this; }
 
+    bool isServiceFuzzing() const { return mServiceFuzzing; }
+    void setServiceFuzzing() { mServiceFuzzing = true; }
+
    private:
     friend android::sp<ABpBinder>;
     explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
     ::android::sp<::android::IBinder> mRemote;
+    bool mServiceFuzzing = false;
 };
 
 struct AIBinder_Class {
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 81975e7..6273804 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -138,6 +138,8 @@
 
     /**
      * Dumps information about the interface. By default, dumps nothing.
+     *
+     * This method is not given ownership of the FD.
      */
     virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
 
@@ -196,6 +198,10 @@
 
     bool isRemote() override final { return false; }
 
+    static std::string makeServiceName(std::string_view instance) {
+        return INTERFACE::descriptor + ("/" + std::string(instance));
+    }
+
    protected:
     /**
      * This function should only be called by asBinder. Otherwise, there is a possibility of
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 2a00736..b0c7f6d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -49,12 +49,17 @@
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #define HAS_NDK_INTERFACE
-#else
+#endif
+
+// TODO: some things include libbinder without having access to libbase. This is
+// due to frameworks/native/include, which symlinks to libbinder headers, so even
+// though we don't use it here, we detect a different header, so that we are more
+// confident libbase will be included
+#if __has_include(<binder/RpcSession.h>)
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <binder/ParcelableHolder.h>
-#endif  //_has_include
+#define HAS_CPP_INTERFACE
+#endif
 
 namespace android {
 namespace internal {
@@ -104,10 +109,12 @@
             IsInstantiationOf<_U, sp>::value ||  // for IBinder and interface types in the C++
                                                  // backend
 #endif
-                    IsInstantiationOf<_U, std::optional>::value ||  // for @nullable types in the
-                                                                    // C++/NDK backends
-                    IsInstantiationOf<_U, std::shared_ptr>::value,  // for interface types in the
-                                                                    // NDK backends
+                    IsInstantiationOf<_U, std::optional>::value ||    // for @nullable types in the
+                                                                      // C++/NDK backends
+                    IsInstantiationOf<_U, std::unique_ptr>::value ||  // for @nullable(heap=true)
+                                                                      // in C++/NDK backends
+                    IsInstantiationOf<_U, std::shared_ptr>::value,    // for interface types in the
+                                                                      // NDK backends
 
             std::true_type>
     _test(int);
@@ -132,36 +139,15 @@
 };
 
 template <typename _T>
-class ToEmptyString {
-    template <typename _U>
-    static std::enable_if_t<
-#ifdef HAS_NDK_INTERFACE
-            std::is_base_of_v<::ndk::ICInterface, _U>
-#if __ANDROID_API__ >= 31
-                    || std::is_same_v<::ndk::AParcelableHolder, _U>
-#endif
-#else
-            std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
-                    std::is_same_v<os::ParcelFileDescriptor, _U> ||
-                    std::is_same_v<os::ParcelableHolder, _U>
-#endif
-            ,
-            std::true_type>
-    _test(int);
-    template <typename _U>
-    static std::false_type _test(...);
-
-   public:
-    enum { value = decltype(_test<_T>(0))::value };
+struct TypeDependentFalse {
+    enum { value = false };
 };
 
 }  // namespace details
 
 template <typename _T>
 std::string ToString(const _T& t) {
-    if constexpr (details::ToEmptyString<_T>::value) {
-        return "<unimplemented>";
-    } else if constexpr (std::is_same_v<bool, _T>) {
+    if constexpr (std::is_same_v<bool, _T>) {
         return t ? "true" : "false";
     } else if constexpr (std::is_same_v<char16_t, _T>) {
         // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the
@@ -181,6 +167,24 @@
         return ss.str();
     } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
         return "fd:" + std::to_string(t.get());
+    } else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) {
+        // TODO(b/266248339): this format is to make it easy to handle resolv_integration_test
+        // freezing the output format. We would like to print more info.
+        return "<interface>";
+#if __ANDROID_API__ >= 31
+    } else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) {
+        return "AParcelableHolder";
+#endif
+#endif  // HAS_NDK_INTERFACE
+#ifdef HAS_CPP_INTERFACE
+    } else if constexpr (std::is_base_of_v<IInterface, _T>) {
+        std::stringstream ss;
+        ss << "interface:" << std::hex << &t;
+        return ss.str();
+    } else if constexpr (std::is_same_v<IBinder, _T>) {
+        std::stringstream ss;
+        ss << "binder:" << std::hex << &t;
+        return ss.str();
 #endif
 #ifdef HAS_STRING16
     } else if constexpr (std::is_same_v<String16, _T>) {
@@ -214,11 +218,27 @@
         out << "]";
         return out.str();
     } else {
-        return "{no toString() implemented}";
+        static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?");
     }
 }
 
 }  // namespace internal
 }  // namespace android
 
+#ifdef HAS_STRONG_POINTER
+#undef HAS_STRONG_POINTER
+#endif
+
+#ifdef HAS_STRING16
+#undef HAS_STRING16
+#endif
+
+#ifdef HAS_NDK_INTERFACE
+#undef HAS_NDK_INTERFACE
+#endif
+
+#ifdef HAS_CPP_INTERFACE
+#undef HAS_CPP_INTERFACE
+#endif
+
 /** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..db2d2c1 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -229,6 +229,11 @@
  *
  * Available since API level 33.
  *
+ * WARNING: this API interacts badly with linkernamespaces. For correct behavior, you must
+ * use it on all instances of a class in the same process which share the same interface
+ * descriptor. In general, it is recommended you do not use this API, because it is disabling
+ * type safety.
+ *
  * \param clazz class to disable interface header on.
  */
 void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(33);
@@ -589,6 +594,9 @@
  *
  * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath.
  *
+ * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically
+ * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked.
+ *
  * Available since API level 33.
  *
  * \param cookie the cookie passed to AIBinder_linkToDeath.
@@ -600,6 +608,9 @@
  *
  * Available since API level 29.
  *
+ * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically
+ * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked.
+ *
  * \param onBinderDied the callback to call when this death recipient is invoked.
  *
  * \return the newly constructed object (or null if onBinderDied is null).
@@ -613,7 +624,8 @@
  *
  *  1. If the binder died, shortly after the call to onBinderDied.
  *  2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- *     AIBinder_DeathRecipient_delete.
+ *     AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ *     finish.
  *  3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
  *
  * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index f68612c..d833b83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,11 +26,11 @@
 
 #pragma once
 
+#include <android/binder_status.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
-
-#include <android/binder_status.h>
+#include <uchar.h>
 
 struct AIBinder;
 typedef struct AIBinder AIBinder;
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index ad4188f..89fd7a3 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -22,6 +22,16 @@
 
 __BEGIN_DECLS
 
+enum AServiceManager_AddServiceFlag : uint32_t {
+    /**
+     * This allows processes with AID_ISOLATED to get the binder of the service added.
+     *
+     * Services with methods that perform file IO, web socket creation or ways to egress data must
+     * not be added with this flag for privacy concerns.
+     */
+    ADD_SERVICE_ALLOW_ISOLATED = 1,
+};
+
 /**
  * This registers the service with the default service manager under this instance name. This does
  * not take ownership of binder.
@@ -38,6 +48,23 @@
         AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
 
 /**
+ * This registers the service with the default service manager under this instance name. This does
+ * not take ownership of binder.
+ *
+ * WARNING: when using this API across an APEX boundary, do not use with unstable
+ * AIDL services. TODO(b/139325195)
+ *
+ * \param binder object to register globally with the service manager.
+ * \param instance identifier of the service. This will be used to lookup the service.
+ * \param flags an AServiceManager_AddServiceFlag enum to denote how the service should be added.
+ *
+ * \return EX_NONE on success.
+ */
+__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithFlags(
+        AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flags)
+        __INTRODUCED_IN(34);
+
+/**
  * Gets a binder object with this specific instance name. Will return nullptr immediately if the
  * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this
  * function is responsible for calling AIBinder_decStrong).
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..68528e1 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -24,23 +24,52 @@
 __BEGIN_DECLS
 
 /**
- * This creates a threadpool for incoming binder transactions if it has not already been created.
+ * This creates a threadpool for incoming binder transactions if it has not already been created,
+ * spawning one thread, and allowing the kernel to lazily start threads according to the count
+ * that is specified in ABinderProcess_setThreadPoolMaxThreadCount.
+ *
+ * For instance, if ABinderProcess_setThreadPoolMaxThreadCount(3) is called,
+ * ABinderProcess_startThreadPool() is called (+1 thread) then the main thread calls
+ * ABinderProcess_joinThreadPool() (+1 thread), up to *5* total threads will be started
+ * (2 directly, and 3 more if the kernel starts them lazily).
  *
  * When using this, it is expected that ABinderProcess_setupPolling and
  * ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
  */
-void ABinderProcess_startThreadPool();
+void ABinderProcess_startThreadPool(void);
 /**
  * This sets 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.
+ * the kernel from starting new threads and will not delete already existing threads. This should
+ * be called once before startThreadPool. The number of threads can never decrease.
+ *
+ * This count refers to the number of threads that will be created lazily by the kernel, in
+ * addition to the threads created by ABinderProcess_startThreadPool or
+ * ABinderProcess_joinThreadPool.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
  */
 bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
 /**
- * This adds the current thread to the threadpool. This may cause the threadpool to exceed the
- * maximum size.
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
  */
-void ABinderProcess_joinThreadPool();
+bool ABinderProcess_isThreadPoolStarted(void);
+/**
+ * This adds the current thread to the threadpool. This thread will be in addition to the thread
+ * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by
+ * ABinderProcess_setThreadPoolMaxThreadCount.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
+ */
+void ABinderProcess_joinThreadPool(void);
 
 /**
  * This gives you an fd to wait on. Whenever data is available on the fd,
@@ -63,6 +92,6 @@
  *
  * \return STATUS_OK on success
  */
-__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31);
+__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands(void) __INTRODUCED_IN(31);
 
 __END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
  * requirements associated with that higher stability level. For instance, a
  * VINTF stability binder is required to be in the VINTF manifest. This API
  * can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ *     auto binder = ...->asBinder();
+ *     AIBinder_forceDowngradeToVendorStability(binder.get());
+ *     doSomething(binder);
  */
 void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
 
@@ -79,6 +88,15 @@
  * requirements associated with that higher stability level. For instance, a
  * VINTF stability binder is required to be in the VINTF manifest. This API
  * can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ *     auto binder = ...->asBinder();
+ *     AIBinder_forceDowngradeToSystemStability(binder.get());
+ *     doSomething(binder);
  */
 void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
 
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 5c7005c..1c5f79f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -154,9 +154,11 @@
 
 LIBBINDER_NDK34 { # introduced=UpsideDownCake
   global:
+    ABinderProcess_isThreadPoolStarted; # systemapi llndk
     AServiceManager_getUpdatableApexName; # systemapi
     AServiceManager_registerForServiceNotifications; # systemapi llndk
     AServiceManager_NotificationRegistration_delete; # systemapi llndk
+    AServiceManager_addServiceWithFlags; # systemapi llndk
 };
 
 LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 8693022..037aa2e 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -270,6 +270,13 @@
     }
     sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(readBinder);
     AIBinder_incStrong(ret.get());
+
+    if (ret.get() != nullptr && parcel->get()->isServiceFuzzing()) {
+        if (auto bp = ret->asABpBinder(); bp != nullptr) {
+            bp->setServiceFuzzing();
+        }
+    }
+
     *binder = ret.get();
     return PruneStatusT(status);
 }
@@ -695,11 +702,17 @@
     if (parcel->get()->objectsCount()) {
         return STATUS_INVALID_OPERATION;
     }
-    int32_t dataSize = AParcel_getDataSize(parcel);
+    // b/264739302 - getDataSize will return dataPos if it is greater than dataSize
+    // which will cause crashes in memcpy at later point. Instead compare with
+    // actual length of internal buffer
+    int32_t dataSize = parcel->get()->dataBufferSize();
     if (len > static_cast<size_t>(dataSize) || start > static_cast<size_t>(dataSize) - len) {
         return STATUS_BAD_VALUE;
     }
     const uint8_t* internalBuffer = parcel->get()->data();
+    if (internalBuffer == nullptr) {
+        return STATUS_UNEXPECTED_NULL;
+    }
     memcpy(buffer, internalBuffer + start, len);
     return STATUS_OK;
 }
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..0fea57b 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -24,14 +24,17 @@
 using ::android::IPCThreadState;
 using ::android::ProcessState;
 
-void ABinderProcess_startThreadPool() {
+void ABinderProcess_startThreadPool(void) {
     ProcessState::self()->startThreadPool();
     ProcessState::self()->giveThreadPoolName();
 }
 bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
     return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
 }
-void ABinderProcess_joinThreadPool() {
+bool ABinderProcess_isThreadPoolStarted(void) {
+    return ProcessState::self()->isThreadPoolStarted();
+}
+void ABinderProcess_joinThreadPool(void) {
     IPCThreadState::self()->joinThreadPool();
 }
 
@@ -39,6 +42,6 @@
     return IPCThreadState::self()->setupPolling(fd);
 }
 
-binder_status_t ABinderProcess_handlePolledCommands() {
+binder_status_t ABinderProcess_handlePolledCommands(void) {
     return IPCThreadState::self()->handlePolledCommands();
 }
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index e107c83..2977786 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -41,6 +41,20 @@
     status_t exception = sm->addService(String16(instance), binder->getBinder());
     return PruneException(exception);
 }
+
+binder_exception_t AServiceManager_addServiceWithFlags(AIBinder* binder, const char* instance,
+                                                       const AServiceManager_AddServiceFlag flags) {
+    if (binder == nullptr || instance == nullptr) {
+        return EX_ILLEGAL_ARGUMENT;
+    }
+
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
+    status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated);
+    return PruneException(exception);
+}
+
 AIBinder* AServiceManager_checkService(const char* instance) {
     if (instance == nullptr) {
         return nullptr;
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 2afe5d2..76acff5 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -72,6 +72,11 @@
 AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate,
                                                      IFoo_Class_onDestroy, IFoo_Class_onTransact);
 
+// Defines the same class. Ordinarly, you would never want to do this, but it's done here
+// to simulate what would happen when multiple linker namespaces interact.
+AIBinder_Class* IFoo::kClassDupe = AIBinder_Class_define(
+        kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact);
+
 class BpFoo : public IFoo {
    public:
     explicit BpFoo(AIBinder* binder) : mBinder(binder) {}
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index 7408d0c..0a562f0 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -30,8 +30,13 @@
     static const char* kIFooDescriptor;
 
     static AIBinder_Class* kClass;
+    static AIBinder_Class* kClassDupe;
 
     // binder representing this interface with one reference count
+    // NOTE - this will create a new binder if it already exists. If you use
+    // getService for instance, you must pull outBinder. Don't use this without
+    // verifying isRemote or pointer equality. This is not a very good testing API - don't
+    // copy it - consider the AIDL-generated APIs instead.
     AIBinder* getBinder();
 
     // Takes ownership of IFoo
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 9d5ef68..27ce615 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -51,10 +51,23 @@
 constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
 constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
 constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
+constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
 
-constexpr unsigned int kShutdownWaitTime = 10;
+constexpr unsigned int kShutdownWaitTime = 11;
 constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
 
+class MyTestFoo : public IFoo {
+    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+        *out = 2 * in;
+        LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+        return STATUS_OK;
+    }
+    binder_status_t die() override {
+        ADD_FAILURE() << "die called on local instance";
+        return STATUS_OK;
+    }
+};
+
 class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
     ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
         *out = in;
@@ -94,11 +107,13 @@
     }
     static bool activeServicesCallback(bool hasClients, void* context) {
         if (hasClients) {
+            LOG(INFO) << "hasClients, so not unregistering.";
             return false;
         }
 
         // Unregister all services
         if (!AServiceManager_tryUnregister()) {
+            LOG(INFO) << "Could not unregister service the first time.";
             // Prevent shutdown (test will fail)
             return false;
         }
@@ -108,6 +123,7 @@
 
         // Unregister again before shutdown
         if (!AServiceManager_tryUnregister()) {
+            LOG(INFO) << "Could not unregister service the second time.";
             // Prevent shutdown (test will fail)
             return false;
         }
@@ -115,6 +131,7 @@
         // Check if the context was passed correctly
         MyBinderNdkUnitTest* service = static_cast<MyBinderNdkUnitTest*>(context);
         if (service->contextTestValue != kContextTestValue) {
+            LOG(INFO) << "Incorrect context value.";
             // Prevent shutdown (test will fail)
             return false;
         }
@@ -146,6 +163,24 @@
     return 1;  // should not return
 }
 
+int generatedFlaggedService(const AServiceManager_AddServiceFlag flags, const char* instance) {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+    auto binder = service->asBinder();
+
+    binder_exception_t exception =
+            AServiceManager_addServiceWithFlags(binder.get(), instance, flags);
+
+    if (exception != EX_NONE) {
+        LOG(FATAL) << "Could not register: " << exception << " " << instance;
+    }
+
+    ABinderProcess_joinThreadPool();
+
+    return 1;  // should not return
+}
+
 // manually-written parceling class considered bad practice
 class MyFoo : public IFoo {
     binder_status_t doubleNumber(int32_t in, int32_t* out) override {
@@ -248,8 +283,8 @@
 
 TEST(NdkBinder, CheckServiceThatDoesExist) {
     AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
-    EXPECT_NE(nullptr, binder);
-    EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+    ASSERT_NE(nullptr, binder) << "Could not get " << kExistingNonNdkService;
+    EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)) << "Could not ping " << kExistingNonNdkService;
 
     AIBinder_decStrong(binder);
 }
@@ -296,11 +331,10 @@
 }
 
 TEST(NdkBinder, UnimplementedDump) {
-    sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+    ndk::SpAIBinder binder;
+    sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR());
     ASSERT_NE(foo, nullptr);
-    AIBinder* binder = foo->getBinder();
-    EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0));
-    AIBinder_decStrong(binder);
+    EXPECT_EQ(OK, AIBinder_dump(binder.get(), STDOUT_FILENO, nullptr, 0));
 }
 
 TEST(NdkBinder, UnimplementedShell) {
@@ -324,6 +358,24 @@
     EXPECT_EQ(2, out);
 }
 
+TEST(NdkBinder, ReassociateBpBinderWithSameDescriptor) {
+    ndk::SpAIBinder binder;
+    sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR());
+
+    EXPECT_TRUE(AIBinder_isRemote(binder.get()));
+
+    EXPECT_TRUE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe));
+}
+
+TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) {
+    sp<IFoo> foo = sp<MyTestFoo>::make();
+    ndk::SpAIBinder binder(foo->getBinder());
+
+    EXPECT_FALSE(AIBinder_isRemote(binder.get()));
+
+    EXPECT_FALSE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe));
+}
+
 TEST(NdkBinder, GetTestServiceStressTest) {
     // libbinder has some complicated logic to make sure only one instance of
     // ABpBinder is associated with each binder.
@@ -431,6 +483,8 @@
 }
 
 TEST(NdkBinder, ActiveServicesCallbackTest) {
+    LOG(INFO) << "ActiveServicesCallbackTest starting";
+
     ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService));
     std::shared_ptr<aidl::IBinderNdkUnitTest> service =
             aidl::IBinderNdkUnitTest::fromBinder(binder);
@@ -441,6 +495,7 @@
     service = nullptr;
     IPCThreadState::self()->flushCommands();
 
+    LOG(INFO) << "ActiveServicesCallbackTest about to sleep";
     sleep(kShutdownWaitTime);
 
     ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService))
@@ -449,14 +504,28 @@
 
 struct DeathRecipientCookie {
     std::function<void(void)>*onDeath, *onUnlink;
+
+    // may contain additional data
+    // - if it contains AIBinder, then you must call AIBinder_unlinkToDeath manually,
+    //   because it would form a strong reference cycle
+    // - if it points to a data member of another structure, this should have a weak
+    //   promotable reference or a strong reference, in case that object is deleted
+    //   while the death recipient is firing
 };
 void LambdaOnDeath(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+
+    // may reference other cookie members
+
     (*funcs->onDeath)();
 };
 void LambdaOnUnlink(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
     (*funcs->onUnlink)();
+
+    // may reference other cookie members
+
+    delete funcs;
 };
 TEST(NdkBinder, DeathRecipient) {
     using namespace std::chrono_literals;
@@ -488,12 +557,12 @@
         unlinkCv.notify_one();
     };
 
-    DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+    DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
 
     AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
     AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
 
-    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
+    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
 
     // the binder driver should return this if the service dies during the transaction
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
@@ -545,18 +614,6 @@
     AIBinder_decStrong(binder);
 }
 
-class MyTestFoo : public IFoo {
-    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
-        *out = 2 * in;
-        LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
-        return STATUS_OK;
-    }
-    binder_status_t die() override {
-        ADD_FAILURE() << "die called on local instance";
-        return STATUS_OK;
-    }
-};
-
 TEST(NdkBinder, SetInheritRt) {
     // functional test in binderLibTest
     sp<IFoo> foo = sp<MyTestFoo>::make();
@@ -597,7 +654,8 @@
     sp<IFoo> foo = new MyTestFoo;
     EXPECT_EQ(EX_NONE, foo->addService(kInstanceName));
 
-    sp<IFoo> getFoo = IFoo::getService(kInstanceName);
+    ndk::SpAIBinder binder;
+    sp<IFoo> getFoo = IFoo::getService(kInstanceName, binder.getR());
     EXPECT_EQ(foo.get(), getFoo.get());
 
     int32_t out;
@@ -829,6 +887,12 @@
     EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
 }
 
+TEST(NdkBinder, FlaggedServiceAccessible) {
+    static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestServiceFlagged));
+    ASSERT_NE(nullptr, testService);
+}
+
 TEST(NdkBinder, GetClassInterfaceDescriptor) {
     ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
 }
@@ -883,6 +947,13 @@
         prctl(PR_SET_PDEATHSIG, SIGHUP);
         return generatedService();
     }
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+        // We may want to change this flag to be more generic ones for the future
+        AServiceManager_AddServiceFlag test_flags =
+                AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
+        return generatedFlaggedService(test_flags, kBinderNdkUnitTestServiceFlagged);
+    }
 
     ABinderProcess_setThreadPoolMaxThreadCount(1);  // to receive death notifications/callbacks
     ABinderProcess_startThreadPool();
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index afd414a..d36ebac 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -21,6 +21,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -72,6 +73,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -129,6 +131,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
 
     // Currently necessary for host builds
     // TODO(b/31559095): bionic on host should define this
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index f70ebfc..788abc4 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -23,7 +23,13 @@
         "liblibc",
         "liblog_rust",
     ],
+    visibility: [
+        "//device/google/cuttlefish/shared/minidroid/sample",
+        "//packages/modules/Virtualization:__subpackages__",
+        "//system/software_defined_vehicle:__subpackages__",
+    ],
     apex_available: [
+        "//apex_available:platform",
         "com.android.compos",
         "com.android.uwb",
         "com.android.virt",
@@ -51,6 +57,7 @@
         "libutils",
     ],
     apex_available: [
+        "//apex_available:platform",
         "com.android.compos",
         "com.android.uwb",
         "com.android.virt",
@@ -72,6 +79,8 @@
         "AIBinder",
         "--raw-line",
         "use binder_ndk_sys::AIBinder;",
+        "--rustified-enum",
+        "ARpcSession_FileDescriptorTransportMode",
     ],
     rustlibs: [
         "libbinder_ndk_sys",
@@ -81,6 +90,7 @@
         "libutils",
     ],
     apex_available: [
+        "//apex_available:platform",
         "com.android.compos",
         "com.android.uwb",
         "com.android.virt",
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
deleted file mode 100644
index 48c787b..0000000
--- a/libs/binder/rust/rpcbinder/src/client.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
-use std::ffi::CString;
-use std::os::{
-    raw::{c_int, c_void},
-    unix::io::RawFd,
-};
-
-/// Connects to an RPC Binder server over vsock.
-pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
-    // SAFETY: AIBinder returned by VsockRpcClient has correct reference count,
-    // and the ownership can safely be taken by new_spibinder.
-    unsafe { new_spibinder(binder_rpc_unstable_bindgen::VsockRpcClient(cid, port)) }
-}
-
-/// Connects to an RPC Binder server for a particular interface over vsock.
-pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>(
-    cid: u32,
-    port: u32,
-) -> Result<Strong<T>, StatusCode> {
-    interface_cast(get_vsock_rpc_service(cid, port))
-}
-
-/// Connects to an RPC Binder server over Unix domain socket.
-pub fn get_unix_domain_rpc_service(socket_name: &str) -> Option<SpIBinder> {
-    let socket_name = match CString::new(socket_name) {
-        Ok(s) => s,
-        Err(e) => {
-            log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
-            return None;
-        }
-    };
-    // SAFETY: AIBinder returned by UnixDomainRpcClient has correct reference count,
-    // and the ownership can safely be taken by new_spibinder.
-    unsafe { new_spibinder(binder_rpc_unstable_bindgen::UnixDomainRpcClient(socket_name.as_ptr())) }
-}
-
-/// Connects to an RPC Binder server for a particular interface over Unix domain socket.
-pub fn get_unix_domain_rpc_interface<T: FromIBinder + ?Sized>(
-    socket_name: &str,
-) -> Result<Strong<T>, StatusCode> {
-    interface_cast(get_unix_domain_rpc_service(socket_name))
-}
-
-/// Connects to an RPC Binder server, using the given callback to get (and take ownership of)
-/// file descriptors already connected to it.
-pub fn get_preconnected_rpc_service(
-    mut request_fd: impl FnMut() -> Option<RawFd>,
-) -> Option<SpIBinder> {
-    // Double reference the factory because trait objects aren't FFI safe.
-    let mut request_fd_ref: RequestFd = &mut request_fd;
-    let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
-
-    // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
-    // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
-    // of param, only passing it to request_fd_wrapper.
-    unsafe {
-        new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient(
-            Some(request_fd_wrapper),
-            param,
-        ))
-    }
-}
-
-type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
-
-unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
-    // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
-    // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
-    // initialized instance.
-    let request_fd_ptr = param as *mut RequestFd;
-    let request_fd = request_fd_ptr.as_mut().unwrap();
-    if let Some(fd) = request_fd() {
-        fd
-    } else {
-        -1
-    }
-}
-
-/// Connects to an RPC Binder server for a particular interface, using the given callback to get
-/// (and take ownership of) file descriptors already connected to it.
-pub fn get_preconnected_rpc_interface<T: FromIBinder + ?Sized>(
-    request_fd: impl FnMut() -> Option<RawFd>,
-) -> Result<Strong<T>, StatusCode> {
-    interface_cast(get_preconnected_rpc_service(request_fd))
-}
-
-fn interface_cast<T: FromIBinder + ?Sized>(
-    service: Option<SpIBinder>,
-) -> Result<Strong<T>, StatusCode> {
-    if let Some(service) = service {
-        FromIBinder::try_from(service)
-    } else {
-        Err(StatusCode::NAME_NOT_FOUND)
-    }
-}
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 1b719aa..a957385 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -16,11 +16,8 @@
 
 //! API for RPC Binder services.
 
-mod client;
 mod server;
+mod session;
 
-pub use client::{
-    get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface,
-    get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service,
-};
-pub use server::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef};
+pub use server::{RpcServer, RpcServerRef};
+pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 42f5567..81f68f5 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-use binder::{
-    unstable_api::{AIBinder, AsNative},
-    SpIBinder,
-};
+use crate::session::FileDescriptorTransportMode;
+use binder::{unstable_api::AsNative, SpIBinder};
 use binder_rpc_unstable_bindgen::ARpcServer;
 use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::ffi::CString;
 use std::io::{Error, ErrorKind};
-use std::{ffi::CString, os::raw, ptr::null_mut};
+use std::os::unix::io::{IntoRawFd, OwnedFd};
 
 foreign_type! {
     type CType = binder_rpc_unstable_bindgen::ARpcServer;
@@ -41,27 +40,66 @@
 
 impl RpcServer {
     /// Creates a binder RPC server, serving the supplied binder service implementation on the given
-    /// vsock port.
-    pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
+    /// vsock port. Only connections from the given CID are accepted.
+    ///
+    // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
+    // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
+    pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
         let service = service.as_native_mut();
 
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
         // Plus the binder objects are threadsafe.
         unsafe {
-            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
+                service, cid, port,
+            ))
         }
     }
 
     /// Creates a binder RPC server, serving the supplied binder service implementation on the given
-    /// socket file name. The socket should be initialized in init.rc with the same name.
-    pub fn new_init_unix_domain(
+    /// socket file descriptor. The socket should be bound to an address before calling this
+    /// function.
+    pub fn new_bound_socket(mut service: SpIBinder, socket_fd: OwnedFd) -> Result<RpcServer, Error> {
+        let service = service.as_native_mut();
+
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        // The server takes ownership of the socket FD.
+        unsafe {
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket(
+                service, socket_fd.into_raw_fd(),
+            ))
+        }
+    }
+
+    /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket
+    /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix
+    /// domain sockets, pass one to the server and the other to the client. Multiple client session
+    /// can be created from the client end of the pair.
+    pub fn new_unix_domain_bootstrap(
         mut service: SpIBinder,
-        socket_name: &str,
+        bootstrap_fd: OwnedFd,
     ) -> Result<RpcServer, Error> {
-        let socket_name = match CString::new(socket_name) {
+        let service = service.as_native_mut();
+
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        // The server takes ownership of the bootstrap FD.
+        unsafe {
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap(
+                service,
+                bootstrap_fd.into_raw_fd(),
+            ))
+        }
+    }
+
+    /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+    /// IP address and port.
+    pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> {
+        let address = match CString::new(address) {
             Ok(s) => s,
             Err(e) => {
-                log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
+                log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
                 return Err(Error::from(ErrorKind::InvalidInput));
             }
         };
@@ -70,9 +108,10 @@
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
         // Plus the binder objects are threadsafe.
         unsafe {
-            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain(
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet(
                 service,
-                socket_name.as_ptr(),
+                address.as_ptr(),
+                port,
             ))
         }
     }
@@ -86,6 +125,22 @@
 }
 
 impl RpcServerRef {
+    /// Sets the list of file descriptor transport modes supported by this server.
+    pub fn set_supported_file_descriptor_transport_modes(
+        &self,
+        modes: &[FileDescriptorTransportMode],
+    ) {
+        // SAFETY - Does not keep the pointer after returning does, nor does it
+        // read past its boundary. Only passes the 'self' pointer as an opaque handle.
+        unsafe {
+            binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
+                self.as_ptr(),
+                modes.as_ptr(),
+                modes.len(),
+            )
+        }
+    }
+
     /// Starts a new background thread and calls join(). Returns immediately.
     pub fn start(&self) {
         unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
@@ -99,51 +154,11 @@
 
     /// Shuts down the running RpcServer. Can be called multiple times and from
     /// multiple threads. Called automatically during drop().
-    pub fn shutdown(&self) {
-        unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) };
-    }
-}
-
-type RpcServerFactoryRef<'a> = &'a mut (dyn FnMut(u32) -> Option<SpIBinder> + Send + Sync);
-
-/// Runs a binder RPC server, using the given factory function to construct a binder service
-/// implementation for each connection.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_vsock_rpc_server_with_factory(
-    port: u32,
-    mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync,
-) -> bool {
-    // Double reference the factory because trait objects aren't FFI safe.
-    // NB: The type annotation is necessary to ensure that we have a `dyn` rather than an `impl`.
-    let mut factory_ref: RpcServerFactoryRef = &mut factory;
-    let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void;
-
-    // SAFETY: `factory_wrapper` is only ever called by `RunVsockRpcServerWithFactory`, with context
-    // taking the pointer value above (so a properly aligned non-null pointer to an initialized
-    // `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made
-    // after `RunVsockRpcServerWithFactory` returns).
-    unsafe {
-        binder_rpc_unstable_bindgen::RunVsockRpcServerWithFactory(
-            Some(factory_wrapper),
-            context,
-            port,
-        )
-    }
-}
-
-unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
-    // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
-    // `run_vsock_rpc_server_with_factory`, and we are still within the lifetime of the value it is
-    // pointing to.
-    let factory_ptr = context as *mut RpcServerFactoryRef;
-    let factory = factory_ptr.as_mut().unwrap();
-
-    if let Some(mut service) = factory(cid) {
-        service.as_native_mut()
-    } else {
-        null_mut()
+    pub fn shutdown(&self) -> Result<(), Error> {
+        if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
+            Ok(())
+        } else {
+            Err(Error::from(ErrorKind::UnexpectedEof))
+        }
     }
 }
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
new file mode 100644
index 0000000..28c5390
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use binder::unstable_api::new_spibinder;
+use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::ffi::CString;
+use std::os::{
+    raw::{c_int, c_void},
+    unix::io::{AsRawFd, BorrowedFd, RawFd},
+};
+
+pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
+
+foreign_type! {
+    type CType = binder_rpc_unstable_bindgen::ARpcSession;
+    fn drop = binder_rpc_unstable_bindgen::ARpcSession_free;
+
+    /// A type that represents a foreign instance of RpcSession.
+    #[derive(Debug)]
+    pub struct RpcSession;
+    /// A borrowed RpcSession.
+    pub struct RpcSessionRef;
+}
+
+/// SAFETY - The opaque handle can be cloned freely.
+unsafe impl Send for RpcSession {}
+/// SAFETY - The underlying C++ RpcSession class is thread-safe.
+unsafe impl Sync for RpcSession {}
+
+impl RpcSession {
+    /// Allocates a new RpcSession object.
+    pub fn new() -> RpcSession {
+        // SAFETY - Takes ownership of the returned handle, which has correct refcount.
+        unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
+    }
+}
+
+impl Default for RpcSession {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl RpcSessionRef {
+    /// Sets the file descriptor transport mode for this session.
+    pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
+        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        unsafe {
+            binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
+                self.as_ptr(),
+                mode,
+            )
+        };
+    }
+
+    /// Sets the maximum number of incoming threads.
+    pub fn set_max_incoming_threads(&self, threads: usize) {
+        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        unsafe {
+            binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
+        };
+    }
+
+    /// Sets the maximum number of outgoing connections.
+    pub fn set_max_outgoing_connections(&self, connections: usize) {
+        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        unsafe {
+            binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
+                self.as_ptr(),
+                connections,
+            )
+        };
+    }
+
+    /// Connects to an RPC Binder server over vsock for a particular interface.
+    pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
+        &self,
+        cid: u32,
+        port: u32,
+    ) -> Result<Strong<T>, StatusCode> {
+        // SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct
+        // reference count, and the ownership can safely be taken by new_spibinder.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient(
+                self.as_ptr(),
+                cid,
+                port,
+            ))
+        };
+        Self::get_interface(service)
+    }
+
+    /// Connects to an RPC Binder server over a names Unix Domain Socket for
+    /// a particular interface.
+    pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
+        &self,
+        socket_name: &str,
+    ) -> Result<Strong<T>, StatusCode> {
+        let socket_name = match CString::new(socket_name) {
+            Ok(s) => s,
+            Err(e) => {
+                log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
+                return Err(StatusCode::NAME_NOT_FOUND);
+            }
+        };
+
+        // SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct
+        // reference count, and the ownership can safely be taken by new_spibinder.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient(
+                self.as_ptr(),
+                socket_name.as_ptr(),
+            ))
+        };
+        Self::get_interface(service)
+    }
+
+    /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
+    /// for a particular interface.
+    pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
+        &self,
+        bootstrap_fd: BorrowedFd,
+    ) -> Result<Strong<T>, StatusCode> {
+        // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
+        // ownership of bootstrap_fd. The returned AIBinder has correct
+        // reference count, and the ownership can safely be taken by new_spibinder.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient(
+                self.as_ptr(),
+                bootstrap_fd.as_raw_fd(),
+            ))
+        };
+        Self::get_interface(service)
+    }
+
+    /// Connects to an RPC Binder server over inet socket at the given address and port.
+    pub fn setup_inet_client<T: FromIBinder + ?Sized>(
+        &self,
+        address: &str,
+        port: u32,
+    ) -> Result<Strong<T>, StatusCode> {
+        let address = match CString::new(address) {
+            Ok(s) => s,
+            Err(e) => {
+                log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
+                return Err(StatusCode::BAD_VALUE);
+            }
+        };
+
+        // SAFETY: AIBinder returned by ARpcSession_setupInet has correct reference
+        // count, and the ownership can safely be taken by new_spibinder.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupInet(
+                self.as_ptr(),
+                address.as_ptr(),
+                port,
+            ))
+        };
+        Self::get_interface(service)
+    }
+
+    /// Connects to an RPC Binder server, using the given callback to get (and
+    /// take ownership of) file descriptors already connected to it.
+    pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
+        &self,
+        mut request_fd: impl FnMut() -> Option<RawFd>,
+    ) -> Result<Strong<T>, StatusCode> {
+        // Double reference the factory because trait objects aren't FFI safe.
+        let mut request_fd_ref: RequestFd = &mut request_fd;
+        let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+
+        // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
+        // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
+        // of param, only passing it to request_fd_wrapper.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient(
+                self.as_ptr(),
+                Some(request_fd_wrapper),
+                param,
+            ))
+        };
+        Self::get_interface(service)
+    }
+
+    fn get_interface<T: FromIBinder + ?Sized>(
+        service: Option<SpIBinder>,
+    ) -> Result<Strong<T>, StatusCode> {
+        if let Some(service) = service {
+            FromIBinder::try_from(service)
+        } else {
+            Err(StatusCode::NAME_NOT_FOUND)
+        }
+    }
+}
+
+type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
+
+unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
+    // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
+    // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
+    // initialized instance.
+    let request_fd_ptr = param as *mut RequestFd;
+    let request_fd = request_fd_ptr.as_mut().unwrap();
+    request_fd().unwrap_or(-1)
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 976f54d..993bdca 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -100,22 +100,17 @@
 /// An interface can promise to be a stable vendor interface ([`Vintf`]), or
 /// makes no stability guarantees ([`Local`]). [`Local`] is
 /// currently the default stability.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
 pub enum Stability {
     /// Default stability, visible to other modules in the same compilation
     /// context (e.g. modules on system.img)
+    #[default]
     Local,
 
     /// A Vendor Interface Object, which promises to be stable
     Vintf,
 }
 
-impl Default for Stability {
-    fn default() -> Self {
-        Stability::Local
-    }
-}
-
 impl From<Stability> for i32 {
     fn from(stability: Stability) -> i32 {
         use Stability::*;
@@ -265,7 +260,14 @@
     /// 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.
+    /// This link only holds a weak reference to its recipient. If the
+    /// `DeathRecipient` is dropped then it will be unlinked.
+    ///
+    /// Note that the notifications won't work if you don't first start at least
+    /// one Binder thread by calling
+    /// [`ProcessState::start_thread_pool`](crate::ProcessState::start_thread_pool)
+    /// or
+    /// [`ProcessState::join_thread_pool`](crate::ProcessState::join_thread_pool).
     fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
 
     /// Remove a previously registered death notification.
@@ -1127,6 +1129,10 @@
         }
 
         impl $crate::binder_impl::Deserialize for $enum {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
+
             fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
                 parcel.read().map(Self)
             }
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index f6b09ed..ba26062 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -20,6 +20,7 @@
 use std::error;
 use std::ffi::{CStr, CString};
 use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+use std::ptr;
 use std::result;
 
 pub use sys::binder_status_t as status_t;
@@ -92,7 +93,7 @@
 /// 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);
+pub struct Status(ptr::NonNull<sys::AStatus>);
 
 // Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
 // duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
@@ -119,7 +120,7 @@
             // Rust takes ownership of the returned pointer.
             sys::AStatus_newOk()
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Create a status object from a service specific error
@@ -147,7 +148,7 @@
                 sys::AStatus_fromServiceSpecificError(err)
             }
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Creates a status object from a service specific error.
@@ -161,7 +162,7 @@
             let ptr = unsafe {
                 sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
             };
-            Self(ptr)
+            Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
         } else {
             exception.into()
         }
@@ -181,7 +182,7 @@
     ///
     /// 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)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Returns `true` if this status represents a successful transaction.
@@ -326,7 +327,7 @@
             // UNKNOWN_ERROR.
             sys::AStatus_fromStatus(status)
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
 
@@ -338,7 +339,7 @@
             // Unknown values will be coerced into EX_TRANSACTION_FAILED.
             sys::AStatus_fromExceptionCode(code as i32)
         };
-        Self(ptr)
+        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
 
@@ -367,7 +368,7 @@
             // 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);
+            sys::AStatus_delete(self.0.as_mut());
         }
     }
 }
@@ -381,11 +382,15 @@
 /// `Status` object is still alive.
 unsafe impl AsNative<sys::AStatus> for Status {
     fn as_native(&self) -> *const sys::AStatus {
-        self.0
+        self.0.as_ptr()
     }
 
     fn as_native_mut(&mut self) -> *mut sys::AStatus {
-        self.0
+        unsafe {
+            // Safety: The pointer will be valid here since `Status` always
+            // contains a valid and initialized pointer while it is alive.
+            self.0.as_mut()
+        }
     }
 }
 
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index a0e61d9..0c8b48f 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -94,14 +94,12 @@
 //! ```
 
 #[macro_use]
-mod proxy;
-
-#[macro_use]
 mod binder;
 mod binder_async;
 mod error;
 mod native;
 mod parcel;
+mod proxy;
 mod state;
 
 use binder_ndk_sys as sys;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 6f686fb..5557168 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -209,8 +209,8 @@
     }
 
     /// Mark this binder object with local stability, which is vendor if we are
-    /// building for the VNDK and system otherwise.
-    #[cfg(any(vendor_ndk, android_vndk))]
+    /// building for android_vendor and system otherwise.
+    #[cfg(android_vendor)]
     fn mark_local_stability(&mut self) {
         unsafe {
             // Safety: Self always contains a valid `AIBinder` pointer, so
@@ -220,8 +220,8 @@
     }
 
     /// Mark this binder object with local stability, which is vendor if we are
-    /// building for the VNDK and system otherwise.
-    #[cfg(not(any(vendor_ndk, android_vndk)))]
+    /// building for android_vendor and system otherwise.
+    #[cfg(not(android_vendor))]
     fn mark_local_stability(&mut self) {
         unsafe {
             // Safety: Self always contains a valid `AIBinder` pointer, so
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 53a24af..e4c568e 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -566,9 +566,6 @@
 impl<'a> ReadableSubParcel<'a> {
     /// Read a type that implements [`Deserialize`] from the sub-parcel.
     pub fn read<D: Deserialize>(&self) -> Result<D> {
-        // The caller should have checked this,
-        // but it can't hurt to double-check
-        assert!(self.has_more_data());
         D::deserialize(&self.parcel)
     }
 
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index de6d649..7fe37f3 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -132,6 +132,14 @@
 }
 
 impl Deserialize for ParcelFileDescriptor {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index c241e4d..5d8c11c 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, FromIBinder, Stability, Strong};
+use crate::binder::{AsNative, FromIBinder, Interface, Stability, Strong};
 use crate::error::{status_result, status_t, Result, Status, StatusCode};
 use crate::parcel::BorrowedParcel;
 use crate::proxy::SpIBinder;
@@ -22,8 +22,8 @@
 
 use std::convert::{TryFrom, TryInto};
 use std::ffi::c_void;
-use std::mem::{self, ManuallyDrop, MaybeUninit};
-use std::os::raw::{c_char, c_ulong};
+use std::mem::{self, ManuallyDrop};
+use std::os::raw::c_char;
 use std::ptr;
 use std::slice;
 
@@ -60,6 +60,26 @@
 /// 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 {
+    /// Type for the uninitialized value of this type. Will be either `Self`
+    /// if the type implements `Default`, `Option<Self>` otherwise.
+    type UninitType;
+
+    /// Assert at compile-time that `Self` and `Self::UninitType` have the same
+    /// size and alignment. This will either fail to compile or evaluate to `true`.
+    /// The only two macros that work here are `panic!` and `assert!`, so we cannot
+    /// use `assert_eq!`.
+    const ASSERT_UNINIT_SIZE_AND_ALIGNMENT: bool = {
+        assert!(std::mem::size_of::<Self>() == std::mem::size_of::<Self::UninitType>());
+        assert!(std::mem::align_of::<Self>() == std::mem::align_of::<Self::UninitType>());
+        true
+    };
+
+    /// Return an uninitialized or default-initialized value for this type.
+    fn uninit() -> Self::UninitType;
+
+    /// Convert an initialized value of type `Self` into `Self::UninitType`.
+    fn from_init(value: Self) -> Self::UninitType;
+
     /// Deserialize an instance from the given [`Parcel`].
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
 
@@ -103,12 +123,8 @@
 unsafe extern "C" fn serialize_element<T: Serialize>(
     parcel: *mut sys::AParcel,
     array: *const c_void,
-    index: c_ulong,
+    index: usize,
 ) -> status_t {
-    // c_ulong and usize are the same, but we need the explicitly sized version
-    // so the function signature matches what bindgen generates.
-    let index = index as usize;
-
     let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1);
 
     let mut parcel = match BorrowedParcel::from_raw(parcel) {
@@ -125,7 +141,7 @@
 pub trait DeserializeArray: Deserialize {
     /// Deserialize an array of type from the given parcel.
     fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
-        let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
+        let mut vec: Option<Vec<Self::UninitType>> = None;
         let res = unsafe {
             // Safety: Safe FFI, vec is the correct opaque type expected by
             // allocate_vec and deserialize_element.
@@ -140,8 +156,8 @@
         let vec: Option<Vec<Self>> = unsafe {
             // Safety: We are assuming that the NDK correctly initialized every
             // element of the vector by now, so we know that all the
-            // MaybeUninits are now properly initialized. We can transmute from
-            // Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> has the same
+            // UninitTypes are now properly initialized. We can transmute from
+            // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
             // alignment and size as T, so the pointer to the vector allocation
             // will be compatible.
             mem::transmute(vec)
@@ -153,18 +169,14 @@
 /// Callback to deserialize a parcelable element.
 ///
 /// The opaque array data pointer must be a mutable pointer to an
-/// `Option<Vec<MaybeUninit<T>>>` with at least enough elements for `index` to be valid
+/// `Option<Vec<T::UninitType>>` with at least enough elements for `index` to be valid
 /// (zero-based).
 unsafe extern "C" fn deserialize_element<T: Deserialize>(
     parcel: *const sys::AParcel,
     array: *mut c_void,
-    index: c_ulong,
+    index: usize,
 ) -> status_t {
-    // c_ulong and usize are the same, but we need the explicitly sized version
-    // so the function signature matches what bindgen generates.
-    let index = index as usize;
-
-    let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>);
+    let vec = &mut *(array as *mut Option<Vec<T::UninitType>>);
     let vec = match vec {
         Some(v) => v,
         None => return StatusCode::BAD_INDEX as status_t,
@@ -178,7 +190,7 @@
         Ok(e) => e,
         Err(code) => return code as status_t,
     };
-    ptr::write(vec[index].as_mut_ptr(), element);
+    vec[index] = T::from_init(element);
     StatusCode::OK as status_t
 }
 
@@ -241,15 +253,15 @@
 /// # Safety
 ///
 /// The opaque data pointer passed to the array read function must be a mutable
-/// pointer to an `Option<Vec<MaybeUninit<T>>>`. `buffer` will be assigned a mutable pointer
+/// pointer to an `Option<Vec<T::UninitType>>`. `buffer` will be assigned a mutable pointer
 /// to the allocated vector data if this function returns true.
-unsafe extern "C" fn allocate_vec_with_buffer<T>(
+unsafe extern "C" fn allocate_vec_with_buffer<T: Deserialize>(
     data: *mut c_void,
     len: i32,
     buffer: *mut *mut T,
 ) -> bool {
     let res = allocate_vec::<T>(data, len);
-    let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
+    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
     if let Some(new_vec) = vec {
         *buffer = new_vec.as_mut_ptr() as *mut T;
     }
@@ -261,20 +273,18 @@
 /// # Safety
 ///
 /// The opaque data pointer passed to the array read function must be a mutable
-/// pointer to an `Option<Vec<MaybeUninit<T>>>`.
-unsafe extern "C" fn allocate_vec<T>(data: *mut c_void, len: i32) -> bool {
-    let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
+/// pointer to an `Option<Vec<T::UninitType>>`.
+unsafe extern "C" fn allocate_vec<T: Deserialize>(data: *mut c_void, len: i32) -> bool {
+    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
     if len < 0 {
         *vec = None;
         return true;
     }
-    let mut new_vec: Vec<MaybeUninit<T>> = Vec::with_capacity(len as usize);
 
-    // Safety: We are filling the vector with uninitialized data here, but this
-    // is safe because the vector contains MaybeUninit elements which can be
-    // uninitialized. We're putting off the actual unsafe bit, transmuting the
-    // vector to a Vec<T> until the contents are initialized.
-    new_vec.set_len(len as usize);
+    // Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
+    let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
+    let mut new_vec: Vec<T::UninitType> = Vec::with_capacity(len as usize);
+    new_vec.resize_with(len as usize, T::uninit);
 
     ptr::write(vec, Some(new_vec));
     true
@@ -291,8 +301,11 @@
 }
 
 /// Safety: All elements in the vector must be properly initialized.
-unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> {
-    // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T>
+unsafe fn vec_assume_init<T: Deserialize>(vec: Vec<T::UninitType>) -> Vec<T> {
+    // Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
+    let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
+
+    // We can convert from Vec<T::UninitType> to Vec<T> because T::UninitType
     // has the same alignment and size as T, so the pointer to the vector
     // allocation will be compatible.
     let mut vec = ManuallyDrop::new(vec);
@@ -315,6 +328,9 @@
 
     {Deserialize, $ty:ty, $read_fn:path} => {
         impl Deserialize for $ty {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
             fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
                 let mut val = Self::default();
                 unsafe {
@@ -356,11 +372,11 @@
     {DeserializeArray, $ty:ty, $read_array_fn:path} => {
         impl DeserializeArray for $ty {
             fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
-                let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
+                let mut vec: Option<Vec<Self::UninitType>> = 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<MaybeUninit<T>>>`, so `&mut vec` is
+                    // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
                     // correct for it.
                     $read_array_fn(
                         parcel.as_native(),
@@ -372,7 +388,7 @@
                 let vec: Option<Vec<Self>> = unsafe {
                     // Safety: We are assuming that the NDK correctly
                     // initialized every element of the vector by now, so we
-                    // know that all the MaybeUninits are now properly
+                    // know that all the UninitTypes are now properly
                     // initialized.
                     vec.map(|vec| vec_assume_init(vec))
                 };
@@ -448,6 +464,14 @@
 }
 
 impl Deserialize for u8 {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         i8::deserialize(parcel).map(|v| v as u8)
     }
@@ -479,6 +503,14 @@
 }
 
 impl Deserialize for i16 {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         u16::deserialize(parcel).map(|v| v as i16)
     }
@@ -555,6 +587,14 @@
 }
 
 impl Deserialize for Option<String> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut vec: Option<Vec<u8>> = None;
         let status = unsafe {
@@ -583,6 +623,14 @@
 impl DeserializeArray for Option<String> {}
 
 impl Deserialize for String {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
@@ -619,6 +667,14 @@
 }
 
 impl<T: DeserializeArray> Deserialize for Vec<T> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         DeserializeArray::deserialize_array(parcel)
             .transpose()
@@ -648,6 +704,14 @@
 impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {}
 
 impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] {
+    type UninitType = [T::UninitType; N];
+    fn uninit() -> Self::UninitType {
+        [(); N].map(|_| T::uninit())
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value.map(T::from_init)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let vec = DeserializeArray::deserialize_array(parcel)
             .transpose()
@@ -672,6 +736,14 @@
 }
 
 impl Deserialize for Stability {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         i32::deserialize(parcel).and_then(Stability::try_from)
     }
@@ -690,6 +762,14 @@
 }
 
 impl Deserialize for Status {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut status_ptr = ptr::null_mut();
         let ret_status = unsafe {
@@ -725,12 +805,29 @@
 impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
 
 impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
+    type UninitType = Option<Strong<T>>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let ibinder: SpIBinder = parcel.read()?;
         FromIBinder::try_from(ibinder)
     }
 }
 
+struct AssertIBinder;
+impl Interface for AssertIBinder {}
+impl FromIBinder for AssertIBinder {
+    // This is only needed so we can assert on the size of Strong<AssertIBinder>
+    fn try_from(_: SpIBinder) -> Result<Strong<Self>> {
+        unimplemented!()
+    }
+}
+
 impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
     fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
         let ibinder: Option<SpIBinder> = parcel.read()?;
@@ -760,6 +857,14 @@
 }
 
 impl<T: DeserializeOption> Deserialize for Option<T> {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         DeserializeOption::deserialize_option(parcel)
     }
@@ -779,7 +884,13 @@
 #[macro_export]
 macro_rules! impl_serialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::binder_impl::Serialize for $parcelable {
+        $crate::impl_serialize_for_parcelable!($parcelable < >);
+    };
+    ($parcelable:ident < $( $param:ident ),* , >) => {
+        $crate::impl_serialize_for_parcelable!($parcelable < $($param),* >);
+    };
+    ($parcelable:ident < $( $param:ident ),* > ) => {
+        impl < $($param),* > $crate::binder_impl::Serialize for $parcelable < $($param),* > {
             fn serialize(
                 &self,
                 parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -788,9 +899,9 @@
             }
         }
 
-        impl $crate::binder_impl::SerializeArray for $parcelable {}
+        impl < $($param),* > $crate::binder_impl::SerializeArray for $parcelable < $($param),* > {}
 
-        impl $crate::binder_impl::SerializeOption for $parcelable {
+        impl < $($param),* > $crate::binder_impl::SerializeOption for $parcelable < $($param),* > {
             fn serialize_option(
                 this: Option<&Self>,
                 parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -816,7 +927,16 @@
 #[macro_export]
 macro_rules! impl_deserialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::binder_impl::Deserialize for $parcelable {
+        $crate::impl_deserialize_for_parcelable!($parcelable < >);
+    };
+    ($parcelable:ident < $( $param:ident ),* , >) => {
+        $crate::impl_deserialize_for_parcelable!($parcelable < $($param),* >);
+    };
+    ($parcelable:ident < $( $param:ident ),* > ) => {
+        impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType { Self::UninitType::default() }
+            fn from_init(value: Self) -> Self::UninitType { value }
             fn deserialize(
                 parcel: &$crate::binder_impl::BorrowedParcel<'_>,
             ) -> std::result::Result<Self, $crate::StatusCode> {
@@ -838,9 +958,9 @@
             }
         }
 
-        impl $crate::binder_impl::DeserializeArray for $parcelable {}
+        impl < $($param: Default),* > $crate::binder_impl::DeserializeArray for $parcelable < $($param),* > {}
 
-        impl $crate::binder_impl::DeserializeOption for $parcelable {
+        impl < $($param: Default),* > $crate::binder_impl::DeserializeOption for $parcelable < $($param),* > {
             fn deserialize_option(
                 parcel: &$crate::binder_impl::BorrowedParcel<'_>,
             ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
@@ -872,6 +992,14 @@
 }
 
 impl<T: Deserialize> Deserialize for Box<T> {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         Deserialize::deserialize(parcel).map(Box::new)
     }
@@ -896,6 +1024,7 @@
 
     #[test]
     fn test_custom_parcelable() {
+        #[derive(Default)]
         struct Custom(u32, bool, String, Vec<String>);
 
         impl Serialize for Custom {
@@ -908,6 +1037,14 @@
         }
 
         impl Deserialize for Custom {
+            type UninitType = Self;
+            fn uninit() -> Self::UninitType {
+                Self::UninitType::default()
+            }
+            fn from_init(value: Self) -> Self::UninitType {
+                value
+            }
+
             fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
                 Ok(Custom(
                     parcel.read()?,
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index c829d37..eb82fb7 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -161,6 +161,15 @@
     }
 }
 
+impl Clone for ParcelableHolder {
+    fn clone(&self) -> ParcelableHolder {
+        ParcelableHolder {
+            data: Mutex::new(self.data.lock().unwrap().clone()),
+            stability: self.stability,
+        }
+    }
+}
+
 impl Serialize for ParcelableHolder {
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
         parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
@@ -169,6 +178,14 @@
 }
 
 impl Deserialize for ParcelableHolder {
+    type UninitType = Self;
+    fn uninit() -> Self::UninitType {
+        Self::new(Default::default())
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        value
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
         let status: i32 = parcel.read()?;
         if status == NULL_PARCELABLE_FLAG {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 254efae..036f6b4 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -439,6 +439,14 @@
 impl SerializeArray for SpIBinder {}
 
 impl Deserialize for SpIBinder {
+    type UninitType = Option<Self>;
+    fn uninit() -> Self::UninitType {
+        Self::UninitType::default()
+    }
+    fn from_init(value: Self) -> Self::UninitType {
+        Some(value)
+    }
+
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> {
         parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
     }
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index cc18741..4886c5f 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -22,7 +22,18 @@
 pub struct ProcessState;
 
 impl ProcessState {
-    /// Start the Binder IPC thread pool
+    /// Starts the Binder IPC thread pool.
+    ///
+    /// Starts 1 thread, plus allows the kernel to lazily start up to
+    /// `num_threads` additional threads as specified by
+    /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
+    ///
+    /// This should be done before creating any Binder client or server. If
+    /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+    /// called, then some things (such as callbacks and
+    /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
+    /// not work: the callbacks will be queued but never called as there is no
+    /// thread to call them on.
     pub fn start_thread_pool() {
         unsafe {
             // Safety: Safe FFI
@@ -30,11 +41,12 @@
         }
     }
 
-    /// Set the maximum number of threads that can be started in the threadpool.
+    /// Sets 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.
+    /// By default, after [`start_thread_pool`](Self::start_thread_pool) is
+    /// called, this is 15. If it is called additional times, the thread pool
+    /// size can only be increased.
     pub fn set_thread_pool_max_thread_count(num_threads: u32) {
         unsafe {
             // Safety: Safe FFI
@@ -42,7 +54,13 @@
         }
     }
 
-    /// Block on the Binder IPC thread pool
+    /// Blocks on the Binder IPC thread pool by adding the current thread to the
+    /// pool.
+    ///
+    /// Note that this adds the current thread in addition to those that are
+    /// created by
+    /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count)
+    /// and [`start_thread_pool`](Self::start_thread_pool).
     pub fn join_thread_pool() {
         unsafe {
             // Safety: Safe FFI
diff --git a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
index 59ca6ed..663b9bb 100644
--- a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
+++ b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
@@ -54,14 +54,12 @@
     EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
 
     auto interface = aidl::IBinderRustNdkInteropTest::fromBinder(binder);
-    // TODO(b/167723746): this test requires that fromBinder allow association
-    // with an already associated local binder by treating it as remote.
-    EXPECT_EQ(interface, nullptr);
+    EXPECT_NE(interface, nullptr);
 
-    // std::string in("testing");
-    // std::string out;
-    // EXPECT_TRUE(interface->echo(in, &out).isOk());
-    // EXPECT_EQ(in, out);
+    std::string in("testing");
+    std::string out;
+    EXPECT_TRUE(interface->echo(in, &out).isOk());
+    EXPECT_EQ(in, out);
 }
 
 int main(int argc, char** argv) {
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index 28e0200..ac96823 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -21,5 +21,8 @@
             "waghpawan@google.com",
             "smoreland@google.com",
         ],
+        triage_assignee: "waghpawan@google.com",
+        // hotlist "AIDL fuzzers bugs" on buganizer
+        hotlists: ["4637097"],
     },
 }
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
index c5c7719..29bf92c 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -17,9 +17,6 @@
 #![allow(missing_docs)]
 #![no_main]
 
-#[macro_use]
-extern crate libfuzzer_sys;
-
 mod read_utils;
 
 use crate::read_utils::READ_FUNCS;
@@ -31,7 +28,7 @@
     StatusCode,
 };
 use binder_random_parcel_rs::create_random_parcel;
-use libfuzzer_sys::arbitrary::Arbitrary;
+use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target};
 
 #[derive(Arbitrary, Debug)]
 enum ReadOperation {
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
index 43a3094..5cac647 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
@@ -11,7 +11,6 @@
     source_stem: "bindings",
     visibility: [":__subpackages__"],
     bindgen_flags: [
-        "--size_t-is-usize",
         "--allowlist-function",
         "createRandomParcel",
         "--allowlist-function",
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 43e407c..2537ce0 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -29,5 +29,8 @@
             "waghpawan@google.com",
             "smoreland@google.com",
         ],
+        triage_assignee: "waghpawan@google.com",
+        // hotlist "AIDL fuzzers bugs" on buganizer
+        hotlists: ["4637097"],
     },
 }
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs
index a427f28..c530382 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs
@@ -16,8 +16,8 @@
 
 #![allow(missing_docs)]
 #![no_main]
-#[macro_use]
-extern crate libfuzzer_sys;
+
+use libfuzzer_sys::fuzz_target;
 
 use binder::{self, BinderFeatures, Interface};
 use binder_random_parcel_rs::fuzz_service;
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index d2bfde1..a2d48b6 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -16,9 +16,9 @@
 
 use binder::binder_impl::BorrowedParcel;
 use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
-use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
-use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
-use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
 
 macro_rules! read_parcel_interface {
     ($data_type:ty) => {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a999d59..41856f9 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -32,28 +32,8 @@
 }
 
 cc_test {
-    name: "binderDriverInterfaceTest_IPC_32",
-    defaults: ["binder_test_defaults"],
-    srcs: ["binderDriverInterfaceTest.cpp"],
-    header_libs: ["libbinder_headers"],
-    compile_multilib: "32",
-    multilib: {
-        lib32: {
-            suffix: "",
-        },
-    },
-    cflags: ["-DBINDER_IPC_32BIT=1"],
-    test_suites: ["vts"],
-}
-
-cc_test {
     name: "binderDriverInterfaceTest",
     defaults: ["binder_test_defaults"],
-    product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
-        },
-    },
     header_libs: ["libbinder_headers"],
     srcs: ["binderDriverInterfaceTest.cpp"],
     test_suites: [
@@ -62,30 +42,6 @@
     ],
 }
 
-cc_test {
-    name: "binderLibTest_IPC_32",
-    defaults: ["binder_test_defaults"],
-    srcs: ["binderLibTest.cpp"],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libutils",
-    ],
-    static_libs: [
-        "libgmock",
-    ],
-    compile_multilib: "32",
-    multilib: {
-        lib32: {
-            suffix: "",
-        },
-    },
-    cflags: ["-DBINDER_IPC_32BIT=1"],
-    test_suites: ["vts"],
-    require_root: true,
-}
-
 // unit test only, which can run on host and doesn't use /dev/binder
 cc_test {
     name: "binderUnitTest",
@@ -100,6 +56,7 @@
         "binderBinderUnitTest.cpp",
         "binderStatusUnitTest.cpp",
         "binderMemoryHeapBaseUnitTest.cpp",
+        "binderRecordedTransactionTest.cpp",
     ],
     shared_libs: [
         "libbinder",
@@ -110,13 +67,39 @@
 }
 
 cc_test {
-    name: "binderLibTest",
-    defaults: ["binder_test_defaults"],
-    product_variables: {
-        binder32bit: {
-            cflags: ["-DBINDER_IPC_32BIT=1"],
+    name: "binderRecordReplayTest",
+    srcs: ["binderRecordReplayTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+    static_libs: [
+        "binderRecordReplayTestIface-cpp",
+        "binderReadParcelIface-cpp",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
+aidl_interface {
+    name: "binderRecordReplayTestIface",
+    unstable: true,
+    srcs: [
+        "IBinderRecordReplayTest.aidl",
+    ],
+    imports: ["binderReadParcelIface"],
+    backend: {
+        java: {
+            enabled: true,
+            platform_apis: true,
         },
     },
+}
+
+cc_test {
+    name: "binderLibTest",
+    defaults: ["binder_test_defaults"],
 
     srcs: ["binderLibTest.cpp"],
     shared_libs: [
@@ -137,6 +120,7 @@
 
 aidl_interface {
     name: "binderRpcTestIface",
+    vendor_available: true,
     host_supported: true,
     unstable: true,
     srcs: [
@@ -158,6 +142,7 @@
 cc_library_static {
     name: "libbinder_tls_test_utils",
     host_supported: true,
+    vendor_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -235,6 +220,13 @@
         "binderRpcUniversalTests.cpp",
     ],
 
+    // This test uses a lot of resources and takes a long time. Due to
+    // design of several tests, it is also very sensitive to resource
+    // contention on the device. b/276820894
+    test_options: {
+        unit_test: false,
+    },
+
     test_suites: ["general-tests"],
     require_root: true,
 
@@ -266,6 +258,7 @@
     name: "binderRpcTest_static_defaults",
 
     shared_libs: [
+        "liblog",
         "libutils",
         // libcrypto_static is not visible to this module
         "libcrypto",
@@ -273,7 +266,6 @@
     static_libs: [
         "libbase",
         "libcutils",
-        "liblog",
         "libssl",
     ],
 
@@ -335,6 +327,54 @@
     ],
 }
 
+cc_binary {
+    name: "binderRpcTestService_on_trusty_mock",
+    defaults: [
+        "trusty_mock_defaults",
+    ],
+
+    srcs: [
+        "binderRpcTestCommon.cpp",
+        "binderRpcTestServiceTrusty.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder_on_trusty_mock",
+        "libbase",
+        "libutils",
+        "libcutils",
+    ],
+
+    static_libs: [
+        "binderRpcTestIface-cpp",
+    ],
+}
+
+cc_binary {
+    name: "binderRpcTest_on_trusty_mock",
+    defaults: [
+        "trusty_mock_defaults",
+    ],
+
+    srcs: [
+        "binderRpcUniversalTests.cpp",
+        "binderRpcTestCommon.cpp",
+        "binderRpcTestTrusty.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder_on_trusty_mock",
+        "libbase",
+        "libutils",
+        "libcutils",
+    ],
+
+    static_libs: [
+        "binderRpcTestIface-cpp",
+        "libgtest",
+    ],
+}
+
 cc_test {
     name: "binderRpcTest",
     defaults: [
@@ -346,6 +386,8 @@
     // Add the Trusty mock library as a fake dependency so it gets built
     required: [
         "libbinder_on_trusty_mock",
+        "binderRpcTestService_on_trusty_mock",
+        "binderRpcTest_on_trusty_mock",
     ],
 }
 
@@ -390,6 +432,37 @@
 }
 
 cc_test {
+    name: "binderRpcToTrustyTest",
+    vendor: true,
+    host_supported: false,
+    defaults: [
+        "binderRpcTest_common_defaults",
+        "binderRpcTest_static_defaults",
+    ],
+
+    srcs: [
+        "binderRpcTest.cpp",
+        "binderRpcTestCommon.cpp",
+        "binderRpcUniversalTests.cpp",
+    ],
+
+    cflags: [
+        "-DBINDER_RPC_TO_TRUSTY_TEST",
+    ],
+
+    static_libs: [
+        // We want to link libbinder statically so we can push the binary
+        // to the device for testing independently of the library
+        "libbinder_rpc_no_kernel",
+        "libbinder_trusty",
+        "libtrusty",
+    ],
+
+    test_suites: ["device-tests"],
+    require_root: true,
+}
+
+cc_test {
     name: "RpcTlsUtilsTest",
     host_supported: true,
     target: {
@@ -625,6 +698,7 @@
         "liblog",
         "libutils",
     ],
+    test_suites: ["general-tests"],
 }
 
 cc_test_host {
@@ -727,3 +801,15 @@
         hotlists: ["4637097"],
     },
 }
+
+cc_defaults {
+    name: "fuzzer_disable_leaks",
+    fuzz_config: {
+        asan_options: [
+            "detect_leaks=0",
+        ],
+        hwasan_options: [
+            "detect_leaks=0",
+        ],
+    },
+}
diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl
new file mode 100644
index 0000000..bd6b03c
--- /dev/null
+++ b/libs/binder/tests/IBinderRecordReplayTest.aidl
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 parcelables.SingleDataParcelable;
+
+interface IBinderRecordReplayTest {
+    void setByte(byte input);
+    byte getByte();
+
+    void setChar(char input);
+    char getChar();
+
+    void setBoolean(boolean input);
+    boolean getBoolean();
+
+    void setInt(int input);
+    int getInt();
+
+    void setFloat(float input);
+    float getFloat();
+
+    void setLong(long input);
+    long getLong();
+
+    void setDouble(double input);
+    double getDouble();
+
+    void setString(String input);
+    String getString();
+
+    void setSingleDataParcelable(in SingleDataParcelable p);
+    SingleDataParcelable getSingleDataParcelable();
+
+    void setByteArray(in byte[] input);
+    byte[] getByteArray();
+
+    void setCharArray(in char[] input);
+    char[] getCharArray();
+
+    void setBooleanArray(in boolean[] input);
+    boolean[] getBooleanArray();
+
+    void setIntArray(in int[] input);
+    int[] getIntArray();
+
+    void setFloatArray(in float[] input);
+    float[] getFloatArray();
+
+    void setLongArray(in long[] input);
+    long[] getLongArray();
+
+    void setDoubleArray(in double[] input);
+    double[] getDoubleArray();
+
+    void setStringArray(in String[] input);
+    String[] getStringArray();
+
+    void setSingleDataParcelableArray(in SingleDataParcelable[] input);
+    SingleDataParcelable[] getSingleDataParcelableArray();
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index a3ed571..1164767 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -80,4 +80,8 @@
     // get queued.
     oneway void blockingSendFdOneway(in ParcelFileDescriptor fd);
     ParcelFileDescriptor blockingRecvFd();
+
+    // Same as blockingSendFdOneway, but with integers.
+    oneway void blockingSendIntOneway(int n);
+    int blockingRecvInt();
 }
diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h
deleted file mode 100644
index 369b55d..0000000
--- a/libs/binder/tests/binderAbiHelper.h
+++ /dev/null
@@ -1,52 +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 <stdlib.h>
-#include <iostream>
-
-#ifdef BINDER_IPC_32BIT
-static constexpr bool kBuild32Abi = true;
-#else
-static constexpr bool kBuild32Abi = false;
-#endif
-
-// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported
-static inline bool ReadKernelConfigIs32BitAbi() {
-    // failure case implies we run with standard ABI
-    return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\"");
-}
-
-static inline void ExitIfWrongAbi() {
-    bool runtime32Abi = ReadKernelConfigIs32BitAbi();
-
-    if (kBuild32Abi != runtime32Abi) {
-        std::cout << "[==========] Running 1 test from 1 test suite." << std::endl;
-        std::cout << "[----------] Global test environment set-up." << std::endl;
-        std::cout << "[----------] 1 tests from BinderLibTest" << std::endl;
-        std::cout << "[ RUN      ] BinderTest.AbortForWrongAbi" << std::endl;
-        std::cout << "[ INFO     ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl;
-        std::cout << "[       OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl;
-        std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl;
-        std::cout << "" << std::endl;
-        std::cout << "[----------] Global test environment tear-down" << std::endl;
-        std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl;
-        std::cout << "[  PASSED  ] 1 tests." << std::endl;
-        exit(0);
-    }
-}
-
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 55a3916..bc40864 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -172,6 +172,28 @@
     a_binder->pingBinder();
 }
 
+TEST(BinderAllocation, InterfaceDescriptorTransaction) {
+    sp<IBinder> a_binder = GetRemoteBinder();
+
+    size_t mallocs = 0;
+    const auto on_malloc = OnMalloc([&](size_t bytes) {
+        mallocs++;
+        // Happens to be SM package length. We could switch to forking
+        // and registering our own service if it became an issue.
+#if defined(__LP64__)
+        EXPECT_EQ(bytes, 78);
+#else
+        EXPECT_EQ(bytes, 70);
+#endif
+    });
+
+    a_binder->getInterfaceDescriptor();
+    a_binder->getInterfaceDescriptor();
+    a_binder->getInterfaceDescriptor();
+
+    EXPECT_EQ(mallocs, 1);
+}
+
 TEST(BinderAllocation, SmallTransaction) {
     String16 empty_descriptor = String16("");
     sp<IServiceManager> manager = defaultServiceManager();
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 8cc3054..cf23a46 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -25,8 +25,6 @@
 #include <sys/mman.h>
 #include <poll.h>
 
-#include "binderAbiHelper.h"
-
 #define BINDER_DEV_NAME "/dev/binder"
 
 testing::Environment* binder_env;
@@ -362,8 +360,7 @@
     binderTestReadEmpty();
 }
 
-int main(int argc, char **argv) {
-    ExitIfWrongAbi();
+int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
 
     binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 464da60..77a5fa8 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -66,7 +66,7 @@
 void initHostRpcServiceManagerOnce() {
     static std::once_flag gSmOnce;
     std::call_once(gSmOnce, [] {
-        setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+        setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
     });
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 25b524f..e021af0 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -48,7 +48,6 @@
 #include <sys/un.h>
 
 #include "../binder_module.h"
-#include "binderAbiHelper.h"
 
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
@@ -83,7 +82,7 @@
 static constexpr int kSchedPolicy = SCHED_RR;
 static constexpr int kSchedPriority = 7;
 static constexpr int kSchedPriorityMore = 8;
-static constexpr int kKernelThreads = 15;
+static constexpr int kKernelThreads = 17; // anything different than the default
 
 static String16 binderLibTestServiceName = String16("test.binderLib");
 
@@ -120,6 +119,7 @@
     BINDER_LIB_TEST_CAN_GET_SID,
     BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
     BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+    BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
     BINDER_LIB_TEST_LOCK_UNLOCK,
     BINDER_LIB_TEST_PROCESS_LOCK,
     BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -506,7 +506,13 @@
     }
 
     EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+
+    // b/268232063 - succeeds ~0.08% of the time
+    {
+        auto ret = IPCThreadState::self()->freeze(pid, true, 0);
+        EXPECT_TRUE(ret == -EAGAIN || ret == OK);
+    }
+
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
     EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 
@@ -1351,17 +1357,20 @@
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
                 StatusEq(NO_ERROR));
     int32_t replyi = reply.readInt32();
-    // Expect 16 threads: kKernelThreads = 15 + Pool thread == 16
-    EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1);
+    // see getThreadPoolMaxTotalThreadCount for why there is a race
+    EXPECT_TRUE(replyi == kKernelThreads + 1 || replyi == kKernelThreads + 2) << replyi;
+
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_LOCK, data, &reply), NO_ERROR);
 
     /*
-     * This will use all threads in the pool expect the main pool thread.
-     * The service should run fine without locking, and the thread count should
-     * not exceed 16 (15 Max + pool thread).
+     * This will use all threads in the pool but one. There are actually kKernelThreads+2
+     * available in the other process (startThreadPool, joinThreadPool, + the kernel-
+     * started threads from setThreadPoolMaxThreadCount
+     *
+     * Adding one more will cause it to deadlock.
      */
     std::vector<std::thread> ts;
-    for (size_t i = 0; i < kKernelThreads; i++) {
+    for (size_t i = 0; i < kKernelThreads + 1; i++) {
         ts.push_back(std::thread([&] {
             Parcel local_reply;
             EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply),
@@ -1369,8 +1378,13 @@
         }));
     }
 
-    data.writeInt32(100);
-    // Give a chance for all threads to be used
+    // make sure all of the above calls will be queued in parallel. Otherwise, most of
+    // the time, the below call will pre-empt them (presumably because we have the
+    // scheduler timeslice already + scheduler hint).
+    sleep(1);
+
+    data.writeInt32(1000);
+    // Give a chance for all threads to be used (kKernelThreads + 1 thread in use)
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
 
     for (auto &t : ts) {
@@ -1380,7 +1394,15 @@
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
                 StatusEq(NO_ERROR));
     replyi = reply.readInt32();
-    EXPECT_EQ(replyi, kKernelThreads + 1);
+    EXPECT_EQ(replyi, kKernelThreads + 2);
+}
+
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+    Parcel data, reply;
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+    EXPECT_TRUE(reply.readBool());
 }
 
 size_t epochMillis() {
@@ -1397,9 +1419,11 @@
     ASSERT_TRUE(server != nullptr);
     int32_t delay = 1000; // ms
     data.writeInt32(delay);
+    // b/266537959 - must take before taking lock, since countdown is started in the remote
+    // process there.
+    size_t epochMsBefore = epochMillis();
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR);
     std::vector<std::thread> ts;
-    size_t epochMsBefore = epochMillis();
     for (size_t i = 0; i < kKernelThreads + 1; i++) {
         ts.push_back(std::thread([&] {
             Parcel local_reply;
@@ -1849,6 +1873,10 @@
                 reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+                reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_PROCESS_LOCK: {
                 m_blockMutex.lock();
                 return NO_ERROR;
@@ -2001,9 +2029,7 @@
     return 1; /* joinThreadPool should not return */
 }
 
-int main(int argc, char **argv) {
-    ExitIfWrongAbi();
-
+int main(int argc, char** argv) {
     if (argc == 4 && !strcmp(argv[1], "--servername")) {
         binderservername = argv[2];
     } else {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 359c783..0a0dae0 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -29,6 +29,7 @@
 using android::status_t;
 using android::String16;
 using android::String8;
+using android::base::unique_fd;
 using android::binder::Status;
 
 TEST(Parcel, NonNullTerminatedString8) {
@@ -112,6 +113,166 @@
     EXPECT_EQ(ret[1], STDIN_FILENO);
 }
 
+TEST(Parcel, AppendFromEmpty) {
+    Parcel p1;
+    Parcel p2;
+    p2.writeInt32(2);
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(2, p1.readInt32());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendPlainData) {
+    Parcel p1;
+    p1.writeInt32(1);
+    Parcel p2;
+    p2.writeInt32(2);
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_EQ(2, p1.readInt32());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendPlainDataPartial) {
+    Parcel p1;
+    p1.writeInt32(1);
+    Parcel p2;
+    p2.writeInt32(2);
+    p2.writeInt32(3);
+    p2.writeInt32(4);
+
+    // only copy 8 bytes (two int32's worth)
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8));
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_EQ(2, p1.readInt32());
+    ASSERT_EQ(3, p1.readInt32());
+    ASSERT_EQ(0, p1.readInt32()); // not 4, end of Parcel
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendWithBinder) {
+    sp<IBinder> b1 = sp<BBinder>::make();
+    sp<IBinder> b2 = sp<BBinder>::make();
+
+    Parcel p1;
+    p1.writeInt32(1);
+    p1.writeStrongBinder(b1);
+    Parcel p2;
+    p2.writeInt32(2);
+    p2.writeStrongBinder(b2);
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_EQ(b1, p1.readStrongBinder());
+    ASSERT_EQ(2, p1.readInt32());
+    ASSERT_EQ(b2, p1.readStrongBinder());
+    ASSERT_EQ(2, p1.objectsCount());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+    ASSERT_EQ(b2, p2.readStrongBinder());
+}
+
+TEST(Parcel, AppendWithBinderPartial) {
+    sp<IBinder> b1 = sp<BBinder>::make();
+    sp<IBinder> b2 = sp<BBinder>::make();
+
+    Parcel p1;
+    p1.writeInt32(1);
+    p1.writeStrongBinder(b1);
+    Parcel p2;
+    p2.writeInt32(2);
+    p2.writeStrongBinder(b2);
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into strong binder
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_EQ(b1, p1.readStrongBinder());
+    ASSERT_EQ(2, p1.readInt32());
+    ASSERT_EQ(1935813253, p1.readInt32()); // whatever garbage that is there (ABI)
+    ASSERT_EQ(1, p1.objectsCount());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+    ASSERT_EQ(b2, p2.readStrongBinder());
+}
+
+TEST(Parcel, AppendWithFd) {
+    unique_fd fd1 = unique_fd(dup(0));
+    unique_fd fd2 = unique_fd(dup(0));
+
+    Parcel p1;
+    p1.writeInt32(1);
+    p1.writeDupFileDescriptor(0);      // with ownership
+    p1.writeFileDescriptor(fd1.get()); // without ownership
+    Parcel p2;
+    p2.writeInt32(2);
+    p2.writeDupFileDescriptor(0);      // with ownership
+    p2.writeFileDescriptor(fd2.get()); // without ownership
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_EQ(2, p1.readInt32());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_EQ(4, p1.objectsCount());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+}
+
+TEST(Parcel, AppendWithFdPartial) {
+    unique_fd fd1 = unique_fd(dup(0));
+    unique_fd fd2 = unique_fd(dup(0));
+
+    Parcel p1;
+    p1.writeInt32(1);
+    p1.writeDupFileDescriptor(0);      // with ownership
+    p1.writeFileDescriptor(fd1.get()); // without ownership
+    Parcel p2;
+    p2.writeInt32(2);
+    p2.writeDupFileDescriptor(0);      // with ownership
+    p2.writeFileDescriptor(fd2.get()); // without ownership
+
+    ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into binder
+
+    p1.setDataPosition(0);
+    ASSERT_EQ(1, p1.readInt32());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_EQ(2, p1.readInt32());
+    ASSERT_EQ(1717840517, p1.readInt32()); // whatever garbage that is there (ABI)
+    ASSERT_EQ(2, p1.objectsCount());
+
+    p2.setDataPosition(0);
+    ASSERT_EQ(2, p2.readInt32());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+    ASSERT_NE(-1, p1.readFileDescriptor());
+}
+
 // Tests a second operation results in a parcel at the same location as it
 // started.
 void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
new file mode 100644
index 0000000..17d5c8a
--- /dev/null
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <BnBinderRecordReplayTest.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/RecordedTransaction.h>
+#include <gtest/gtest.h>
+
+#include <sys/prctl.h>
+
+#include "parcelables/SingleDataParcelable.h"
+
+using namespace android;
+using android::binder::Status;
+using android::binder::debug::RecordedTransaction;
+using parcelables::SingleDataParcelable;
+
+const String16 kServerName = String16("binderRecordReplay");
+
+#define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \
+    Status set##name(T input) {                   \
+        m##name = input;                          \
+        return Status::ok();                      \
+    }                                             \
+                                                  \
+    Status get##name(T* output) {                 \
+        *output = m##name;                        \
+        return Status::ok();                      \
+    }                                             \
+    T m##name
+
+#define GENERATE_GETTER_SETTER(name, T) \
+    Status set##name(const T& input) {  \
+        m##name = input;                \
+        return Status::ok();            \
+    }                                   \
+                                        \
+    Status get##name(T* output) {       \
+        *output = m##name;              \
+        return Status::ok();            \
+    }                                   \
+    T m##name
+
+class MyRecordReplay : public BnBinderRecordReplayTest {
+public:
+    GENERATE_GETTER_SETTER_PRIMITIVE(Boolean, bool);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Byte, int8_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Int, int);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Char, char16_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Long, int64_t);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Float, float);
+    GENERATE_GETTER_SETTER_PRIMITIVE(Double, double);
+
+    GENERATE_GETTER_SETTER(String, String16);
+    GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable);
+
+    GENERATE_GETTER_SETTER(BooleanArray, std::vector<bool>);
+    GENERATE_GETTER_SETTER(ByteArray, std::vector<uint8_t>);
+    GENERATE_GETTER_SETTER(IntArray, std::vector<int>);
+    GENERATE_GETTER_SETTER(CharArray, std::vector<char16_t>);
+    GENERATE_GETTER_SETTER(LongArray, std::vector<int64_t>);
+    GENERATE_GETTER_SETTER(FloatArray, std::vector<float>);
+    GENERATE_GETTER_SETTER(DoubleArray, std::vector<double>);
+    GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>);
+    GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
+};
+
+class BinderRecordReplayTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        // get the remote service
+        auto binder = defaultServiceManager()->getService(kServerName);
+        ASSERT_NE(nullptr, binder);
+        mInterface = interface_cast<IBinderRecordReplayTest>(binder);
+        mBpBinder = binder->remoteBinder();
+        ASSERT_NE(nullptr, mBpBinder);
+    }
+
+    template <typename T, typename U>
+    void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue,
+                      Status (IBinderRecordReplayTest::*get)(U*), U changedValue) {
+        base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
+                                O_RDWR | O_CREAT | O_CLOEXEC, 0666));
+        ASSERT_TRUE(fd.ok());
+
+        // record a transaction
+        mBpBinder->startRecordingBinder(fd);
+        auto status = (*mInterface.*set)(recordedValue);
+        EXPECT_TRUE(status.isOk());
+        mBpBinder->stopRecordingBinder();
+
+        // test transaction does the thing we expect it to do
+        U output;
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(output, recordedValue);
+
+        // write over the existing state
+        status = (*mInterface.*set)(changedValue);
+        EXPECT_TRUE(status.isOk());
+
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+
+        EXPECT_EQ(output, changedValue);
+
+        // replay transaction
+        ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
+        std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
+        ASSERT_NE(transaction, std::nullopt);
+
+        // TODO: move logic to replay RecordedTransaction into RecordedTransaction
+        Parcel data;
+        data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
+        auto result =
+                mBpBinder->transact(transaction->getCode(), data, nullptr, transaction->getFlags());
+
+        // make sure recording does the thing we expect it to do
+        EXPECT_EQ(OK, result);
+
+        status = (*mInterface.*get)(&output);
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(output, recordedValue);
+    }
+
+private:
+    sp<BpBinder> mBpBinder;
+    sp<IBinderRecordReplayTest> mInterface;
+};
+
+TEST_F(BinderRecordReplayTest, ReplayByte) {
+    recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte,
+                 int8_t{90});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayBoolean) {
+    recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean,
+                 false);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayChar) {
+    recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'},
+                 &IBinderRecordReplayTest::getChar, char16_t{'K'});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayInt) {
+    recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayFloat) {
+    recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat,
+                 22.0f);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayLong) {
+    recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55},
+                 &IBinderRecordReplayTest::getLong, int64_t{1LL << 12});
+}
+
+TEST_F(BinderRecordReplayTest, ReplayDouble) {
+    recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble,
+                 1.11);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayString) {
+    const ::android::String16& input1 = String16("This is saved string");
+    const ::android::String16& input2 = String16("This is changed string");
+    recordReplay(&IBinderRecordReplayTest::setString, input1, &IBinderRecordReplayTest::getString,
+                 input2);
+}
+
+TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelable) {
+    SingleDataParcelable saved, changed;
+    saved.data = 3;
+    changed.data = 5;
+    recordReplay(&IBinderRecordReplayTest::setSingleDataParcelable, saved,
+                 &IBinderRecordReplayTest::getSingleDataParcelable, changed);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayByteArray) {
+    std::vector<uint8_t> savedArray = {uint8_t{255}, uint8_t{0}, uint8_t{127}};
+    std::vector<uint8_t> changedArray = {uint8_t{2}, uint8_t{7}, uint8_t{117}};
+    recordReplay(&IBinderRecordReplayTest::setByteArray, savedArray,
+                 &IBinderRecordReplayTest::getByteArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayBooleanArray) {
+    std::vector<bool> savedArray = {true, false, true};
+    std::vector<bool> changedArray = {false, true, false};
+    recordReplay(&IBinderRecordReplayTest::setBooleanArray, savedArray,
+                 &IBinderRecordReplayTest::getBooleanArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayCharArray) {
+    std::vector<char16_t> savedArray = {char16_t{'G'}, char16_t{'L'}, char16_t{'K'}, char16_t{'T'}};
+    std::vector<char16_t> changedArray = {char16_t{'X'}, char16_t{'Y'}, char16_t{'Z'}};
+    recordReplay(&IBinderRecordReplayTest::setCharArray, savedArray,
+                 &IBinderRecordReplayTest::getCharArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayIntArray) {
+    std::vector<int> savedArray = {12, 45, 178};
+    std::vector<int> changedArray = {32, 14, 78, 1899};
+    recordReplay(&IBinderRecordReplayTest::setIntArray, savedArray,
+                 &IBinderRecordReplayTest::getIntArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayFloatArray) {
+    std::vector<float> savedArray = {12.14f, 45.56f, 123.178f};
+    std::vector<float> changedArray = {0.00f, 14.0f, 718.1f, 1899.122f, 3268.123f};
+    recordReplay(&IBinderRecordReplayTest::setFloatArray, savedArray,
+                 &IBinderRecordReplayTest::getFloatArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayLongArray) {
+    std::vector<int64_t> savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}};
+    std::vector<int64_t> changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33},
+                                         int64_t{1LL << 62}};
+    recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray,
+                 &IBinderRecordReplayTest::getLongArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayDoubleArray) {
+    std::vector<double> savedArray = {12.1412313, 45.561232, 123.1781111};
+    std::vector<double> changedArray = {0.00111, 14.32130, 712312318.19, 1899212.122,
+                                        322168.122123};
+    recordReplay(&IBinderRecordReplayTest::setDoubleArray, savedArray,
+                 &IBinderRecordReplayTest::getDoubleArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplayStringArray) {
+    std::vector<String16> savedArray = {String16("This is saved value"), String16(),
+                                        String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")};
+
+    std::vector<String16> changedArray = {String16("This is changed value"),
+                                          String16("\xF0\x90\x90\xB7\xE2\x82\xAC")};
+    recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray,
+                 &IBinderRecordReplayTest::getStringArray, changedArray);
+}
+
+TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) {
+    SingleDataParcelable s1, s2, s3, s4, s5;
+    s1.data = 5213;
+    s2.data = 1512;
+    s3.data = 4233;
+    s4.data = 123124;
+    s5.data = 0;
+    std::vector<SingleDataParcelable> saved = {s1, s2, s3};
+    std::vector<SingleDataParcelable> changed = {s4, s5};
+
+    recordReplay(&IBinderRecordReplayTest::setSingleDataParcelableArray, saved,
+                 &IBinderRecordReplayTest::getSingleDataParcelableArray, changed);
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+        auto server = sp<MyRecordReplay>::make();
+        android::defaultServiceManager()->addService(kServerName, server.get());
+
+        IPCThreadState::self()->joinThreadPool(true);
+        exit(1); // should not reach
+    }
+
+    // not racey, but getService sleeps for 1s
+    usleep(100000);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
new file mode 100644
index 0000000..30172cc
--- /dev/null
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/RecordedTransaction.h>
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+using android::Parcel;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+TEST(BinderRecordedTransaction, RoundTripEncoding) {
+    android::String16 interfaceName("SampleInterface");
+    Parcel d;
+    d.writeInt32(12);
+    d.writeInt64(2);
+    Parcel r;
+    r.writeInt32(99);
+    timespec ts = {1232456, 567890};
+
+    auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0);
+    EXPECT_TRUE(transaction.has_value());
+
+    auto file = std::tmpfile();
+    auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+    status_t status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+
+    std::rewind(file);
+
+    auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+    EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName).c_str());
+    EXPECT_EQ(retrievedTransaction->getCode(), 1);
+    EXPECT_EQ(retrievedTransaction->getFlags(), 42);
+    EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
+    EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec);
+    EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12);
+    EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4);
+    EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
+    EXPECT_EQ(retrievedTransaction->getVersion(), 0);
+
+    EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12);
+    EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2);
+    EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
+}
+
+TEST(BinderRecordedTransaction, Checksum) {
+    android::String16 interfaceName("SampleInterface");
+    Parcel d;
+    d.writeInt32(12);
+    d.writeInt64(2);
+    Parcel r;
+    r.writeInt32(99);
+    timespec ts = {1232456, 567890};
+    auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0);
+
+    auto file = std::tmpfile();
+    auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+    status_t status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+
+    lseek(fd.get(), 9, SEEK_SET);
+    uint32_t badData = 0xffffffff;
+    write(fd.get(), &badData, sizeof(uint32_t));
+    std::rewind(file);
+
+    auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+    EXPECT_FALSE(retrievedTransaction.has_value());
+}
+
+TEST(BinderRecordedTransaction, PayloadsExceedPageBoundaries) {
+    // File contents are read with mmap.
+    // This test verifies that transactions are read from portions
+    // of files that cross page boundaries and don't start at a
+    // page boundary offset of the fd.
+    const size_t pageSize = sysconf(_SC_PAGE_SIZE);
+    const size_t largeDataSize = pageSize + 100;
+    std::vector<uint8_t> largePayload;
+    uint8_t filler = 0xaa;
+    largePayload.insert(largePayload.end(), largeDataSize, filler);
+    android::String16 interfaceName("SampleInterface");
+    Parcel d;
+    d.writeInt32(12);
+    d.writeInt64(2);
+    d.writeByteVector(largePayload);
+    Parcel r;
+    r.writeInt32(99);
+    timespec ts = {1232456, 567890};
+    auto transaction = RecordedTransaction::fromDetails(interfaceName, 1, 42, ts, d, r, 0);
+
+    auto file = std::tmpfile();
+    auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+    // Write to file twice
+    status_t status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+    status = transaction->dumpToFile(fd);
+    ASSERT_EQ(android::NO_ERROR, status);
+
+    std::rewind(file);
+
+    for (int i = 0; i < 2; i++) {
+        auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+        EXPECT_EQ(retrievedTransaction->getCode(), 1);
+        EXPECT_EQ(retrievedTransaction->getFlags(), 42);
+        EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
+        EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec);
+        EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), d.dataSize());
+        EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4);
+        EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
+        EXPECT_EQ(retrievedTransaction->getVersion(), 0);
+
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12);
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2);
+        std::optional<std::vector<uint8_t>> payloadOut;
+        EXPECT_EQ(retrievedTransaction->getDataParcel().readByteVector(&payloadOut), android::OK);
+        EXPECT_EQ(payloadOut.value(), largePayload);
+
+        EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
+    }
+}
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 52ba9b0..9c96c41 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -102,9 +102,11 @@
 }
 
 static sp<RpcSession> gSession = RpcSession::make();
+static sp<IBinder> gRpcBinder;
 // Certificate validation happens during handshake and does not affect the result of benchmarks.
 // Skip certificate validation to simplify the setup process.
 static sp<RpcSession> gSessionTls = RpcSession::make(makeFactoryTls());
+static sp<IBinder> gRpcTlsBinder;
 #ifdef __BIONIC__
 static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
 static sp<IBinder> gKernelBinder;
@@ -118,21 +120,42 @@
             return gKernelBinder;
 #endif
         case RPC:
-            return gSession->getRootObject();
+            return gRpcBinder;
         case RPC_TLS:
-            return gSessionTls->getRootObject();
+            return gRpcTlsBinder;
         default:
             LOG(FATAL) << "Unknown transport value: " << transport;
             return nullptr;
     }
 }
 
+static void SetLabel(benchmark::State& state) {
+    Transport transport = static_cast<Transport>(state.range(0));
+    switch (transport) {
+#ifdef __BIONIC__
+        case KERNEL:
+            state.SetLabel("kernel");
+            break;
+#endif
+        case RPC:
+            state.SetLabel("rpc");
+            break;
+        case RPC_TLS:
+            state.SetLabel("rpc_tls");
+            break;
+        default:
+            LOG(FATAL) << "Unknown transport value: " << transport;
+    }
+}
+
 void BM_pingTransaction(benchmark::State& state) {
     sp<IBinder> binder = getBinderForOptions(state);
 
     while (state.KeepRunning()) {
         CHECK_EQ(OK, binder->pingBinder());
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_pingTransaction)->ArgsProduct({kTransportList});
 
@@ -162,6 +185,8 @@
         Status ret = iface->repeatString(str, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_repeatTwoPageString)->ArgsProduct({kTransportList});
 
@@ -180,6 +205,8 @@
         Status ret = iface->repeatBytes(bytes, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_throughputForTransportAndBytes)
         ->ArgsProduct({kTransportList,
@@ -199,6 +226,8 @@
         Status ret = iface->repeatBinder(binder, &out);
         CHECK(ret.isOk()) << ret;
     }
+
+    SetLabel(state);
 }
 BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList});
 
@@ -226,11 +255,6 @@
     ::benchmark::Initialize(&argc, argv);
     if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
 
-    std::cerr << "Tests suffixes:" << std::endl;
-    std::cerr << "\t.../" << Transport::KERNEL << " is KERNEL" << std::endl;
-    std::cerr << "\t.../" << Transport::RPC << " is RPC" << std::endl;
-    std::cerr << "\t.../" << Transport::RPC_TLS << " is RPC with TLS" << std::endl;
-
 #ifdef __BIONIC__
     if (0 == fork()) {
         prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
@@ -254,11 +278,13 @@
     (void)unlink(addr.c_str());
     forkRpcServer(addr.c_str(), RpcServer::make(RpcTransportCtxFactoryRaw::make()));
     setupClient(gSession, addr.c_str());
+    gRpcBinder = gSession->getRootObject();
 
     std::string tlsAddr = tmp + "/binderRpcTlsBenchmark";
     (void)unlink(tlsAddr.c_str());
     forkRpcServer(tlsAddr.c_str(), RpcServer::make(makeFactoryTls()));
     setupClient(gSessionTls, tlsAddr.c_str());
+    gRpcTlsBinder = gSessionTls->getRootObject();
 
     ::benchmark::RunSpecifiedBenchmarks();
     return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 02aa45f..d352ce5 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <aidl/IBinderRpcTest.h>
 #include <android-base/stringprintf.h>
 
 #include <chrono>
@@ -27,6 +28,11 @@
 #include <sys/prctl.h>
 #include <sys/socket.h>
 
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+#endif // BINDER_RPC_TO_TRUSTY_TEST
+
 #include "binderRpcTestCommon.h"
 #include "binderRpcTestFixture.h"
 
@@ -44,6 +50,10 @@
 constexpr bool kEnableSharedLibs = true;
 #endif
 
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
+#endif
+
 static std::string WaitStatusToString(int wstatus) {
     if (WIFEXITED(wstatus)) {
         return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -119,7 +129,7 @@
 static std::string allocateSocketAddress() {
     static size_t id = 0;
     std::string temp = getenv("TMPDIR") ?: "/tmp";
-    auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+    auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++);
     unlink(ret.c_str());
     return ret;
 };
@@ -153,7 +163,8 @@
             session.root = nullptr;
         }
 
-        for (auto& info : sessions) {
+        for (size_t sessionNum = 0; sessionNum < sessions.size(); sessionNum++) {
+            auto& info = sessions.at(sessionNum);
             sp<RpcSession>& session = info.session;
 
             EXPECT_NE(nullptr, session);
@@ -169,6 +180,7 @@
             for (size_t i = 0; i < 3; i++) {
                 sp<RpcSession> strongSession = weakSession.promote();
                 EXPECT_EQ(nullptr, strongSession)
+                        << "For session " << sessionNum << ". "
                         << (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
                             "Leaked sess: ")
                         << strongSession->getStrongCount() << " checked time " << i;
@@ -202,6 +214,7 @@
     return serverFd;
 }
 
+#ifndef BINDER_RPC_TO_TRUSTY_TEST
 static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
     base::unique_fd sockClient, sockServer;
     if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
@@ -220,18 +233,10 @@
     }
     return std::move(sockClient);
 }
+#endif // BINDER_RPC_TO_TRUSTY_TEST
 
-std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
-    auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
-    auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
-            std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
-    if (singleThreaded) {
-        ret += "_single_threaded";
-    }
-    if (noKernel) {
-        ret += "_no_kernel";
-    }
-    return ret;
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+    return newTlsFactory(rpcSecurity);
 }
 
 // This creates a new process serving an interface on a certain number of
@@ -240,6 +245,10 @@
         const BinderRpcOptions& options) {
     CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
 
+    if (options.numIncomingConnectionsBySession.size() != 0) {
+        CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
+    }
+
     SocketType socketType = std::get<0>(GetParam());
     RpcSecurity rpcSecurity = std::get<1>(GetParam());
     uint32_t clientVersion = std::get<2>(GetParam());
@@ -269,6 +278,11 @@
 
     auto ret = std::make_unique<LinuxProcessSession>(
             Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+                if (socketType == SocketType::TIPC) {
+                    // Trusty has a single persistent service
+                    return;
+                }
+
                 auto writeFd = std::to_string(writeEnd.get());
                 auto readFd = std::to_string(readEnd.get());
                 execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
@@ -287,39 +301,61 @@
         serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
                 static_cast<int32_t>(mode));
     }
-    writeToFd(ret->host.writeEnd(), serverConfig);
+    if (socketType != SocketType::TIPC) {
+        writeToFd(ret->host.writeEnd(), serverConfig);
+    }
 
     std::vector<sp<RpcSession>> sessions;
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
     for (size_t i = 0; i < options.numSessions; i++) {
-        sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
+        std::unique_ptr<RpcTransportCtxFactory> factory;
+        if (socketType == SocketType::TIPC) {
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+            factory = RpcTransportCtxFactoryTipcAndroid::make();
+#else
+            LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
+#endif
+        } else {
+            factory = newTlsFactory(rpcSecurity, certVerifier);
+        }
+        sessions.emplace_back(RpcSession::make(std::move(factory)));
     }
 
-    auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
-    BinderRpcTestClientInfo clientInfo;
-    for (const auto& session : sessions) {
-        auto& parcelableCert = clientInfo.certs.emplace_back();
-        parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
-    }
-    writeToFd(ret->host.writeEnd(), clientInfo);
+    BinderRpcTestServerInfo serverInfo;
+    if (socketType != SocketType::TIPC) {
+        serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
+        BinderRpcTestClientInfo clientInfo;
+        for (const auto& session : sessions) {
+            auto& parcelableCert = clientInfo.certs.emplace_back();
+            parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+        }
+        writeToFd(ret->host.writeEnd(), clientInfo);
 
-    CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
-    if (socketType == SocketType::INET) {
-        CHECK_NE(0, serverInfo.port);
-    }
+        CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+        if (socketType == SocketType::INET) {
+            CHECK_NE(0, serverInfo.port);
+        }
 
-    if (rpcSecurity == RpcSecurity::TLS) {
-        const auto& serverCert = serverInfo.cert.data;
-        CHECK_EQ(OK,
-                 certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
+        if (rpcSecurity == RpcSecurity::TLS) {
+            const auto& serverCert = serverInfo.cert.data;
+            CHECK_EQ(OK,
+                     certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+                                                             serverCert));
+        }
     }
 
     status_t status;
 
-    for (const auto& session : sessions) {
+    for (size_t i = 0; i < sessions.size(); i++) {
+        const auto& session = sessions.at(i);
+
+        size_t numIncoming = options.numIncomingConnectionsBySession.size() > 0
+                ? options.numIncomingConnectionsBySession.at(i)
+                : 0;
+
         CHECK(session->setProtocolVersion(clientVersion));
-        session->setMaxIncomingThreads(options.numIncomingConnections);
-        session->setMaxOutgoingThreads(options.numOutgoingConnections);
+        session->setMaxIncomingThreads(numIncoming);
+        session->setMaxOutgoingConnections(options.numOutgoingConnections);
         session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
 
         switch (socketType) {
@@ -342,6 +378,26 @@
             case SocketType::INET:
                 status = session->setupInetClient("127.0.0.1", serverInfo.port);
                 break;
+            case SocketType::TIPC:
+                status = session->setupPreconnectedClient({}, [=]() {
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+                    auto port = trustyIpcPort(serverVersion);
+                    for (size_t i = 0; i < 5; i++) {
+                        // Try to connect several times,
+                        // in case the service is slow to start
+                        int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+                        if (tipcFd >= 0) {
+                            return android::base::unique_fd(tipcFd);
+                        }
+                        usleep(50000);
+                    }
+                    return android::base::unique_fd();
+#else
+                    LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor");
+                    return android::base::unique_fd();
+#endif
+                });
+                break;
             default:
                 LOG_ALWAYS_FATAL("Unknown socket type");
         }
@@ -372,12 +428,12 @@
         ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); }));
     }
 
-    usleep(10000); // give chance for calls on other threads
+    usleep(100000); // give chance for calls on other threads
 
     // other calls still work
     EXPECT_EQ(OK, proc.rootBinder->pingBinder());
 
-    constexpr size_t blockTimeMs = 50;
+    constexpr size_t blockTimeMs = 100;
     size_t epochMsBefore = epochMillis();
     // after this, we should never see a response within this time
     EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs));
@@ -391,8 +447,7 @@
     for (auto& t : ts) t.join();
 }
 
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
-                                        size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
     size_t epochMsBefore = epochMillis();
 
     std::vector<std::thread> ts;
@@ -406,8 +461,11 @@
 
     EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
 
-    // Potential flake, but make sure calls are handled in parallel.
-    EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
+    // Potential flake, but make sure calls are handled in parallel. Due
+    // to past flakes, this only checks that the amount of time taken has
+    // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested
+    // check this more exactly.
+    EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs);
 }
 
 TEST_P(BinderRpc, ThreadPoolOverSaturated) {
@@ -418,7 +476,9 @@
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+
+    // b/272429574 - below 500ms, the test fails
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -431,7 +491,9 @@
     constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
     auto proc = createRpcTestSocketServerProcess(
             {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+
+    // b/272429574 - below 500ms, the test fails
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadingStressTest) {
@@ -439,9 +501,9 @@
         GTEST_SKIP() << "This test requires multiple threads";
     }
 
-    constexpr size_t kNumClientThreads = 10;
-    constexpr size_t kNumServerThreads = 10;
-    constexpr size_t kNumCalls = 100;
+    constexpr size_t kNumClientThreads = 5;
+    constexpr size_t kNumServerThreads = 5;
+    constexpr size_t kNumCalls = 50;
 
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
 
@@ -500,6 +562,8 @@
         GTEST_SKIP() << "This test requires multiple threads";
     }
 
+    constexpr size_t kNumServerThreads = 3;
+
     // This test forces a oneway transaction to be queued by issuing two
     // `blockingSendFdOneway` calls, then drains the queue by issuing two
     // `blockingRecvFd` calls.
@@ -508,7 +572,7 @@
     // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
 
     auto proc = createRpcTestSocketServerProcess({
-            .numThreads = 3,
+            .numThreads = kNumServerThreads,
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
                     {RpcSession::FileDescriptorTransportMode::UNIX},
@@ -529,6 +593,8 @@
     EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
     CHECK(android::base::ReadFdToString(fdB.get(), &result));
     EXPECT_EQ(result, "b");
+
+    saturateThreadPool(kNumServerThreads, proc.rootIface);
 }
 
 TEST_P(BinderRpc, OnewayCallQueueing) {
@@ -536,30 +602,22 @@
         GTEST_SKIP() << "This test requires multiple threads";
     }
 
-    constexpr size_t kNumSleeps = 10;
+    constexpr size_t kNumQueued = 10;
     constexpr size_t kNumExtraServerThreads = 4;
-    constexpr size_t kSleepMs = 50;
 
     // make sure calls to the same object happen on the same thread
     auto proc = createRpcTestSocketServerProcess({.numThreads = 1 + kNumExtraServerThreads});
 
-    EXPECT_OK(proc.rootIface->lock());
-
-    size_t epochMsBefore = epochMillis();
-
-    // all these *Async commands should be queued on the server sequentially,
+    // all these *Oneway commands should be queued on the server sequentially,
     // even though there are multiple threads.
-    for (size_t i = 0; i + 1 < kNumSleeps; i++) {
-        proc.rootIface->sleepMsAsync(kSleepMs);
+    for (size_t i = 0; i + 1 < kNumQueued; i++) {
+        proc.rootIface->blockingSendIntOneway(i);
     }
-    EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
-
-    // this can only return once the final async call has unlocked
-    EXPECT_OK(proc.rootIface->lockUnlock());
-
-    size_t epochMsAfter = epochMillis();
-
-    EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+    for (size_t i = 0; i + 1 < kNumQueued; i++) {
+        int n;
+        proc.rootIface->blockingRecvInt(&n);
+        EXPECT_EQ(n, i);
+    }
 
     saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
 }
@@ -608,6 +666,38 @@
     proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
 }
 
+TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
+    // session 0 - will check for leaks in destrutor of proc
+    // session 1 - we want to make sure it gets deleted when we drop all references to it
+    auto proc = createRpcTestSocketServerProcess(
+            {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2});
+
+    wp<RpcSession> session = proc.proc->sessions.at(1).session;
+
+    // remove all references to the second session
+    proc.proc->sessions.at(1).root = nullptr;
+    proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
+
+    // TODO(b/271830568) more efficient way to wait for other incoming threadpool
+    // to drain commands.
+    for (size_t i = 0; i < 100; i++) {
+        usleep(10 * 1000);
+        if (session.promote() == nullptr) break;
+    }
+
+    EXPECT_EQ(nullptr, session.promote());
+
+    // now that it has died, wait for the remote session to shutdown
+    std::vector<int32_t> remoteCounts;
+    do {
+        EXPECT_OK(proc.rootIface->countBinders(&remoteCounts));
+    } while (remoteCounts.size() > 1);
+}
+
 TEST_P(BinderRpc, SingleDeathRecipient) {
     if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
@@ -625,7 +715,7 @@
 
     // Death recipient needs to have an incoming connection to be called
     auto proc = createRpcTestSocketServerProcess(
-            {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+            {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
 
     auto dr = sp<MyDeathRec>::make();
     ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -638,6 +728,10 @@
     ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; }));
 
     // need to wait for the session to shutdown so we don't "Leak session"
+    // can't do this before checking the death recipient by calling
+    // forceShutdown earlier, because shutdownAndWait will also trigger
+    // a death recipient, but if we had a way to wait for the service
+    // to gracefully shutdown, we could use that here.
     EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
     proc.expectAlreadyShutdown = true;
 }
@@ -659,7 +753,7 @@
 
     // Death recipient needs to have an incoming connection to be called
     auto proc = createRpcTestSocketServerProcess(
-            {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+            {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
 
     auto dr = sp<MyDeathRec>::make();
     EXPECT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -682,18 +776,20 @@
     proc.expectAlreadyShutdown = true;
 }
 
-TEST_P(BinderRpc, DeathRecipientFatalWithoutIncoming) {
+TEST_P(BinderRpc, DeathRecipientFailsWithoutIncoming) {
+    if (socketType() == SocketType::TIPC) {
+        // This should work, but Trusty takes too long to restart the service
+        GTEST_SKIP() << "Service death test not supported on Trusty";
+    }
     class MyDeathRec : public IBinder::DeathRecipient {
     public:
         void binderDied(const wp<IBinder>& /* who */) override {}
     };
 
-    auto proc = createRpcTestSocketServerProcess(
-            {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 0});
+    auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 1});
 
     auto dr = sp<MyDeathRec>::make();
-    EXPECT_DEATH(proc.rootBinder->linkToDeath(dr, (void*)1, 0),
-                 "Cannot register a DeathRecipient without any incoming connections.");
+    EXPECT_EQ(INVALID_OPERATION, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
 }
 
 TEST_P(BinderRpc, UnlinkDeathRecipient) {
@@ -709,22 +805,21 @@
 
     // Death recipient needs to have an incoming connection to be called
     auto proc = createRpcTestSocketServerProcess(
-            {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+            {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
 
     auto dr = sp<MyDeathRec>::make();
     ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
     ASSERT_EQ(OK, proc.rootBinder->unlinkToDeath(dr, (void*)1, 0, nullptr));
 
-    if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
-        EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
-    }
-
-    // need to wait for the session to shutdown so we don't "Leak session"
-    EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
-    proc.expectAlreadyShutdown = true;
+    proc.forceShutdown();
 }
 
 TEST_P(BinderRpc, Die) {
+    if (socketType() == SocketType::TIPC) {
+        // This should work, but Trusty takes too long to restart the service
+        GTEST_SKIP() << "Service death test not supported on Trusty";
+    }
+
     for (bool doDeathCleanup : {true, false}) {
         auto proc = createRpcTestSocketServerProcess({});
 
@@ -777,6 +872,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportRejectNone) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
             .serverSupportedFileDescriptorTransportModes =
@@ -793,6 +892,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -809,6 +912,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
             .serverSupportedFileDescriptorTransportModes =
@@ -822,6 +929,10 @@
 }
 
 TEST_P(BinderRpc, ReceiveFile) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -842,6 +953,10 @@
 }
 
 TEST_P(BinderRpc, SendFiles) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -914,6 +1029,10 @@
 }
 
 TEST_P(BinderRpc, AppendInvalidFd) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -940,6 +1059,7 @@
     ASSERT_EQ(-1, pRaw.readFileDescriptor());
 }
 
+#ifndef __ANDROID_VENDOR__ // No AIBinder_fromPlatformBinder on vendor
 TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
     if constexpr (!kEnableSharedLibs) {
         GTEST_SKIP() << "Test disabled because Binder was built as a static library";
@@ -971,6 +1091,7 @@
     ASSERT_TRUE(status.isOk()) << status.getDescription();
     ASSERT_EQ("aoeuaoeu", out);
 }
+#endif // __ANDROID_VENDOR__
 
 ssize_t countFds() {
     DIR* dir = opendir("/proc/self/fd/");
@@ -986,6 +1107,9 @@
     if (serverSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
     }
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
 
     ssize_t beforeFds = countFds();
     ASSERT_GE(beforeFds, 0);
@@ -996,12 +1120,26 @@
     ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
 }
 
-static bool testSupportVsockLoopback() {
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+                        ::testing::Combine(::testing::Values(SocketType::TIPC),
+                                           ::testing::Values(RpcSecurity::RAW),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::Values(true), ::testing::Values(true)),
+                        BinderRpc::PrintParamInfo);
+#else // BINDER_RPC_TO_TRUSTY_TEST
+bool testSupportVsockLoopback() {
     // We don't need to enable TLS to know if vsock is supported.
     unsigned int vsockPort = allocateVsockPort();
 
     android::base::unique_fd serverFd(
             TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+
+    if (errno == EAFNOSUPPORT) {
+        return false;
+    }
+
     LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
 
     sockaddr_vm serverAddr{
@@ -1091,7 +1229,15 @@
 
     if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
 
+#ifdef __BIONIC__
+    // Devices may not have vsock support. AVF tests will verify whether they do, but
+    // we can't require it due to old kernels for the time being.
     static bool hasVsockLoopback = testSupportVsockLoopback();
+#else
+    // On host machines, we always assume we have vsock loopback. If we don't, the
+    // subsequent failures will be more clear than showing one now.
+    static bool hasVsockLoopback = true;
+#endif
 
     if (hasVsockLoopback) {
         ret.push_back(SocketType::VSOCK);
@@ -1100,15 +1246,6 @@
     return ret;
 }
 
-static std::vector<uint32_t> testVersions() {
-    std::vector<uint32_t> versions;
-    for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) {
-        versions.push_back(i);
-    }
-    versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-    return versions;
-}
-
 INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
                         ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
                                            ::testing::ValuesIn(RpcSecurityValues()),
@@ -1128,7 +1265,7 @@
     };
 
     auto [isStrong1, isStrong2, rpcSecurity] = GetParam();
-    auto server = RpcServer::make(newFactory(rpcSecurity));
+    auto server = RpcServer::make(newTlsFactory(rpcSecurity));
     auto binder1 = sp<BBinder>::make();
     IBinder* binderRaw1 = binder1.get();
     setRootObject(isStrong1)(server.get(), binder1);
@@ -1224,7 +1361,7 @@
 class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> {
 public:
     static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
-        return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
+        return std::string(newTlsFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
                 std::to_string(std::get<1>(info.param));
     }
 };
@@ -1232,8 +1369,8 @@
 TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
     base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     int sinkFd = sink.get();
-    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_FALSE(server->hasServer());
     ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
     ASSERT_TRUE(server->hasServer());
@@ -1248,8 +1385,8 @@
     }
 
     auto addr = allocateSocketAddress();
-    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
     auto joinEnds = std::make_shared<OneOffSignal>();
 
@@ -1297,8 +1434,10 @@
                 const Param& param,
                 std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
             auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
-            auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
-            rpcServer->setProtocolVersion(serverVersion);
+            auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
+            if (!rpcServer->setProtocolVersion(serverVersion)) {
+                return AssertionFailure() << "Invalid protocol version: " << serverVersion;
+            }
             switch (socketType) {
                 case SocketType::PRECONNECTED: {
                     return AssertionFailure() << "Not supported by this test";
@@ -1342,7 +1481,7 @@
                 } break;
                 case SocketType::VSOCK: {
                     auto port = allocateVsockPort();
-                    auto status = rpcServer->setupVsockServer(port);
+                    auto status = rpcServer->setupVsockServer(VMADDR_CID_LOCAL, port);
                     if (status != OK) {
                         return AssertionFailure() << "setupVsockServer: " << statusToString(status);
                     }
@@ -1369,11 +1508,14 @@
                               addr, port);
                         return base::unique_fd{};
                     };
-                }
+                } break;
+                case SocketType::TIPC: {
+                    LOG_ALWAYS_FATAL("RpcTransportTest should not be enabled for TIPC");
+                } break;
             }
             mFd = rpcServer->releaseServer();
             if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
-            mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
+            mCtx = newTlsFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
             if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
             mSetup = true;
             return AssertionSuccess();
@@ -1478,7 +1620,7 @@
             auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
             (void)serverVersion;
             mFdTrigger = FdTrigger::make();
-            mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
+            mCtx = newTlsFactory(rpcSecurity, mCertVerifier)->newClientCtx();
             if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
             return AssertionSuccess();
         }
@@ -1550,7 +1692,7 @@
     using Client = RpcTransportTestUtils::Client;
     static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
         auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param;
-        auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+        auto ret = PrintToString(socketType) + "_" + newTlsFactory(rpcSecurity)->toCString();
         if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
         ret += "_serverV" + std::to_string(serverVersion);
         return ret;
@@ -1883,6 +2025,7 @@
                          testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER),
                          testing::ValuesIn(testVersions())),
         RpcTransportTlsKeyTest::PrintParamInfo);
+#endif // BINDER_RPC_TO_TRUSTY_TEST
 
 } // namespace android
 
diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp
index 0d9aa95..fe9a5a1 100644
--- a/libs/binder/tests/binderRpcTestCommon.cpp
+++ b/libs/binder/tests/binderRpcTestCommon.cpp
@@ -19,6 +19,6 @@
 namespace android {
 
 std::atomic<int32_t> MyBinderRpcSession::gNum;
-sp<IBinder> MyBinderRpcTest::mHeldBinder;
+sp<IBinder> MyBinderRpcTestBase::mHeldBinder;
 
 } // namespace android
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 654e16c..c1364dd 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -22,37 +22,42 @@
 #include <BnBinderRpcCallback.h>
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
-#include <aidl/IBinderRpcTest.h>
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcThreads.h>
+#include <binder/RpcTransport.h>
+#include <binder/RpcTransportRaw.h>
+#include <unistd.h>
+#include <cinttypes>
+#include <string>
+#include <vector>
+
+#ifndef __TRUSTY__
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_libbinder.h>
-#include <binder/Binder.h>
-#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
-#include <binder/RpcServer.h>
-#include <binder/RpcSession.h>
-#include <binder/RpcThreads.h>
 #include <binder/RpcTlsTestUtils.h>
 #include <binder/RpcTlsUtils.h>
-#include <binder/RpcTransport.h>
-#include <binder/RpcTransportRaw.h>
 #include <binder/RpcTransportTls.h>
-#include <unistd.h>
-#include <string>
-#include <vector>
 
 #include <signal.h>
 
-#include "../BuildFlags.h"
-#include "../FdTrigger.h"
 #include "../OS.h"               // for testing UnixBootstrap clients
 #include "../RpcSocketAddress.h" // for testing preconnected clients
-#include "../RpcState.h"         // for debugging
 #include "../vm_sockets.h"       // for VMADDR_*
+#endif                           // __TRUSTY__
+
+#include "../BuildFlags.h"
+#include "../FdTrigger.h"
+#include "../RpcState.h" // for debugging
 #include "utils/Errors.h"
 
 namespace android {
@@ -65,6 +70,19 @@
     return {RpcSecurity::RAW, RpcSecurity::TLS};
 }
 
+static inline std::vector<uint32_t> testVersions() {
+    std::vector<uint32_t> versions;
+    for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) {
+        versions.push_back(i);
+    }
+    versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+    return versions;
+}
+
+static inline std::string trustyIpcPort(uint32_t serverVersion) {
+    return base::StringPrintf("com.android.trusty.binderRpcTestService.V%" PRIu32, serverVersion);
+}
+
 enum class SocketType {
     PRECONNECTED,
     UNIX,
@@ -72,6 +90,7 @@
     UNIX_RAW,
     VSOCK,
     INET,
+    TIPC,
 };
 
 static inline std::string PrintToString(SocketType socketType) {
@@ -88,6 +107,8 @@
             return "vm_socket";
         case SocketType::INET:
             return "inet_socket";
+        case SocketType::TIPC:
+            return "trusty_ipc";
         default:
             LOG_ALWAYS_FATAL("Unknown socket type");
             return "";
@@ -105,7 +126,11 @@
 struct BinderRpcOptions {
     size_t numThreads = 1;
     size_t numSessions = 1;
-    size_t numIncomingConnections = 0;
+    // right now, this can be empty, or length numSessions, where each value
+    // represents the info for the corresponding session, but we should
+    // probably switch this to be a list of sessions options so that other
+    // options can all be specified per session
+    std::vector<size_t> numIncomingConnectionsBySession = {};
     size_t numOutgoingConnections = SIZE_MAX;
     RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode =
             RpcSession::FileDescriptorTransportMode::NONE;
@@ -118,6 +143,7 @@
     bool allowConnectFailure = false;
 };
 
+#ifndef __TRUSTY__
 static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
     uint64_t length = str.length();
     CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
@@ -148,7 +174,7 @@
     return object;
 }
 
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+static inline std::unique_ptr<RpcTransportCtxFactory> newTlsFactory(
         RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
         std::unique_ptr<RpcAuth> auth = nullptr) {
     switch (rpcSecurity) {
@@ -182,6 +208,7 @@
     }).detach();
     return readFd;
 }
+#endif // __TRUSTY__
 
 // A threadsafe channel where writes block until the value is read.
 template <typename T>
@@ -252,9 +279,12 @@
     std::vector<std::string> mValues;
 };
 
-class MyBinderRpcTest : public BnBinderRpcTest {
+// Base class for all concrete implementations of MyBinderRpcTest.
+// Sub-classes that want to provide a full implementation should derive
+// from this class instead of MyBinderRpcTestDefault below so the compiler
+// checks that all methods are implemented.
+class MyBinderRpcTestBase : public BnBinderRpcTest {
 public:
-    wp<RpcServer> server;
     int port = 0;
 
     Status sendString(const std::string& str) override {
@@ -269,18 +299,6 @@
         *out = port;
         return Status::ok();
     }
-    Status countBinders(std::vector<int32_t>* out) override {
-        sp<RpcServer> spServer = server.promote();
-        if (spServer == nullptr) {
-            return Status::fromExceptionCode(Status::EX_NULL_POINTER);
-        }
-        out->clear();
-        for (auto session : spServer->listSessions()) {
-            size_t count = session->state()->countBinders();
-            out->push_back(count);
-        }
-        return Status::ok();
-    }
     Status getNullBinder(sp<IBinder>* out) override {
         out->clear();
         return Status::ok();
@@ -381,63 +399,62 @@
         return doCallback(callback, oneway, delayed, value);
     }
 
-    Status die(bool cleanup) override {
-        if (cleanup) {
-            exit(1);
-        } else {
-            _exit(1);
-        }
-    }
-
-    Status scheduleShutdown() override {
-        sp<RpcServer> strongServer = server.promote();
-        if (strongServer == nullptr) {
+protected:
+    // Generic version of countBinders that works with both
+    // RpcServer and RpcServerTrusty
+    template <typename T>
+    Status countBindersImpl(const wp<T>& server, std::vector<int32_t>* out) {
+        sp<T> spServer = server.promote();
+        if (spServer == nullptr) {
             return Status::fromExceptionCode(Status::EX_NULL_POINTER);
         }
-        RpcMaybeThread([=] {
-            LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
-        }).detach();
-        return Status::ok();
-    }
-
-    Status useKernelBinderCallingId() override {
-        // this is WRONG! It does not make sense when using RPC binder, and
-        // because it is SO wrong, and so much code calls this, it should abort!
-
-        if constexpr (kEnableKernelIpc) {
-            (void)IPCThreadState::self()->getCallingPid();
+        out->clear();
+        for (auto session : spServer->listSessions()) {
+            size_t count = session->state()->countBinders();
+            out->push_back(count);
         }
         return Status::ok();
     }
+};
 
-    Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override {
-        out->reset(mockFileDescriptor(content));
-        return Status::ok();
+// Default implementation of MyBinderRpcTest that can be used as-is
+// or derived from by classes that only want to implement a subset of
+// the unimplemented methods
+class MyBinderRpcTestDefault : public MyBinderRpcTestBase {
+public:
+    Status countBinders(std::vector<int32_t>* /*out*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
     }
 
-    Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files,
-                       android::os::ParcelFileDescriptor* out) override {
-        std::string acc;
-        for (const auto& file : files) {
-            std::string result;
-            CHECK(android::base::ReadFdToString(file.get(), &result));
-            acc.append(result);
-        }
-        out->reset(mockFileDescriptor(acc));
-        return Status::ok();
+    Status die(bool /*cleanup*/) override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
+
+    Status scheduleShutdown() override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
+
+    Status useKernelBinderCallingId() override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
+
+    Status echoAsFile(const std::string& /*content*/,
+                      android::os::ParcelFileDescriptor* /*out*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
     }
 
-    HandoffChannel<android::base::unique_fd> mFdChannel;
-
-    Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override {
-        mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
-        return Status::ok();
+    Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& /*files*/,
+                       android::os::ParcelFileDescriptor* /*out*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
     }
 
-    Status blockingRecvFd(android::os::ParcelFileDescriptor* fd) override {
-        fd->reset(mFdChannel.read());
-        return Status::ok();
+    Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& /*fd*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
     }
+
+    Status blockingRecvFd(android::os::ParcelFileDescriptor* /*fd*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
+    }
+
+    Status blockingSendIntOneway(int /*n*/) override {
+        return Status::fromStatusT(UNKNOWN_TRANSACTION);
+    }
+
+    Status blockingRecvInt(int* /*n*/) override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
 };
 
 } // namespace android
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 5a78782..0b8920b 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -64,6 +64,22 @@
     // whether session should be invalidated by end of run
     bool expectAlreadyShutdown = false;
 
+    // TODO(b/271830568): fix this in binderRpcTest, we always use the first session to cause the
+    // remote process to shutdown. Normally, when we shutdown, the default in the destructor is to
+    // check that there are no leaks and shutdown. However, when there are incoming threadpools,
+    // there will be a few extra binder threads there, so we can't shutdown the server. We should
+    // consider an alternative way of doing the test so that we don't need this, some ideas, such as
+    // program in understanding of incoming threadpool into the destructor so that (e.g.
+    // intelligently wait for sessions to shutdown now that they will do this)
+    void forceShutdown() {
+        if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+            EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+        }
+        EXPECT_TRUE(proc->sessions.at(0).session->shutdownAndWait(true));
+        expectAlreadyShutdown = true;
+    }
+
+    BinderRpcTestProcessSession(std::unique_ptr<ProcessSession> proc) : proc(std::move(proc)){};
     BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
     ~BinderRpcTestProcessSession() {
         if (!expectAlreadyShutdown) {
@@ -106,6 +122,10 @@
 
     // Whether the test params support sending FDs in parcels.
     bool supportsFdTransport() const {
+        if (socketType() == SocketType::TIPC) {
+            // Trusty does not support file descriptors yet
+            return false;
+        }
         return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
                 (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
                  socketType() == SocketType::UNIX_BOOTSTRAP ||
@@ -119,9 +139,7 @@
     }
 
     BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
-        BinderRpcTestProcessSession ret{
-                .proc = createRpcTestSocketServerProcessEtc(options),
-        };
+        BinderRpcTestProcessSession ret(createRpcTestSocketServerProcessEtc(options));
 
         ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root;
         ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
@@ -129,9 +147,26 @@
         return ret;
     }
 
-    static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info);
+    static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+        auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+        auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
+                std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
+        if (singleThreaded) {
+            ret += "_single_threaded";
+        } else {
+            ret += "_multi_threaded";
+        }
+        if (noKernel) {
+            ret += "_no_kernel";
+        } else {
+            ret += "_with_kernel";
+        }
+        return ret;
+    }
 
 protected:
+    static std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity);
+
     std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc(
             const BinderRpcOptions& options);
 };
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 995e761..7435f30 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -18,7 +18,88 @@
 
 using namespace android;
 
-int main(int argc, const char* argv[]) {
+class MyBinderRpcTestAndroid : public MyBinderRpcTestBase {
+public:
+    wp<RpcServer> server;
+
+    Status countBinders(std::vector<int32_t>* out) override {
+        return countBindersImpl(server, out);
+    }
+
+    Status die(bool cleanup) override {
+        if (cleanup) {
+            exit(1);
+        } else {
+            _exit(1);
+        }
+    }
+
+    Status scheduleShutdown() override {
+        sp<RpcServer> strongServer = server.promote();
+        if (strongServer == nullptr) {
+            return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        }
+        RpcMaybeThread([=] {
+            LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown");
+        }).detach();
+        return Status::ok();
+    }
+
+    Status useKernelBinderCallingId() override {
+        // this is WRONG! It does not make sense when using RPC binder, and
+        // because it is SO wrong, and so much code calls this, it should abort!
+
+        if constexpr (kEnableKernelIpc) {
+            (void)IPCThreadState::self()->getCallingPid();
+        }
+        return Status::ok();
+    }
+
+    Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override {
+        out->reset(mockFileDescriptor(content));
+        return Status::ok();
+    }
+
+    Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files,
+                       android::os::ParcelFileDescriptor* out) override {
+        std::string acc;
+        for (const auto& file : files) {
+            std::string result;
+            CHECK(android::base::ReadFdToString(file.get(), &result));
+            acc.append(result);
+        }
+        out->reset(mockFileDescriptor(acc));
+        return Status::ok();
+    }
+
+    HandoffChannel<android::base::unique_fd> mFdChannel;
+
+    Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override {
+        mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
+        return Status::ok();
+    }
+
+    Status blockingRecvFd(android::os::ParcelFileDescriptor* fd) override {
+        fd->reset(mFdChannel.read());
+        return Status::ok();
+    }
+
+    HandoffChannel<int> mIntChannel;
+
+    Status blockingSendIntOneway(int n) override {
+        mIntChannel.write(n);
+        return Status::ok();
+    }
+
+    Status blockingRecvInt(int* n) override {
+        *n = mIntChannel.read();
+        return Status::ok();
+    }
+};
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
     LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
     base::unique_fd writeEnd(atoi(argv[1]));
     base::unique_fd readEnd(atoi(argv[2]));
@@ -35,9 +116,9 @@
     }
 
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
-    sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
+    sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
 
-    server->setProtocolVersion(serverConfig.serverVersion);
+    CHECK(server->setProtocolVersion(serverConfig.serverVersion));
     server->setMaxThreads(serverConfig.numThreads);
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
@@ -58,7 +139,8 @@
             CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
             break;
         case SocketType::VSOCK:
-            CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+            CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort))
+                    << "Need `sudo modprobe vsock_loopback`?";
             break;
         case SocketType::INET: {
             CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
@@ -83,12 +165,17 @@
         }
     }
 
-    server->setPerSessionRootObject([&](const void* addrPtr, size_t len) {
+    server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) {
+        {
+            sp<RpcSession> spSession = session.promote();
+            CHECK_NE(nullptr, spSession.get());
+        }
+
         // UNIX sockets with abstract addresses return
         // sizeof(sa_family_t)==2 in addrlen
         CHECK_GE(len, sizeof(sa_family_t));
         const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
-        sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make();
+        sp<MyBinderRpcTestAndroid> service = sp<MyBinderRpcTestAndroid>::make();
         switch (addr->sa_family) {
             case AF_UNIX:
                 // nothing to save
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
new file mode 100644
index 0000000..cb632e9
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TLOG_TAG "binderRpcTestService"
+
+#include <android-base/stringprintf.h>
+#include <binder/RpcServerTrusty.h>
+#include <inttypes.h>
+#include <lib/tipc/tipc.h>
+#include <lk/err_ptr.h>
+#include <stdio.h>
+#include <trusty_log.h>
+#include <vector>
+
+#include "binderRpcTestCommon.h"
+
+using namespace android;
+using android::base::StringPrintf;
+using binder::Status;
+
+static int gConnectionCounter = 0;
+
+class MyBinderRpcTestTrusty : public MyBinderRpcTestDefault {
+public:
+    wp<RpcServerTrusty> server;
+
+    Status countBinders(std::vector<int32_t>* out) override {
+        return countBindersImpl(server, out);
+    }
+
+    Status scheduleShutdown() override {
+        // TODO: Trusty does not support shutting down the tipc event loop,
+        // so we just terminate the service app since it is marked
+        // restart_on_exit
+        exit(EXIT_SUCCESS);
+    }
+
+    // TODO(b/242940548): implement echoAsFile and concatFiles
+};
+
+struct ServerInfo {
+    std::unique_ptr<std::string> port;
+    sp<RpcServerTrusty> server;
+};
+
+int main(void) {
+    TLOGI("Starting service\n");
+
+    tipc_hset* hset = tipc_hset_create();
+    if (IS_ERR(hset)) {
+        TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
+        return EXIT_FAILURE;
+    }
+
+    const auto port_acl = RpcServerTrusty::PortAcl{
+            .flags = IPC_PORT_ALLOW_NS_CONNECT | IPC_PORT_ALLOW_TA_CONNECT,
+    };
+
+    std::vector<ServerInfo> servers;
+    for (auto serverVersion : testVersions()) {
+        ServerInfo serverInfo{
+                .port = std::make_unique<std::string>(trustyIpcPort(serverVersion)),
+        };
+        TLOGI("Adding service port '%s'\n", serverInfo.port->c_str());
+
+        // Message size needs to be large enough to cover all messages sent by the
+        // tests: SendAndGetResultBackBig sends two large strings.
+        constexpr size_t max_msg_size = 4096;
+        auto serverOrErr =
+                RpcServerTrusty::make(hset, serverInfo.port->c_str(),
+                                      std::shared_ptr<const RpcServerTrusty::PortAcl>(&port_acl),
+                                      max_msg_size);
+        if (!serverOrErr.ok()) {
+            TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error());
+            return EXIT_FAILURE;
+        }
+
+        auto server = std::move(*serverOrErr);
+        serverInfo.server = server;
+        if (!serverInfo.server->setProtocolVersion(serverVersion)) {
+            return EXIT_FAILURE;
+        }
+        serverInfo.server->setPerSessionRootObject(
+                [=](wp<RpcSession> /*session*/, const void* /*addrPtr*/, size_t /*len*/) {
+                    auto service = sp<MyBinderRpcTestTrusty>::make();
+                    // Assign a unique connection identifier to service->port so
+                    // getClientPort returns a unique value per connection
+                    service->port = ++gConnectionCounter;
+                    service->server = server;
+                    return service;
+                });
+
+        servers.push_back(std::move(serverInfo));
+    }
+
+    return tipc_run_event_loop(hset);
+}
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
new file mode 100644
index 0000000..28be10d
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "binderRpcTest"
+
+#include <android-base/stringprintf.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <trusty-gtest.h>
+#include <trusty_ipc.h>
+
+#include "binderRpcTestFixture.h"
+
+namespace android {
+
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class TrustyProcessSession : public ProcessSession {
+public:
+    ~TrustyProcessSession() override {}
+
+    void setCustomExitStatusCheck(std::function<void(int wstatus)> /*f*/) override {
+        LOG_ALWAYS_FATAL("setCustomExitStatusCheck() not supported");
+    }
+
+    void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); }
+};
+
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+    switch (rpcSecurity) {
+        case RpcSecurity::RAW:
+            return RpcTransportCtxFactoryTipcTrusty::make();
+        default:
+            LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+    }
+}
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
+        const BinderRpcOptions& options) {
+    LOG_ALWAYS_FATAL_IF(std::any_of(options.numIncomingConnectionsBySession.begin(),
+                                    options.numIncomingConnectionsBySession.end(),
+                                    [](size_t n) { return n != 0; }),
+                        "Non-zero incoming connections on Trusty");
+
+    RpcSecurity rpcSecurity = std::get<1>(GetParam());
+    uint32_t clientVersion = std::get<2>(GetParam());
+    uint32_t serverVersion = std::get<3>(GetParam());
+
+    auto ret = std::make_unique<TrustyProcessSession>();
+
+    status_t status;
+    for (size_t i = 0; i < options.numSessions; i++) {
+        auto session = android::RpcSession::make(newFactory(rpcSecurity));
+
+        EXPECT_TRUE(session->setProtocolVersion(clientVersion));
+        session->setMaxOutgoingConnections(options.numOutgoingConnections);
+        session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
+
+        status = session->setupPreconnectedClient({}, [&]() {
+            auto port = trustyIpcPort(serverVersion);
+            int rc = connect(port.c_str(), IPC_CONNECT_WAIT_FOR_PORT);
+            LOG_ALWAYS_FATAL_IF(rc < 0, "Failed to connect to service: %d", rc);
+            return base::unique_fd(rc);
+        });
+        if (options.allowConnectFailure && status != OK) {
+            ret->sessions.clear();
+            break;
+        }
+        LOG_ALWAYS_FATAL_IF(status != OK, "Failed to connect to service: %s",
+                            statusToString(status).c_str());
+        ret->sessions.push_back({session, session->getRootObject()});
+    }
+
+    return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+                        ::testing::Combine(::testing::Values(SocketType::TIPC),
+                                           ::testing::Values(RpcSecurity::RAW),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::Values(false), ::testing::Values(true)),
+                        BinderRpc::PrintParamInfo);
+
+} // namespace android
+
+PORT_GTEST(BinderRpcTest, "com.android.trusty.binderRpcTest");
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index f960442..1f46010 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -113,6 +113,10 @@
 }
 
 TEST_P(BinderRpc, AppendSeparateFormats) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "Trusty does not support multiple server processes";
+    }
+
     auto proc1 = createRpcTestSocketServerProcess({});
     auto proc2 = createRpcTestSocketServerProcess({});
 
@@ -155,7 +159,9 @@
 
 TEST_P(BinderRpc, SendAndGetResultBackBig) {
     auto proc = createRpcTestSocketServerProcess({});
-    std::string single = std::string(1024, 'a');
+    // Trusty has a limit of 4096 bytes for the entire RPC Binder message
+    size_t singleLen = socketType() == SocketType::TIPC ? 512 : 4096;
+    std::string single = std::string(singleLen, 'a');
     std::string doubled;
     EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
     EXPECT_EQ(single + single, doubled);
@@ -259,6 +265,10 @@
 // aren't supported.
 
 TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "Trusty does not support multiple server processes";
+    }
+
     auto proc1 = createRpcTestSocketServerProcess({});
     auto proc2 = createRpcTestSocketServerProcess({});
 
@@ -319,15 +329,19 @@
 }
 
 TEST_P(BinderRpc, NestedTransactions) {
+    auto fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX;
+    if (socketType() == SocketType::TIPC) {
+        // TIPC does not support file descriptors yet
+        fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE;
+    }
     auto proc = createRpcTestSocketServerProcess({
             // Enable FD support because it uses more stack space and so represents
             // something closer to a worst case scenario.
-            .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
-            .serverSupportedFileDescriptorTransportModes =
-                    {RpcSession::FileDescriptorTransportMode::UNIX},
+            .clientFileDescriptorTransportMode = fileDescriptorTransportMode,
+            .serverSupportedFileDescriptorTransportModes = {fileDescriptorTransportMode},
     });
 
-    auto nastyNester = sp<MyBinderRpcTest>::make();
+    auto nastyNester = sp<MyBinderRpcTestDefault>::make();
     EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
 
     wp<IBinder> weak = nastyNester;
@@ -372,11 +386,11 @@
     EXPECT_EQ(b, weak.promote());
 }
 
-#define expectSessions(expected, iface)                   \
+#define EXPECT_SESSIONS(expected, iface)                  \
     do {                                                  \
         int session;                                      \
         EXPECT_OK((iface)->getNumOpenSessions(&session)); \
-        EXPECT_EQ(expected, session);                     \
+        EXPECT_EQ(static_cast<int>(expected), session);   \
     } while (false)
 
 TEST_P(BinderRpc, SingleSession) {
@@ -388,9 +402,9 @@
     EXPECT_OK(session->getName(&out));
     EXPECT_EQ("aoeu", out);
 
-    expectSessions(1, proc.rootIface);
+    EXPECT_SESSIONS(1, proc.rootIface);
     session = nullptr;
-    expectSessions(0, proc.rootIface);
+    EXPECT_SESSIONS(0, proc.rootIface);
 }
 
 TEST_P(BinderRpc, ManySessions) {
@@ -399,24 +413,24 @@
     std::vector<sp<IBinderRpcSession>> sessions;
 
     for (size_t i = 0; i < 15; i++) {
-        expectSessions(i, proc.rootIface);
+        EXPECT_SESSIONS(i, proc.rootIface);
         sp<IBinderRpcSession> session;
         EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
         sessions.push_back(session);
     }
-    expectSessions(sessions.size(), proc.rootIface);
+    EXPECT_SESSIONS(sessions.size(), proc.rootIface);
     for (size_t i = 0; i < sessions.size(); i++) {
         std::string out;
         EXPECT_OK(sessions.at(i)->getName(&out));
         EXPECT_EQ(std::to_string(i), out);
     }
-    expectSessions(sessions.size(), proc.rootIface);
+    EXPECT_SESSIONS(sessions.size(), proc.rootIface);
 
     while (!sessions.empty()) {
         sessions.pop_back();
-        expectSessions(sessions.size(), proc.rootIface);
+        EXPECT_SESSIONS(sessions.size(), proc.rootIface);
     }
-    expectSessions(0, proc.rootIface);
+    EXPECT_SESSIONS(0, proc.rootIface);
 }
 
 TEST_P(BinderRpc, OnewayCallDoesNotWait) {
@@ -449,7 +463,7 @@
                 auto proc = createRpcTestSocketServerProcess(
                         {.numThreads = 1,
                          .numSessions = 1,
-                         .numIncomingConnections = numIncomingConnections});
+                         .numIncomingConnectionsBySession = {numIncomingConnections}});
                 auto cb = sp<MyBinderRpcCallback>::make();
 
                 if (callIsOneway) {
@@ -469,7 +483,7 @@
                     cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
                 }
 
-                EXPECT_EQ(cb->mValues.size(), 1)
+                EXPECT_EQ(cb->mValues.size(), 1UL)
                         << "callIsOneway: " << callIsOneway
                         << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
                 if (cb->mValues.empty()) continue;
@@ -477,16 +491,7 @@
                         << "callIsOneway: " << callIsOneway
                         << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
 
-                // since we are severing the connection, we need to go ahead and
-                // tell the server to shutdown and exit so that waitpid won't hang
-                if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
-                    EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
-                }
-
-                // since this session has an incoming connection w/ a threadpool, we
-                // need to manually shut it down
-                EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
-                proc.expectAlreadyShutdown = true;
+                proc.forceShutdown();
             }
         }
     }
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 3dab2c7..642cea4 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -237,14 +237,25 @@
     checkRepr(kCurrentRepr, 0);
 }
 
+TEST(RpcWire, V1) {
+    checkRepr(kCurrentRepr, 1);
+}
+
 TEST(RpcWire, CurrentVersion) {
     checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
 }
 
-static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL,
+static_assert(RPC_WIRE_PROTOCOL_VERSION == 1,
               "If the binder wire protocol is updated, this test should test additional versions. "
               "The binder wire protocol should only be updated on upstream AOSP.");
 
+TEST(RpcWire, NextIsPlusOneReminder) {
+    if (RPC_WIRE_PROTOCOL_VERSION != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        EXPECT_EQ(RPC_WIRE_PROTOCOL_VERSION + 1, RPC_WIRE_PROTOCOL_VERSION_NEXT)
+                << "Make sure to note what the next version should be.";
+    }
+}
+
 TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) {
     if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
         EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL")
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index c857d62..5e8a32a 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -35,6 +35,7 @@
 
 #include <optional>
 
+#include <inttypes.h>
 #include <sys/eventfd.h>
 #include <sys/prctl.h>
 
@@ -686,10 +687,12 @@
     // Determine the maximum number of fds this process can have open
     struct rlimit limit {};
     ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit));
-    uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur);
+    uint64_t maxFds = limit.rlim_cur;
+
+    ALOG(LOG_INFO, "SafeInterfaceTest", "%s max FDs: %" PRIu64, __PRETTY_FUNCTION__, maxFds);
 
     // Perform this test enough times to rule out fd leaks
-    for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) {
+    for (uint32_t iter = 0; iter < (maxFds + 100); ++iter) {
         native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
         ASSERT_NE(nullptr, handle);
         handle->data[0] = dup(eventFd.get());
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 4330e3e..25e286c 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -37,17 +37,24 @@
     EXPECT_EQ(result->stdoutStr, "foo\n");
 }
 
+template <typename T>
+auto millisSince(std::chrono::time_point<T> now) {
+    auto elapsed = std::chrono::system_clock::now() - now;
+    return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+}
+
 TEST(UtilsHost, ExecuteLongRunning) {
-    auto now = std::chrono::system_clock::now();
+    auto start = std::chrono::system_clock::now();
 
     {
-        std::vector<std::string> args{"sh", "-c",
-                                      "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"};
-        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+        std::vector<std::string>
+                args{"sh", "-c", "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 100"};
+        auto result = execute(std::move(args), [&](const CommandResult& commandResult) {
+            std::cout << millisSince(start)
+                      << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl;
             return android::base::EndsWith(commandResult.stdoutStr, "\n");
         });
-        auto elapsed = std::chrono::system_clock::now() - now;
-        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        auto elapsedMs = millisSince(start);
         EXPECT_GE(elapsedMs, 1000);
         EXPECT_LT(elapsedMs, 2000);
 
@@ -58,22 +65,21 @@
 
     // ~CommandResult() called, child process is killed.
     // Assert that the second sleep does not finish.
-    auto elapsed = std::chrono::system_clock::now() - now;
-    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
-    EXPECT_LT(elapsedMs, 2000);
+    EXPECT_LT(millisSince(start), 2000);
 }
 
 TEST(UtilsHost, ExecuteLongRunning2) {
-    auto now = std::chrono::system_clock::now();
+    auto start = std::chrono::system_clock::now();
 
     {
         std::vector<std::string> args{"sh", "-c",
-                                      "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"};
-        auto result = execute(std::move(args), [](const CommandResult& commandResult) {
+                                      "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 100"};
+        auto result = execute(std::move(args), [&](const CommandResult& commandResult) {
+            std::cout << millisSince(start)
+                      << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl;
             return android::base::EndsWith(commandResult.stdoutStr, "\n");
         });
-        auto elapsed = std::chrono::system_clock::now() - now;
-        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
+        auto elapsedMs = millisSince(start);
         EXPECT_GE(elapsedMs, 4000);
         EXPECT_LT(elapsedMs, 6000);
 
@@ -84,9 +90,7 @@
 
     // ~CommandResult() called, child process is killed.
     // Assert that the second sleep does not finish.
-    auto elapsed = std::chrono::system_clock::now() - now;
-    auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
-    EXPECT_LT(elapsedMs, 6000);
+    EXPECT_LT(millisSince(start), 6000);
 }
 
 TEST(UtilsHost, KillWithSigKill) {
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 61a2412..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,14 @@
     host_supported: true,
     unstable: true,
     srcs: [
-        "EmptyParcelable.aidl",
-        "SingleDataParcelable.aidl",
-        "GenericDataParcelable.aidl",
+        "parcelables/EmptyParcelable.aidl",
+        "parcelables/SingleDataParcelable.aidl",
+        "parcelables/GenericDataParcelable.aidl",
     ],
     backend: {
         java: {
-            enabled: false,
+            enabled: true,
+            platform_apis: true,
         },
         rust: {
             enabled: true,
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..46d387c 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
 #define FUZZ_LOG_TAG "binder"
 
 #include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
 #include "util.h"
 
 #include <android-base/hex.h>
@@ -359,21 +359,23 @@
     },
     [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
-        EmptyParcelable emptyParcelable{};
+        parcelables::EmptyParcelable emptyParcelable{};
         status_t status = emptyParcelable.readFromParcel(&p);
         FUZZ_LOG() << " status: " << status;
     },
     [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
-        SingleDataParcelable singleDataParcelable;
+        parcelables::SingleDataParcelable singleDataParcelable;
         status_t status = singleDataParcelable.readFromParcel(&p);
-        FUZZ_LOG() <<" status: " << status;
+        FUZZ_LOG() << " status: " << status;
     },
     [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
-        GenericDataParcelable genericDataParcelable;
+        parcelables::GenericDataParcelable genericDataParcelable;
         status_t status = genericDataParcelable.readFromParcel(&p);
-        FUZZ_LOG() <<" status: " << status;
+        FUZZ_LOG() << " status: " << status;
+        std::string toString = genericDataParcelable.toString();
+        FUZZ_LOG() << " toString() result: " << toString;
     },
 };
 // clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..3a1471e 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
 #define FUZZ_LOG_TAG "binder_ndk"
 
 #include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
 
 #include <android/binder_parcel_utils.h>
 #include <android/binder_parcelable_utils.h>
@@ -183,21 +183,43 @@
 
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
-            aidl::EmptyParcelable emptyParcelable;
+            aidl::parcelables::EmptyParcelable emptyParcelable;
             binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
-            aidl::SingleDataParcelable singleDataParcelable;
+            aidl::parcelables::SingleDataParcelable singleDataParcelable;
             binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
-            aidl::GenericDataParcelable genericDataParcelable;
+            aidl::parcelables::GenericDataParcelable genericDataParcelable;
             binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
+            std::string toString = genericDataParcelable.toString();
+            FUZZ_LOG() << "toString() result: " << toString;
         },
+        [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
+            FUZZ_LOG() << "about to marshal AParcel";
+            size_t start = provider.ConsumeIntegral<size_t>();
+            // limit 1MB to avoid OOM issues
+            size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1000000);
+            uint8_t buffer[len];
+            binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
+            FUZZ_LOG() << "status: " << status;
+        },
+        [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
+            FUZZ_LOG() << "about to unmarshal AParcel";
+            size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+            std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
+            const uint8_t* buffer = parcelData.data();
+            const size_t bufferLen = parcelData.size();
+            NdkParcelAdapter adapter;
+            binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
+            FUZZ_LOG() << "status: " << status;
+        },
+
 };
 // clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
index a9a6197..cb37cfa 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
@@ -19,7 +19,17 @@
 #include <binder/IBinder.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
@@ -34,4 +44,5 @@
  *   }
  */
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider);
+
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
index f2b7823..d8bf87a 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
@@ -16,10 +16,21 @@
 
 #pragma once
 
+#include <android/binder_auto_utils.h>
 #include <android/binder_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 86461c8..45c3a90 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -24,24 +24,37 @@
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
-    sp<IBinder> target;
+    fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
+}
 
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
     RandomParcelOptions options{
-            .extraBinders = {binder},
+            .extraBinders = binders,
             .extraFds = {},
     };
 
+    // always refresh the calling identity, because we sometimes set it below, but also,
+    // the code we're fuzzing might reset it
+    IPCThreadState::self()->clearCallingIdentity();
+
+    // Always take so that a perturbation of just the one ConsumeBool byte will always
+    // take the same path, but with a different UID. Without this, the fuzzer needs to
+    // guess both the change in value and the shift at the same time.
+    int64_t maybeSetUid = provider.ConsumeIntegral<int64_t>();
     if (provider.ConsumeBool()) {
         // set calling uid
-        IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>());
+        IPCThreadState::self()->restoreCallingIdentity(maybeSetUid);
     }
 
     while (provider.remaining_bytes() > 0) {
-        uint32_t code = provider.ConsumeIntegral<uint32_t>();
+        // Most of the AIDL services will have small set of transaction codes.
+        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+                                               : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
         uint32_t flags = provider.ConsumeIntegral<uint32_t>();
         Parcel data;
         // for increased fuzz coverage
-        data.setEnforceNoDataAvail(provider.ConsumeBool());
+        data.setEnforceNoDataAvail(false);
+        data.setServiceFuzzing();
 
         sp<IBinder> target = options.extraBinders.at(
                 provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
@@ -59,7 +72,8 @@
 
         Parcel reply;
         // for increased fuzz coverage
-        reply.setEnforceNoDataAvail(provider.ConsumeBool());
+        reply.setEnforceNoDataAvail(false);
+        reply.setServiceFuzzing();
         (void)target->transact(code, data, &reply, flags);
 
         // feed back in binders and fds that are returned from the service, so that
@@ -75,7 +89,6 @@
     }
 
     // invariants
-
     auto ps = ProcessState::selfOrNull();
     if (ps) {
         CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount())
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
index a1fb701..0b0ca34 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
@@ -24,6 +24,15 @@
 
 namespace android {
 
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider) {
+    std::vector<sp<IBinder>> cppBinders;
+    for (const auto& binder : binders) {
+        cppBinders.push_back(binder.get()->getBinder());
+    }
+
+    fuzzService(cppBinders, std::move(provider));
+}
+
 void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) {
     fuzzService(binder->getBinder(), std::move(provider));
 }
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-parcelable SingleDataParcelable{
-   int data;
+package parcelables;
+parcelable EmptyParcelable {
 }
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 69%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..dd08f72 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,12 +13,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package parcelables;
 
 parcelable GenericDataParcelable {
+    enum JustSomeEnum {
+        SOME_ENUMERATOR,
+        ANOTHER_ENUMERATOR,
+        MAYBE_ONE_MORE_ENUMERATOR,
+    }
+
+    const int COOL_CONSTANT = 0x1234;
+
     int data;
     float majorVersion;
     float minorVersion;
     IBinder binder;
     ParcelFileDescriptor fileDescriptor;
     int[] array;
-}
\ No newline at end of file
+    String greatString;
+    @utf8InCpp
+    String greaterString;
+    @nullable String nullableString;
+    JustSomeEnum gretEnum = JustSomeEnum.ANOTHER_ENUMERATOR;
+}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package parcelables;
 
 parcelable SingleDataParcelable{
    int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index edc695f..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,6 +73,11 @@
                                                                                 1));
                         CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
                     } else {
+                        // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+                        if (options->extraFds.size() > 1000) {
+                            return;
+                        }
+
                         std::vector<base::unique_fd> fds = getRandomFds(&provider);
                         CHECK(OK ==
                               p->writeFileDescriptor(fds.begin()->release(),
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
new file mode 100644
index 0000000..690c39a
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -0,0 +1,64 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+aidl_interface {
+    name: "testServiceIface",
+    host_supported: true,
+    unstable: true,
+    srcs: [
+        "ITestService.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: true,
+            platform_apis: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
+// Adding this fuzzer to test the fuzzService functionality
+cc_fuzz {
+    name: "test_service_fuzzer_should_crash",
+    defaults: [
+        "service_fuzzer_defaults",
+    ],
+    static_libs: [
+        "liblog",
+        "testServiceIface-cpp",
+    ],
+    host_supported: true,
+    srcs: ["TestServiceFuzzer.cpp"],
+    fuzz_config: {
+        triage_assignee: "waghpawan@google.com",
+
+        // This fuzzer should be used only test fuzzService locally
+        fuzz_on_haiku_host: false,
+        fuzz_on_haiku_device: false,
+    },
+}
+
+sh_test_host {
+    name: "fuzz_service_test",
+    src: "run_fuzz_service_test.sh",
+    filename: "run_fuzz_service_test.sh",
+    test_config: "fuzz_service_test_config.xml",
+    data_bins: [
+        "test_service_fuzzer_should_crash",
+    ],
+    required: [
+        "test_service_fuzzer_should_crash",
+    ],
+    target: {
+        linux_bionic: {
+            enabled: false,
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+    test_suites: ["general-tests"],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
similarity index 70%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
index fc2542b..5089ae5 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open 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,11 +14,13 @@
  * limitations under the License.
  */
 
-parcelable GenericDataParcelable {
-    int data;
-    float majorVersion;
-    float minorVersion;
-    IBinder binder;
-    ParcelFileDescriptor fileDescriptor;
-    int[] array;
-}
\ No newline at end of file
+interface ITestService {
+
+    void setIntData(int input);
+
+    void setCharData(char input);
+
+    void setBooleanData(boolean input);
+
+    void setService(ITestService service);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
new file mode 100644
index 0000000..7fbf2d0
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <BnTestService.h>
+#include <fuzzbinder/libbinder_driver.h>
+
+#include <binder/IPCThreadState.h>
+#include <log/log.h>
+
+using android::binder::Status;
+
+namespace android {
+
+enum class CrashType {
+    NONE,
+    ON_PLAIN,
+    ON_BINDER,
+    ON_KNOWN_UID,
+};
+
+// This service is to verify that fuzzService is functioning properly
+class TestService : public BnTestService {
+public:
+    TestService(CrashType crash) : mCrash(crash) {}
+
+    void onData() {
+        switch (mCrash) {
+            case CrashType::ON_PLAIN: {
+                LOG_ALWAYS_FATAL("Expected crash, PLAIN.");
+                break;
+            }
+            case CrashType::ON_KNOWN_UID: {
+                if (IPCThreadState::self()->getCallingUid() == getuid()) {
+                    LOG_ALWAYS_FATAL("Expected crash, KNOWN_UID.");
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    Status setIntData(int /*input*/) override {
+        onData();
+        return Status::ok();
+    }
+
+    Status setCharData(char16_t /*input*/) override {
+        onData();
+        return Status::ok();
+    }
+
+    Status setBooleanData(bool /*input*/) override {
+        onData();
+        return Status::ok();
+    }
+
+    Status setService(const sp<ITestService>& service) override {
+        onData();
+        if (mCrash == CrashType::ON_BINDER && service != nullptr) {
+            LOG_ALWAYS_FATAL("Expected crash, BINDER.");
+        }
+        return Status::ok();
+    }
+
+private:
+    CrashType mCrash;
+};
+
+CrashType gCrashType = CrashType::NONE;
+
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
+    if (*argc < 2) {
+        printf("You must specify at least one argument\n");
+        exit(0); // success because this is a crash test
+    }
+
+    std::string arg = std::string((*argv)[1]);
+
+    // ignore first argument, because we consume it
+    (*argv)[1] = (*argv[0]);
+    (*argc)--;
+    (*argv)++;
+
+    if (arg == "PLAIN") {
+        gCrashType = CrashType::ON_PLAIN;
+    } else if (arg == "KNOWN_UID") {
+        gCrashType = CrashType::ON_KNOWN_UID;
+    } else if (arg == "BINDER") {
+        gCrashType = CrashType::ON_BINDER;
+    } else {
+        printf("INVALID ARG\n");
+        exit(0); // success because this is a crash test
+    }
+
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto service = sp<TestService>::make(gCrashType);
+    fuzzService(service, FuzzedDataProvider(data, size));
+    return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
new file mode 100644
index 0000000..19eb33a
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs fuzzService test">
+    <option name="null-device" value="true" />
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="run_fuzz_service_test.sh"/>
+        <option name="relative-path-execution" value="true" />
+    </test>
+</configuration>
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
new file mode 100755
index 0000000..e568035
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+color_success=$'\E'"[0;32m"
+color_failed=$'\E'"[0;31m"
+color_reset=$'\E'"[00m"
+
+FUZZER_NAME=test_service_fuzzer_should_crash
+FUZZER_OUT=fuzzer-output
+
+if [ ! -f "$FUZZER_NAME" ]
+then
+    echo -e "${color_failed}Binary $FUZZER_NAME does not exist"
+    echo "${color_reset}"
+    exit 1
+fi
+
+for CRASH_TYPE in PLAIN KNOWN_UID BINDER; do
+    echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
+
+    ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
+
+    echo "INFO: Searching fuzzer output for expected crashes"
+    if grep -q "Expected crash, $CRASH_TYPE." "$FUZZER_OUT"
+    then
+        echo -e "${color_success}Success: Found expected crash. fuzzService test successful!"
+    else
+        echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash"
+        echo "${color_reset}"
+        exit 1
+    fi
+done
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index f68a561..b8ae84d 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -133,8 +133,13 @@
 
     bool hangupBeforeShutdown = provider.ConsumeBool();
 
+    // b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently
+    // aborts. Servers should consider RpcServer::setConnectionFilter instead.
+    constexpr size_t kMaxConnections = 1000;
+
     while (provider.remaining_bytes() > 0) {
-        if (connections.empty() || provider.ConsumeBool()) {
+        if (connections.empty() ||
+            (connections.size() < kMaxConnections && provider.ConsumeBool())) {
             base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
             CHECK_NE(fd.get(), -1);
             CHECK_EQ(0,
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index 8ea948c..a881582 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -104,3 +104,42 @@
     defaults: ["binder_fuzz_defaults"],
     srcs: ["MemoryDealerFuzz.cpp"],
 }
+
+cc_fuzz {
+    name: "binder_recordedTransactionFileFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["RecordedTransactionFileFuzz.cpp"],
+    corpus: [
+        "recorded_transaction_corpus/*",
+    ],
+}
+
+cc_fuzz {
+    name: "binder_recordedTransactionFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["RecordedTransactionFuzz.cpp"],
+    target: {
+        android: {
+            shared_libs: [
+                "libcutils",
+                "libutils",
+                "libbase",
+                "libbinder",
+            ],
+            static_libs: ["libbinder_random_parcel"],
+        },
+        host: {
+            static_libs: [
+                "libcutils",
+                "liblog",
+                "libutils",
+                "libbase",
+                "libbinder",
+                "libbinder_random_parcel",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 910c9dc..a6fd487 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -51,8 +51,10 @@
     sp<RpcSession> session = RpcSession::make();
     session->setMaxIncomingThreads(1);
     status_t status;
-    for (size_t tries = 0; tries < 5; tries++) {
-        usleep(10000);
+
+    // b/274084938 - ASAN may be slow, wait a while
+    for (size_t tries = 0; tries < 50; tries++) {
+        usleep(100000);
         status = session->setupUnixDomainClient(addr.c_str());
         if (status == OK) break;
     }
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
new file mode 100644
index 0000000..e494366
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <filesystem>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::FILE* intermediateFile = std::tmpfile();
+    fwrite(data, sizeof(uint8_t), size, intermediateFile);
+    rewind(intermediateFile);
+    int fileNumber = fileno(intermediateFile);
+
+    android::base::unique_fd fd(dup(fileNumber));
+
+    auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
+
+    std::fclose(intermediateFile);
+
+    if (transaction.has_value()) {
+        intermediateFile = std::tmpfile();
+
+        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+        std::fclose(intermediateFile);
+    }
+
+    return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
new file mode 100644
index 0000000..943fb9f
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <fuzzbinder/random_parcel.h>
+#include <filesystem>
+#include <string>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::fillRandomParcel;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+    android::String16 interfaceName =
+            android::String16(provider.ConsumeRandomLengthString().c_str());
+
+    uint32_t code = provider.ConsumeIntegral<uint32_t>();
+    uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+    time_t sec = provider.ConsumeIntegral<time_t>();
+    long nsec = provider.ConsumeIntegral<long>();
+    timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec};
+    android::status_t transactionStatus = provider.ConsumeIntegral<android::status_t>();
+
+    std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
+            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+
+    // same options so that FDs and binders could be shared in both Parcels
+    android::RandomParcelOptions options;
+
+    android::Parcel p0, p1;
+    fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+    fillRandomParcel(&p1, std::move(provider), &options);
+
+    auto transaction =
+            android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags,
+                                                                     timestamp, p0, p1,
+                                                                     transactionStatus);
+
+    if (transaction.has_value()) {
+        std::FILE* intermediateFile = std::tmpfile();
+        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+        std::fclose(intermediateFile);
+    }
+
+    return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
new file mode 100644
index 0000000..79442078
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
Binary files differ
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
new file mode 100644
index 0000000..658addb
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
Binary files differ
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 18ce316..8f64323 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -65,6 +65,10 @@
     mTipcPort.msg_queue_len = 6; // Three each way
     mTipcPort.priv = this;
 
+    // TODO(b/266741352): follow-up to prevent needing this in the future
+    // Trusty needs to be set to the latest stable version that is in prebuilts there.
+    LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0));
+
     if (mPortAcl) {
         // Initialize the array of pointers to uuids.
         // The pointers in mUuidPtrs should stay valid across moves of
@@ -117,7 +121,15 @@
         *ctx_p = channelContext;
     };
 
-    base::unique_fd clientFd(chan);
+    // We need to duplicate the channel handle here because the tipc library
+    // owns the original handle and closes is automatically on channel cleanup.
+    // We use dup() because Trusty does not have fcntl().
+    // NOLINTNEXTLINE(android-cloexec-dup)
+    handle_t chanDup = dup(chan);
+    if (chanDup < 0) {
+        return chanDup;
+    }
+    base::unique_fd clientFd(chanDup);
     android::RpcTransportFd transportFd(std::move(clientFd));
 
     std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
@@ -146,8 +158,18 @@
     return NO_ERROR;
 }
 
-void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/,
-                                       void* /*ctx*/) {}
+void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) {
+    auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
+    if (channelContext == nullptr) {
+        // Connections marked "incoming" (outgoing from the server's side)
+        // do not have a valid channel context because joinFn does not get
+        // called for them. We ignore them here.
+        return;
+    }
+
+    auto& session = channelContext->session;
+    (void)session->shutdownAndWait(false);
+}
 
 void RpcServerTrusty::handleChannelCleanup(void* ctx) {
     auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 58bfe71..692f82d 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -29,8 +29,6 @@
 
 namespace android {
 
-namespace {
-
 // RpcTransport for Trusty.
 class RpcTransportTipcTrusty : public RpcTransport {
 public:
@@ -239,6 +237,12 @@
         }
         if (!(uevt.event & IPC_HANDLE_POLL_MSG)) {
             /* No message, terminate here and leave mHaveMessage false */
+            if (uevt.event & IPC_HANDLE_POLL_HUP) {
+                // Peer closed the connection. We need to preserve the order
+                // between MSG and HUP from FdTrigger.cpp, which means that
+                // getting MSG&HUP should return OK instead of DEAD_OBJECT.
+                return DEAD_OBJECT;
+            }
             return OK;
         }
 
@@ -276,8 +280,6 @@
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
-} // namespace
-
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const {
     return std::make_unique<RpcTransportCtxTipcTrusty>();
 }
diff --git a/libs/binder/trusty/binderRpcTest/aidl/rules.mk b/libs/binder/trusty/binderRpcTest/aidl/rules.mk
new file mode 100644
index 0000000..1afd324
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/aidl/rules.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDLS := \
+	$(LIBBINDER_TESTS_DIR)/BinderRpcTestClientInfo.aidl \
+	$(LIBBINDER_TESTS_DIR)/BinderRpcTestServerConfig.aidl \
+	$(LIBBINDER_TESTS_DIR)/BinderRpcTestServerInfo.aidl \
+	$(LIBBINDER_TESTS_DIR)/IBinderRpcCallback.aidl \
+	$(LIBBINDER_TESTS_DIR)/IBinderRpcSession.aidl \
+	$(LIBBINDER_TESTS_DIR)/IBinderRpcTest.aidl \
+	$(LIBBINDER_TESTS_DIR)/ParcelableCertificateData.aidl \
+
+include make/aidl.mk
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
new file mode 100644
index 0000000..1cefac5
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -0,0 +1,6 @@
+{
+    "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
+    "app_name": "binderRpcTest",
+    "min_heap": 262144,
+    "min_stack": 16384
+}
diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
new file mode 100644
index 0000000..975f689
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests
+
+MODULE := $(LOCAL_DIR)
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+MODULE_SRCS += \
+	$(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
+
+MODULE_LIBRARY_DEPS += \
+	$(LOCAL_DIR)/aidl \
+	frameworks/native/libs/binder/trusty \
+	frameworks/native/libs/binder/trusty/ndk \
+	trusty/user/base/lib/googletest \
+	trusty/user/base/lib/libstdc++-trusty \
+
+# TEST_P tests from binderRpcUniversalTests.cpp don't get linked in
+# unless we pass in --whole-archive to the linker (b/275620340).
+MODULE_USE_WHOLE_ARCHIVE := true
+
+include make/trusted_app.mk
diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json
new file mode 100644
index 0000000..1c4f7ee
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/service/manifest.json
@@ -0,0 +1,10 @@
+{
+    "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94",
+    "app_name": "binderRpcTestService",
+    "min_heap": 65536,
+    "min_stack": 16384,
+    "mgmt_flags": {
+        "restart_on_exit": true,
+        "non_critical_app": true
+    }
+}
diff --git a/libs/binder/trusty/binderRpcTest/service/rules.mk b/libs/binder/trusty/binderRpcTest/service/rules.mk
new file mode 100644
index 0000000..5d1a51d
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/service/rules.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests
+
+MODULE := $(LOCAL_DIR)
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+MODULE_SRCS := \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \
+
+MODULE_LIBRARY_DEPS := \
+	frameworks/native/libs/binder/trusty \
+	frameworks/native/libs/binder/trusty/binderRpcTest/aidl \
+	trusty/user/base/lib/libstdc++-trusty \
+	trusty/user/base/lib/tipc \
+
+include make/trusted_app.mk
diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests
new file mode 100644
index 0000000..d0a1fbc
--- /dev/null
+++ b/libs/binder/trusty/build-config-usertests
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file lists userspace tests
+
+[
+    porttest("com.android.trusty.binderRpcTest"),
+]
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 7d9dd8c..8924b36 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -59,18 +59,26 @@
             size_t msgMaxSize,
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
-    void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    [[nodiscard]] bool setProtocolVersion(uint32_t version) {
+        return mRpcServer->setProtocolVersion(version);
+    }
     void setSupportedFileDescriptorTransportModes(
             const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
         mRpcServer->setSupportedFileDescriptorTransportModes(modes);
     }
     void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
     void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
-    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
+    void setPerSessionRootObject(
+            std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object) {
         mRpcServer->setPerSessionRootObject(std::move(object));
     }
     sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); }
 
+    /**
+     * For debugging!
+     */
+    std::vector<sp<RpcSession>> listSessions() { return mRpcServer->listSessions(); }
+
 private:
     // Both this class and RpcServer have multiple non-copyable fields,
     // including mPortAcl below which can't be copied because mUuidPtrs
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index d88d18a..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -121,6 +121,8 @@
         TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
     } while (0)
 
-extern "C" inline void __assert(const char* file, int line, const char* str) {
-    LOG_ALWAYS_FATAL("%s:%d: assertion \"%s\" failed", file, line, str);
-}
+// Override the definition of __assert from binder_status.h
+#ifndef __BIONIC__
+#undef __assert
+#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
+#endif // __BIONIC__
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/trusty/include_mock/lib/tipc/tipc.h
similarity index 71%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/trusty/include_mock/lib/tipc/tipc.h
index fc2542b..ead9f9c 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/trusty/include_mock/lib/tipc/tipc.h
@@ -13,12 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-parcelable GenericDataParcelable {
-    int data;
-    float majorVersion;
-    float minorVersion;
-    IBinder binder;
-    ParcelFileDescriptor fileDescriptor;
-    int[] array;
-}
\ No newline at end of file
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct tipc_hset;
+
+struct tipc_hset* tipc_hset_create(void) {
+    return nullptr;
+}
+int tipc_run_event_loop(struct tipc_hset*) {
+    return 0;
+}
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/trusty/include_mock/lk/err_ptr.h
similarity index 89%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/trusty/include_mock/lk/err_ptr.h
index d62891b..ab3fbba 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/trusty/include_mock/lk/err_ptr.h
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-parcelable SingleDataParcelable{
-   int data;
-}
\ No newline at end of file
+#define IS_ERR(x) (!(x))
+#define PTR_ERR(x) (!!(x))
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/trusty/include_mock/trusty-gtest.h
similarity index 82%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/trusty/include_mock/trusty-gtest.h
index d62891b..046b403 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/trusty/include_mock/trusty-gtest.h
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-parcelable SingleDataParcelable{
-   int data;
-}
\ No newline at end of file
+#define PORT_GTEST(suite, port) \
+    int main(void) {            \
+        return 0;               \
+    }
diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h
index a2170ce..db044c2 100644
--- a/libs/binder/trusty/include_mock/trusty_ipc.h
+++ b/libs/binder/trusty/include_mock/trusty_ipc.h
@@ -24,6 +24,11 @@
 #define INFINITE_TIME 1
 #define IPC_MAX_MSG_HANDLES 8
 
+#define IPC_PORT_ALLOW_TA_CONNECT 0x1
+#define IPC_PORT_ALLOW_NS_CONNECT 0x2
+
+#define IPC_CONNECT_WAIT_FOR_PORT 0x1
+
 #define IPC_HANDLE_POLL_HUP 0x1
 #define IPC_HANDLE_POLL_MSG 0x2
 #define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
new file mode 100644
index 0000000..ab7a50d
--- /dev/null
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -0,0 +1,83 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_DIR := frameworks/native/libs/binder
+LIBBASE_DIR := system/libbase
+LIBCUTILS_DIR := system/core/libcutils
+LIBUTILS_DIR := system/core/libutils
+FMTLIB_DIR := external/fmtlib
+
+MODULE_SRCS := \
+	$(LOCAL_DIR)/../logging.cpp \
+	$(LOCAL_DIR)/../TrustyStatus.cpp \
+	$(LIBBINDER_DIR)/Binder.cpp \
+	$(LIBBINDER_DIR)/BpBinder.cpp \
+	$(LIBBINDER_DIR)/FdTrigger.cpp \
+	$(LIBBINDER_DIR)/IInterface.cpp \
+	$(LIBBINDER_DIR)/IResultReceiver.cpp \
+	$(LIBBINDER_DIR)/Parcel.cpp \
+	$(LIBBINDER_DIR)/Stability.cpp \
+	$(LIBBINDER_DIR)/Status.cpp \
+	$(LIBBINDER_DIR)/Utils.cpp \
+	$(LIBBASE_DIR)/hex.cpp \
+	$(LIBBASE_DIR)/stringprintf.cpp \
+	$(LIBUTILS_DIR)/Errors.cpp \
+	$(LIBUTILS_DIR)/misc.cpp \
+	$(LIBUTILS_DIR)/RefBase.cpp \
+	$(LIBUTILS_DIR)/StrongPointer.cpp \
+	$(LIBUTILS_DIR)/Unicode.cpp \
+
+# TODO: remove the following when libbinder supports std::string
+# instead of String16 and String8 for Status and descriptors
+MODULE_SRCS += \
+	$(LIBUTILS_DIR)/SharedBuffer.cpp \
+	$(LIBUTILS_DIR)/String16.cpp \
+	$(LIBUTILS_DIR)/String8.cpp \
+
+# TODO: disable dump() transactions to get rid of Vector
+MODULE_SRCS += \
+	$(LIBUTILS_DIR)/VectorImpl.cpp \
+
+MODULE_DEFINES += \
+	LK_DEBUGLEVEL_NO_ALIASES=1 \
+
+MODULE_INCLUDES += \
+	$(LOCAL_DIR)/.. \
+
+GLOBAL_INCLUDES += \
+	$(LOCAL_DIR)/include \
+	$(LOCAL_DIR)/../include \
+	$(LIBBINDER_DIR)/include \
+	$(LIBBINDER_DIR)/ndk/include_cpp \
+	$(LIBBASE_DIR)/include \
+	$(LIBCUTILS_DIR)/include \
+	$(LIBUTILS_DIR)/include \
+	$(FMTLIB_DIR)/include \
+
+GLOBAL_COMPILEFLAGS += \
+	-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \
+	-DBINDER_NO_KERNEL_IPC \
+	-DBINDER_RPC_SINGLE_THREADED \
+	-D__ANDROID_VNDK__ \
+
+MODULE_DEPS += \
+	trusty/kernel/lib/libcxx-trusty \
+	trusty/kernel/lib/libcxxabi-trusty \
+
+include make/module.mk
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 4e5cd18..42db29a 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -79,6 +79,11 @@
 	-DBINDER_RPC_SINGLE_THREADED \
 	-D__ANDROID_VNDK__ \
 
+# libbinder has some deprecated declarations that we want to produce warnings
+# not errors
+MODULE_EXPORT_COMPILEFLAGS += \
+	-Wno-error=deprecated-declarations \
+
 MODULE_LIBRARY_DEPS += \
 	trusty/user/base/lib/libstdc++-trusty \
 	trusty/user/base/lib/tipc \
diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk
new file mode 100644
index 0000000..1300121
--- /dev/null
+++ b/libs/binder/trusty/usertests-inc.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+TRUSTY_USER_TESTS += \
+	frameworks/native/libs/binder/trusty/binderRpcTest \
+	frameworks/native/libs/binder/trusty/binderRpcTest/service \
+
diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp
index 3eeaf3e..1454727 100644
--- a/libs/binderdebug/Android.bp
+++ b/libs/binderdebug/Android.bp
@@ -21,6 +21,17 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+cc_benchmark {
+    name: "binder_thread_stats",
+    shared_libs: [
+        "libutils",
+        "libbinder",
+        "libbase",
+    ],
+    static_libs: ["libbinderdebug"],
+    srcs: ["stats.cpp"],
+}
+
 cc_library {
     name: "libbinderdebug",
     vendor_available: true,
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
index d086b49..a8f2cbf 100644
--- a/libs/binderdebug/BinderDebug.cpp
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -48,14 +48,12 @@
             return -errno;
         }
     }
-    static const std::regex kContextLine("^context (\\w+)$");
 
     bool isDesiredContext = false;
     std::string line;
-    std::smatch match;
     while (getline(ifs, line)) {
-        if (std::regex_search(line, match, kContextLine)) {
-            isDesiredContext = match.str(1) == contextName;
+        if (base::StartsWith(line, "context")) {
+            isDesiredContext = base::Split(line, " ").back() == contextName;
             continue;
         }
         if (!isDesiredContext) {
@@ -66,57 +64,72 @@
     return OK;
 }
 
+// Examples of what we are looking at:
+// node 66730: u00007590061890e0 c0000759036130950 pri 0:120 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 2300 1790
+// thread 2999: l 00 need_return 1 tr 0
 status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) {
     std::smatch match;
     static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
     static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
     std::string contextStr = contextToString(context);
     status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
-        if (std::regex_search(line, match, kReferencePrefix)) {
-            const std::string& ptrString = "0x" + match.str(2); // use number after c
-            uint64_t ptr;
-            if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
-                // Should not reach here, but just be tolerant.
-                return;
-            }
-            const std::string proc = " proc ";
-            auto pos = line.rfind(proc);
-            if (pos != std::string::npos) {
-                for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) {
-                    int32_t pid;
-                    if (!::android::base::ParseInt(pidStr, &pid)) {
+        if (base::StartsWith(line, "  node")) {
+            std::vector<std::string> splitString = base::Tokenize(line, " ");
+            bool pids = false;
+            uint64_t ptr = 0;
+            for (const auto& token : splitString) {
+                if (base::StartsWith(token, "u")) {
+                    const std::string ptrString = "0x" + token.substr(1);
+                    if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+                        LOG(ERROR) << "Failed to parse pointer: " << ptrString;
                         return;
                     }
-                    pidInfo->refPids[ptr].push_back(pid);
+                } else {
+                    // The last numbers in the line after "proc" are all client PIDs
+                    if (token == "proc") {
+                        pids = true;
+                    } else if (pids) {
+                        int32_t pid;
+                        if (!::android::base::ParseInt(token, &pid)) {
+                            LOG(ERROR) << "Failed to parse pid int: " << token;
+                            return;
+                        }
+                        if (ptr == 0) {
+                            LOG(ERROR) << "We failed to parse the pointer, so we can't add the refPids";
+                            return;
+                        }
+                        pidInfo->refPids[ptr].push_back(pid);
+                    }
                 }
             }
+        } else if (base::StartsWith(line, "  thread")) {
+            auto pos = line.find("l ");
+            if (pos != std::string::npos) {
+                // "1" is waiting in binder driver
+                // "2" is poll. It's impossible to tell if these are in use.
+                //     and HIDL default code doesn't use it.
+                bool isInUse = line.at(pos + 2) != '1';
+                // "0" is a thread that has called into binder
+                // "1" is looper thread
+                // "2" is main looper thread
+                bool isBinderThread = line.at(pos + 3) != '0';
+                if (!isBinderThread) {
+                    return;
+                }
+                if (isInUse) {
+                    pidInfo->threadUsage++;
+                }
 
-            return;
-        }
-        if (std::regex_search(line, match, kThreadPrefix)) {
-            // "1" is waiting in binder driver
-            // "2" is poll. It's impossible to tell if these are in use.
-            //     and HIDL default code doesn't use it.
-            bool isInUse = match.str(1) != "1";
-            // "0" is a thread that has called into binder
-            // "1" is looper thread
-            // "2" is main looper thread
-            bool isBinderThread = match.str(2) != "0";
-            if (!isBinderThread) {
-                return;
+                pidInfo->threadCount++;
             }
-            if (isInUse) {
-                pidInfo->threadUsage++;
-            }
-
-            pidInfo->threadCount++;
-            return;
         }
-        return;
     });
     return ret;
 }
 
+// Examples of what we are looking at:
+// ref 52493: desc 910 node 52492 s 1 w 1 d 0000000000000000
+// node 29413: u00007803fc982e80 c000078042c982210 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 488 683
 status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
                              int32_t handle, std::vector<pid_t>* pids) {
     std::smatch match;
@@ -124,51 +137,64 @@
     std::string contextStr = contextToString(context);
     int32_t node;
     status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
-        if (std::regex_search(line, match, kNodeNumber)) {
-            const std::string& descString = match.str(1);
-            int32_t desc;
-            if (!::android::base::ParseInt(descString.c_str(), &desc)) {
-                LOG(ERROR) << "Failed to parse desc int: " << descString;
-                return;
-            }
-            if (handle != desc) {
-                return;
-            }
-            const std::string& nodeString = match.str(2);
-            if (!::android::base::ParseInt(nodeString.c_str(), &node)) {
-                LOG(ERROR) << "Failed to parse node int: " << nodeString;
-                return;
-            }
+        if (!base::StartsWith(line, "  ref")) return;
+
+        std::vector<std::string> splitString = base::Tokenize(line, " ");
+        if (splitString.size() < 12) {
+            LOG(ERROR) << "Failed to parse binder_logs ref entry. Expecting size greater than 11, but got: " << splitString.size();
             return;
         }
-        return;
+        int32_t desc;
+        if (!::android::base::ParseInt(splitString[3].c_str(), &desc)) {
+            LOG(ERROR) << "Failed to parse desc int: " << splitString[3];
+            return;
+        }
+        if (handle != desc) {
+            return;
+        }
+        if (!::android::base::ParseInt(splitString[5].c_str(), &node)) {
+            LOG(ERROR) << "Failed to parse node int: " << splitString[5];
+            return;
+        }
+        LOG(INFO) << "Parsed the node: " << node;
     });
     if (ret != OK) {
         return ret;
     }
-    static const std::regex kClients("^\\s+node\\s+(\\d+).*proc\\s+([\\d+\\s*]*)");
+
     ret = scanBinderContext(servicePid, contextStr, [&](const std::string& line) {
-        if (std::regex_search(line, match, kClients)) {
-            const std::string nodeString = match.str(1);
-            int32_t matchedNode;
-            if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
-                LOG(ERROR) << "Failed to parse node int: " << nodeString;
-                return;
-            }
-            if (node != matchedNode) {
-                return;
-            }
-            const std::string clients = match.str(2);
-            for (const std::string& pidStr : base::Split(clients, " ")) {
+        if (!base::StartsWith(line, "  node")) return;
+
+        std::vector<std::string> splitString = base::Tokenize(line, " ");
+        if (splitString.size() < 21) {
+            LOG(ERROR) << "Failed to parse binder_logs node entry. Expecting size greater than 20, but got: " << splitString.size();
+            return;
+        }
+
+        // remove the colon
+        const std::string nodeString = splitString[1].substr(0, splitString[1].size() - 1);
+        int32_t matchedNode;
+        if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
+            LOG(ERROR) << "Failed to parse node int: " << nodeString;
+            return;
+        }
+
+        if (node != matchedNode) {
+            return;
+        }
+        bool pidsSection = false;
+        for (const auto& token : splitString) {
+            if (token == "proc") {
+                pidsSection = true;
+            } else if (pidsSection == true) {
                 int32_t pid;
-                if (!::android::base::ParseInt(pidStr, &pid)) {
+                if (!::android::base::ParseInt(token.c_str(), &pid)) {
+                    LOG(ERROR) << "Failed to parse PID int: " << token;
                     return;
                 }
                 pids->push_back(pid);
             }
-            return;
         }
-        return;
     });
     return ret;
 }
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
index dfd5a7c..6ce8edf 100644
--- a/libs/binderdebug/include/binderdebug/BinderDebug.h
+++ b/libs/binderdebug/include/binderdebug/BinderDebug.h
@@ -15,6 +15,8 @@
  */
 #pragma once
 
+#include <utils/Errors.h>
+
 #include <map>
 #include <vector>
 
diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
new file mode 100644
index 0000000..9c26afa
--- /dev/null
+++ b/libs/binderdebug/stats.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/BpBinder.h>
+#include <binder/IServiceManager.h>
+#include <binderdebug/BinderDebug.h>
+#include <utils/Errors.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+extern "C" int main() {
+    // ignore args - we only print csv
+
+    // we should use a csv library here for escaping, because
+    // the name is coming from another process
+    printf("name,binder_threads_in_use,binder_threads_started,client_count\n");
+
+    for (const String16& name : defaultServiceManager()->listServices()) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(name);
+        if (binder == nullptr) {
+            fprintf(stderr, "%s is null", String8(name).c_str());
+            continue;
+        }
+
+        BpBinder* remote = binder->remoteBinder();
+        const auto handle = remote->getDebugBinderHandle();
+        CHECK(handle != std::nullopt);
+
+        pid_t pid;
+        CHECK_EQ(OK, binder->getDebugPid(&pid));
+
+        BinderPidInfo info;
+        CHECK_EQ(OK, getBinderPidInfo(BinderDebugContext::BINDER, pid, &info));
+
+        std::vector<pid_t> clientPids;
+        CHECK_EQ(OK,
+                 getBinderClientPids(BinderDebugContext::BINDER, getpid(), pid, *handle,
+                                     &clientPids));
+
+        printf("%s,%" PRIu32 ",%" PRIu32 ",%zu\n", String8(name).c_str(), info.threadUsage,
+               info.threadCount, clientPids.size());
+    }
+    return 0;
+}
+
+} // namespace android
diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
index a3e5026..54259d2 100644
--- a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
+++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
@@ -36,8 +36,12 @@
 // Based on where we are in recursion of nested binder/hwbinder calls, determine
 // which one we are closer to.
 inline static BinderCallType getCurrentServingCall() {
-    const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
-    const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+    auto* hwState = android::hardware::IPCThreadState::selfOrNull();
+    auto* state = android::IPCThreadState::selfOrNull();
+
+    // getServingStackPointer can also return nullptr
+    const void* hwbinderSp = hwState ? hwState->getServingStackPointer() : nullptr;
+    const void* binderSp = state ? state->getServingStackPointer() : nullptr;
 
     if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE;
     if (hwbinderSp == nullptr) return BinderCallType::BINDER;
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index df1f35d..b5c4010 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -16,11 +16,16 @@
 
 #include <BnAidlStuff.h>
 #include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binderthreadstate/CallerUtils.h>
 #include <binderthreadstateutilstest/1.0/IHidlStuff.h>
 #include <gtest/gtest.h>
 #include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+
+#include <thread>
+
 #include <linux/prctl.h>
 #include <sys/prctl.h>
 
@@ -154,6 +159,20 @@
     EXPECT_TRUE(server->callLocal().isOk());
 }
 
+TEST(BinderThreadState, DoesntInitializeBinderDriver) {
+    // this is on another thread, because it's testing thread-specific
+    // state and we expect it not to be initialized.
+    std::thread([&] {
+        EXPECT_EQ(nullptr, android::IPCThreadState::selfOrNull());
+        EXPECT_EQ(nullptr, android::hardware::IPCThreadState::selfOrNull());
+
+        (void)getCurrentServingCall();
+
+        EXPECT_EQ(nullptr, android::IPCThreadState::selfOrNull());
+        EXPECT_EQ(nullptr, android::hardware::IPCThreadState::selfOrNull());
+    }).join();
+}
+
 TEST(BindThreadState, RemoteHidlCall) {
     auto stuff = IHidlStuff::getService(id2name(kP1Id));
     ASSERT_NE(nullptr, stuff);
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 706704a..4a7bd36 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -55,6 +55,7 @@
 static uint32_t gNCpus = 0;
 static std::vector<std::vector<uint32_t>> gPolicyFreqs;
 static std::vector<std::vector<uint32_t>> gPolicyCpus;
+static std::vector<uint32_t> gCpuIndexMap;
 static std::set<uint32_t> gAllFreqs;
 static unique_fd gTisTotalMapFd;
 static unique_fd gTisMapFd;
@@ -108,7 +109,7 @@
         free(dirlist[i]);
     }
     free(dirlist);
-
+    uint32_t max_cpu_number = 0;
     for (const auto &policy : policyFileNames) {
         std::vector<uint32_t> freqs;
         for (const auto &name : {"available", "boost"}) {
@@ -127,8 +128,19 @@
         std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
         auto cpus = readNumbersFromFile(path);
         if (!cpus) return false;
+        for (auto cpu : *cpus) {
+            if(cpu > max_cpu_number)
+                max_cpu_number = cpu;
+        }
         gPolicyCpus.emplace_back(*cpus);
     }
+    gCpuIndexMap = std::vector<uint32_t>(max_cpu_number+1, -1);
+    uint32_t cpuorder = 0;
+    for (const auto &cpuList : gPolicyCpus) {
+        for (auto cpu : cpuList) {
+            gCpuIndexMap[cpu] = cpuorder++;
+        }
+    }
 
     gTisTotalMapFd =
             unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_total_time_in_state_map")};
@@ -277,7 +289,7 @@
         for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) {
             if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue;
             for (const auto &cpu : gPolicyCpus[policyIdx]) {
-                out[policyIdx][freqIdx] += vals[cpu];
+                out[policyIdx][freqIdx] += vals[gCpuIndexMap[cpu]];
             }
         }
     }
@@ -316,7 +328,8 @@
             auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end();
 
             for (const auto &cpu : gPolicyCpus[j]) {
-                std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>());
+                std::transform(begin, end, std::begin(vals[gCpuIndexMap[cpu]].ar), begin,
+                               std::plus<uint64_t>());
             }
         }
     }
@@ -382,7 +395,8 @@
             auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY :
                 map[key.uid][i].end();
             for (const auto &cpu : gPolicyCpus[i]) {
-                std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>());
+                std::transform(begin, end, std::begin(vals[gCpuIndexMap[cpu]].ar), begin,
+                               std::plus<uint64_t>());
             }
         }
         prevKey = key;
@@ -437,8 +451,8 @@
                                                                      : ret.policy[policy].end();
 
             for (const auto &cpu : gPolicyCpus[policy]) {
-                std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
-                               std::plus<uint64_t>());
+                std::transform(policyBegin, policyEnd, std::begin(vals[gCpuIndexMap[cpu]].policy),
+                               policyBegin, std::plus<uint64_t>());
             }
         }
     }
@@ -506,8 +520,8 @@
                                                                 : ret[key.uid].policy[policy].end();
 
             for (const auto &cpu : gPolicyCpus[policy]) {
-                std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
-                               std::plus<uint64_t>());
+                std::transform(policyBegin, policyEnd, std::begin(vals[gCpuIndexMap[cpu]].policy),
+                               policyBegin, std::plus<uint64_t>());
             }
         }
     } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key));
@@ -640,7 +654,7 @@
                 auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY
                                                                : map[key.aggregation_key][j].end();
                 for (const auto &cpu : gPolicyCpus[j]) {
-                    std::transform(begin, end, std::begin(vals[cpu].ar), begin,
+                    std::transform(begin, end, std::begin(vals[gCpuIndexMap[cpu]].ar), begin,
                                    std::plus<uint64_t>());
                 }
             }
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 067ce17..97cb810 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,6 +16,7 @@
 #include <set>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -210,3 +211,18 @@
     return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" ||
             cmdline == "usap64" || cmdline == "webview_zygote";
 }
+
+bool IsCached(int pid) {
+    std::string oom_score_adj;
+    if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/oom_score_adj",
+                                                                     pid),
+                                         &oom_score_adj)) {
+        return false;
+    }
+    int32_t oom_score_adj_value;
+    if (!android::base::ParseInt(android::base::Trim(oom_score_adj), &oom_score_adj_value)) {
+        return false;
+    }
+    // An OOM score greater than 900 indicates a cached process.
+    return oom_score_adj_value >= 900;
+}
diff --git a/libs/dumputils/include/dumputils/dump_utils.h b/libs/dumputils/include/dumputils/dump_utils.h
index 7c5329d..f973d9f 100644
--- a/libs/dumputils/include/dumputils/dump_utils.h
+++ b/libs/dumputils/include/dumputils/dump_utils.h
@@ -25,4 +25,6 @@
 
 bool IsZygote(int pid);
 
+bool IsCached(int pid);
+
 #endif  // DUMPUTILS_H_
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 29924ff..96dcce1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -11,7 +11,7 @@
     name: "fakeservicemanager_defaults",
     host_supported: true,
     srcs: [
-        "ServiceManager.cpp",
+        "FakeServiceManager.cpp",
     ],
 
     shared_libs: [
@@ -28,7 +28,7 @@
 cc_library {
     name: "libfakeservicemanager",
     defaults: ["fakeservicemanager_defaults"],
-    export_include_dirs: ["include/fakeservicemanager"],
+    export_include_dirs: ["include"],
 }
 
 cc_test_host {
@@ -38,5 +38,5 @@
         "test_sm.cpp",
     ],
     static_libs: ["libgmock"],
-    local_include_dirs: ["include/fakeservicemanager"],
+    local_include_dirs: ["include"],
 }
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
similarity index 66%
rename from libs/fakeservicemanager/ServiceManager.cpp
rename to libs/fakeservicemanager/FakeServiceManager.cpp
index 1109ad8..3272bbc 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
 
 namespace android {
 
-ServiceManager::ServiceManager() {}
+FakeServiceManager::FakeServiceManager() {}
 
-sp<IBinder> ServiceManager::getService( const String16& name) const {
+sp<IBinder> FakeServiceManager::getService( const String16& name) const {
     // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
     return checkService(name);
 }
 
-sp<IBinder> ServiceManager::checkService( const String16& name) const {
+sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
     auto it = mNameToService.find(name);
     if (it == mNameToService.end()) {
         return nullptr;
@@ -33,7 +33,7 @@
     return it->second;
 }
 
-status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
                                 bool /*allowIsolated*/,
                                 int /*dumpsysFlags*/) {
     if (service == nullptr) {
@@ -43,7 +43,7 @@
     return NO_ERROR;
 }
 
-Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
     Vector<String16> services;
     for (auto const& [name, service] : mNameToService) {
         (void) service;
@@ -52,19 +52,19 @@
   return services;
 }
 
-IBinder* ServiceManager::onAsBinder() {
+IBinder* FakeServiceManager::onAsBinder() {
     return nullptr;
 }
 
-sp<IBinder> ServiceManager::waitForService(const String16& name) {
+sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
     return checkService(name);
 }
 
-bool ServiceManager::isDeclared(const String16& name) {
+bool FakeServiceManager::isDeclared(const String16& name) {
     return mNameToService.find(name) != mNameToService.end();
 }
 
-Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
     Vector<String16> out;
     const String16 prefix = name + String16("/");
     for (const auto& [registeredName, service] : mNameToService) {
@@ -76,38 +76,38 @@
     return out;
 }
 
-std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) {
     (void)name;
     return std::nullopt;
 }
 
-Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) {
     (void)apexName;
     return {};
 }
 
-std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo(
         const String16& name) {
     (void)name;
     return std::nullopt;
 }
 
-status_t ServiceManager::registerForNotifications(const String16&,
+status_t FakeServiceManager::registerForNotifications(const String16&,
                                                   const sp<LocalRegistrationCallback>&) {
     return INVALID_OPERATION;
 }
 
-status_t ServiceManager::unregisterForNotifications(const String16&,
+status_t FakeServiceManager::unregisterForNotifications(const String16&,
                                                 const sp<LocalRegistrationCallback>&) {
     return INVALID_OPERATION;
 }
 
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() {
     std::vector<IServiceManager::ServiceDebugInfo> ret;
     return ret;
 }
 
-void ServiceManager::clear() {
+void FakeServiceManager::clear() {
     mNameToService.clear();
 }
 }  // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
similarity index 96%
rename from libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index ba6bb7d..97add24 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -28,9 +28,9 @@
  * A local host simple implementation of IServiceManager, that does not
  * communicate over binder.
 */
-class ServiceManager : public IServiceManager {
+class FakeServiceManager : public IServiceManager {
 public:
-    ServiceManager();
+    FakeServiceManager();
 
     sp<IBinder> getService( const String16& name) const override;
 
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 8682c1c..6fc21c6 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -21,14 +21,14 @@
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
 
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
 
 using android::sp;
 using android::BBinder;
 using android::IBinder;
 using android::OK;
 using android::status_t;
-using android::ServiceManager;
+using android::FakeServiceManager;
 using android::String16;
 using android::IServiceManager;
 using testing::ElementsAre;
@@ -45,19 +45,19 @@
 }
 
 TEST(AddService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
 }
 
 TEST(AddService, SadNullBinder) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
 }
 
 TEST(AddService, HappyOverExistingService) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -65,7 +65,7 @@
 }
 
 TEST(AddService, HappyClearAddedService) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
     EXPECT_NE(sm->getService(String16("foo")), nullptr);
@@ -74,7 +74,7 @@
 }
 
 TEST(GetService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -84,13 +84,13 @@
 }
 
 TEST(GetService, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->getService(String16("foo")), nullptr);
 }
 
 TEST(ListServices, AllServices) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
@@ -109,13 +109,13 @@
 }
 
 TEST(WaitForService, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
 }
 
 TEST(WaitForService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -125,13 +125,13 @@
 }
 
 TEST(IsDeclared, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_FALSE(sm->isDeclared(String16("foo")));
 }
 
 TEST(IsDeclared, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index c010a2e..09422d3 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,9 +10,6 @@
 cc_test {
     name: "ftl_test",
     test_suites: ["device-tests"],
-    sanitize: {
-        address: true,
-    },
     srcs: [
         "cast_test.cpp",
         "concat_test.cpp",
diff --git a/libs/ftl/TEST_MAPPING b/libs/ftl/TEST_MAPPING
new file mode 100644
index 0000000..ec0c671
--- /dev/null
+++ b/libs/ftl/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "ftl_test"
+    }
+  ],
+  "hwasan-presubmit": [
+    {
+      "name": "ftl_test"
+    }
+  ]
+}
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index bd21fba..f5af425 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -51,14 +51,14 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
     ],
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index a96a07a..af50a29 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -27,10 +27,13 @@
     srcs: [
         "GpuStatsInfo.cpp",
         "GraphicsEnv.cpp",
-        "IGpuService.cpp"
+        "IGpuService.cpp",
     ],
 
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     shared_libs: [
         "libbase",
@@ -46,4 +49,13 @@
     ],
 
     export_include_dirs: ["include"],
+
+    product_variables: {
+        // `debuggable` is set for eng and userdebug builds
+        debuggable: {
+            cflags: [
+                "-DANDROID_DEBUGGABLE",
+            ],
+        },
+    },
 }
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4a0a839..5f5f85a 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -126,7 +126,20 @@
 }
 
 bool GraphicsEnv::isDebuggable() {
-    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+    // This flag determines if the application is marked debuggable
+    bool appDebuggable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+
+    // This flag is set only in `debuggable` builds of the platform
+#if defined(ANDROID_DEBUGGABLE)
+    bool platformDebuggable = true;
+#else
+    bool platformDebuggable = false;
+#endif
+
+    ALOGV("GraphicsEnv::isDebuggable returning appDebuggable=%s || platformDebuggable=%s",
+          appDebuggable ? "true" : "false", platformDebuggable ? "true" : "false");
+
+    return appDebuggable || platformDebuggable;
 }
 
 void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index 347c4e0..1db8cbe 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,10 +1,4 @@
-abdolrashidi@google.com
-cclao@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 ianelliott@google.com
-lfy@google.com
 lpy@google.com
-romanl@google.com
-vantablack@google.com
-yuxinhu@google.com
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 82a6b6c..73d3196 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -35,7 +35,7 @@
 
     // Check if the process is debuggable. It returns false except in any of the
     // following circumstances:
-    // 1. ro.debuggable=1 (global debuggable enabled).
+    // 1. ANDROID_DEBUGGABLE is defined (global debuggable enabled).
     // 2. android:debuggable="true" in the manifest for an individual app.
     // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
     // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 6c39bbf..0fe6f24 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -214,6 +214,7 @@
 
     shared_libs: [
         "libbinder",
+        "libGLESv2",
     ],
 
     export_shared_lib_headers: [
@@ -321,12 +322,11 @@
         "android.hardware.graphics.bufferqueue@2.0",
         "android.hardware.graphics.common@1.1",
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hidl.token@1.0-utils",
         "libbase",
         "libcutils",
         "libEGL",
-        "libGLESv2",
         "libhidlbase",
         "liblog",
         "libnativewindow",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dbccf30..000f458 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,6 +132,11 @@
     }
 }
 
+void BLASTBufferItemConsumer::resizeFrameEventHistory(size_t newSize) {
+    Mutex::Autolock lock(mMutex);
+    mFrameEventHistory.resize(newSize);
+}
+
 BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
       : mSurfaceControl(nullptr),
         mSize(1, 1),
@@ -287,18 +292,17 @@
 
                 // We need to check if we were waiting for a transaction callback in order to
                 // process any pending buffers and unblock. It's possible to get transaction
-                // callbacks for previous requests so we need to ensure the frame from this
-                // transaction callback matches the last acquired buffer. Since acquireNextBuffer
-                // will stop processing buffers when mWaitForTransactionCallback is set, we know
-                // that mLastAcquiredFrameNumber is the frame we're waiting on.
-                // We also want to check if mNextTransaction is null because it's possible another
+                // callbacks for previous requests so we need to ensure that there are no pending
+                // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
+                // set and then check if it's empty. If there are no more pending syncs, we can
+                // proceed with flushing the shadow queue.
+                // We also want to check if mSyncTransaction is null because it's possible another
                 // sync request came in while waiting, but it hasn't started processing yet. In that
                 // case, we don't actually want to flush the frames in between since they will get
                 // processed and merged with the sync transaction and released earlier than if they
                 // were sent to SF
-                if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
-                    currFrameNumber >= mLastAcquiredFrameNumber) {
-                    mWaitForTransactionCallback = false;
+                mSyncedFrameNumbers.erase(currFrameNumber);
+                if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
                     flushShadowQueue();
                 }
             } else {
@@ -308,7 +312,6 @@
             BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
                      "empty.");
         }
-
         decStrong((void*)transactionCommittedCallbackThunk);
     }
 }
@@ -351,6 +354,21 @@
                                                     stat.latchTime,
                                                     stat.frameEventStats.dequeueReadyTime);
                 }
+                auto currFrameNumber = stat.frameEventStats.frameNumber;
+                std::vector<ReleaseCallbackId> staleReleases;
+                for (const auto& [key, value]: mSubmitted) {
+                    if (currFrameNumber > key.framenumber) {
+                        staleReleases.push_back(key);
+                    }
+                }
+                for (const auto& staleRelease : staleReleases) {
+                    releaseBufferCallbackLocked(staleRelease,
+                                                stat.previousReleaseFence
+                                                        ? stat.previousReleaseFence
+                                                        : Fence::NO_FENCE,
+                                                stat.currentMaxAcquiredBufferCount,
+                                                true /* fakeRelease */);
+                }
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
             }
@@ -391,7 +409,16 @@
         const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
         std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     BBQ_TRACE();
+
     std::unique_lock _lock{mMutex};
+    releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount,
+                                false /* fakeRelease */);
+}
+
+void BLASTBufferQueue::releaseBufferCallbackLocked(
+        const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+        std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) {
+    ATRACE_CALL();
     BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
     // Calculate how many buffers we need to hold before we release them back
@@ -407,18 +434,29 @@
         mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
     }
 
-    const auto numPendingBuffersToHold =
-            isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
-    mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+    const uint32_t numPendingBuffersToHold =
+            isEGL ? std::max(0, mMaxAcquiredBuffers - (int32_t)mCurrentMaxAcquiredBufferCount) : 0;
+
+    auto rb = ReleasedBuffer{id, releaseFence};
+    if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
+        mPendingRelease.emplace_back(rb);
+        if (fakeRelease) {
+            BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback %" PRIu64,
+                     id.framenumber);
+            BBQ_TRACE("FakeReleaseCallback");
+        }
+    }
 
     // Release all buffers that are beyond the ones that we need to hold
     while (mPendingRelease.size() > numPendingBuffersToHold) {
         const auto releasedBuffer = mPendingRelease.front();
         mPendingRelease.pop_front();
         releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
-        // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
-        // onFrameAvailable handle processing them since it will merge with the syncTransaction.
-        if (!mWaitForTransactionCallback) {
+        // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means
+        // are still transactions that have sync buffers in them that have not been applied or
+        // dropped. Instead, let onFrameAvailable handle processing them since it will merge with
+        // the syncTransaction.
+        if (mSyncedFrameNumbers.empty()) {
             acquireNextBufferLocked(std::nullopt);
         }
     }
@@ -442,22 +480,31 @@
     BQA_LOGV("released %s", callbackId.to_string().c_str());
     mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
     mSubmitted.erase(it);
+    // Remove the frame number from mSyncedFrameNumbers since we can get a release callback
+    // without getting a transaction committed if the buffer was dropped.
+    mSyncedFrameNumbers.erase(callbackId.framenumber);
 }
 
-void BLASTBufferQueue::acquireNextBufferLocked(
+status_t BLASTBufferQueue::acquireNextBufferLocked(
         const std::optional<SurfaceComposerClient::Transaction*> transaction) {
-    // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
-    // include the extra buffer when checking if we can acquire the next buffer.
-    const bool includeExtraAcquire = !transaction;
-    const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
-    if (mNumFrameAvailable == 0 || maxAcquired) {
-        BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
-        return;
+    // Check if we have frames available and we have not acquired the maximum number of buffers.
+    // Even with this check, the consumer can fail to acquire an additional buffer if the consumer
+    // has already acquired (mMaxAcquiredBuffers + 1) and the new buffer is not droppable. In this
+    // case mBufferItemConsumer->acquireBuffer will return with NO_BUFFER_AVAILABLE.
+    if (mNumFrameAvailable == 0) {
+        BQA_LOGV("Can't acquire next buffer. No available frames");
+        return BufferQueue::NO_BUFFER_AVAILABLE;
+    }
+
+    if (mNumAcquired >= (mMaxAcquiredBuffers + 2)) {
+        BQA_LOGV("Can't acquire next buffer. Already acquired max frames %d max:%d + 2",
+                 mNumAcquired, mMaxAcquiredBuffers);
+        return BufferQueue::NO_BUFFER_AVAILABLE;
     }
 
     if (mSurfaceControl == nullptr) {
         BQA_LOGE("ERROR : surface control is null");
-        return;
+        return NAME_NOT_FOUND;
     }
 
     SurfaceComposerClient::Transaction localTransaction;
@@ -474,10 +521,10 @@
             mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
     if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
         BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
-        return;
+        return status;
     } else if (status != OK) {
         BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
-        return;
+        return status;
     }
 
     auto buffer = bufferItem.mGraphicBuffer;
@@ -487,7 +534,7 @@
     if (buffer == nullptr) {
         mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
         BQA_LOGE("Buffer was empty");
-        return;
+        return BAD_VALUE;
     }
 
     if (rejectBuffer(bufferItem)) {
@@ -496,8 +543,7 @@
                  mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
                  buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
         mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
-        acquireNextBufferLocked(transaction);
-        return;
+        return acquireNextBufferLocked(transaction);
     }
 
     mNumAcquired++;
@@ -551,9 +597,23 @@
         t->setDesiredPresentTime(bufferItem.mTimestamp);
     }
 
-    if (!mNextFrameTimelineInfoQueue.empty()) {
-        t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
-        mNextFrameTimelineInfoQueue.pop();
+    // Drop stale frame timeline infos
+    while (!mPendingFrameTimelines.empty() &&
+           mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
+        ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
+                              mPendingFrameTimelines.front().first,
+                              mPendingFrameTimelines.front().second.vsyncId);
+        mPendingFrameTimelines.pop();
+    }
+
+    if (!mPendingFrameTimelines.empty() &&
+        mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
+        ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
+                              " vsyncId: %" PRId64,
+                              bufferItem.mFrameNumber,
+                              mPendingFrameTimelines.front().second.vsyncId);
+        t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
+        mPendingFrameTimelines.pop();
     }
 
     {
@@ -585,6 +645,7 @@
              bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
              static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
              bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform);
+    return OK;
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -607,42 +668,19 @@
     mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
 }
 
-void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
-    if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
-        // We are waiting on a previous sync's transaction callback so allow another sync
-        // transaction to proceed.
-        //
-        // We need to first flush out the transactions that were in between the two syncs.
-        // We do this by merging them into mSyncTransaction so any buffer merging will get
-        // a release callback invoked. The release callback will be async so we need to wait
-        // on max acquired to make sure we have the capacity to acquire another buffer.
-        if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-            BQA_LOGD("waiting to flush shadow queue...");
-            mCallbackCV.wait(lock);
-        }
-        while (mNumFrameAvailable > 0) {
-            // flush out the shadow queue
-            acquireAndReleaseBuffer();
-        }
-    }
-
-    while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-        BQA_LOGD("waiting for free buffer.");
-        mCallbackCV.wait(lock);
-    }
-}
-
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
     std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
     SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+
     {
-        BBQ_TRACE();
         std::unique_lock _lock{mMutex};
+        BBQ_TRACE();
+
+        bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
         const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
         BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
 
         if (syncTransactionSet) {
-            bool mayNeedToWaitForBuffer = true;
             // If we are going to re-use the same mSyncTransaction, release the buffer that may
             // already be set in the Transaction. This is to allow us a free slot early to continue
             // processing a new buffer.
@@ -653,20 +691,35 @@
                              bufferData->frameNumber);
                     releaseBuffer(bufferData->generateReleaseCallbackId(),
                                   bufferData->acquireFence);
-                    // Because we just released a buffer, we know there's no need to wait for a free
-                    // buffer.
-                    mayNeedToWaitForBuffer = false;
                 }
             }
 
-            if (mayNeedToWaitForBuffer) {
-                flushAndWaitForFreeBuffer(_lock);
+            if (waitForTransactionCallback) {
+                // We are waiting on a previous sync's transaction callback so allow another sync
+                // transaction to proceed.
+                //
+                // We need to first flush out the transactions that were in between the two syncs.
+                // We do this by merging them into mSyncTransaction so any buffer merging will get
+                // a release callback invoked.
+                while (mNumFrameAvailable > 0) {
+                    // flush out the shadow queue
+                    acquireAndReleaseBuffer();
+                }
+            } else {
+                // Make sure the frame available count is 0 before proceeding with a sync to ensure
+                // the correct frame is used for the sync. The only way mNumFrameAvailable would be
+                // greater than 0 is if we already ran out of buffers previously. This means we
+                // need to flush the buffers before proceeding with the sync.
+                while (mNumFrameAvailable > 0) {
+                    BQA_LOGD("waiting until no queued buffers");
+                    mCallbackCV.wait(_lock);
+                }
             }
         }
 
         // add to shadow queue
         mNumFrameAvailable++;
-        if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+        if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
             acquireAndReleaseBuffer();
         }
         ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -676,21 +729,30 @@
                  item.mFrameNumber, boolToString(syncTransactionSet));
 
         if (syncTransactionSet) {
-            acquireNextBufferLocked(mSyncTransaction);
+            // Add to mSyncedFrameNumbers before waiting in case any buffers are released
+            // while waiting for a free buffer. The release and commit callback will try to
+            // acquire buffers if there are any available, but we don't want it to acquire
+            // in the case where a sync transaction wants the buffer.
+            mSyncedFrameNumbers.emplace(item.mFrameNumber);
+            // If there's no available buffer and we're in a sync transaction, we need to wait
+            // instead of returning since we guarantee a buffer will be acquired for the sync.
+            while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) {
+                BQA_LOGD("waiting for available buffer");
+                mCallbackCV.wait(_lock);
+            }
 
             // Only need a commit callback when syncing to ensure the buffer that's synced has been
             // sent to SF
             incStrong((void*)transactionCommittedCallbackThunk);
             mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
                                                               static_cast<void*>(this));
-            mWaitForTransactionCallback = true;
             if (mAcquireSingleBuffer) {
                 prevCallback = mTransactionReadyCallback;
                 prevTransaction = mSyncTransaction;
                 mTransactionReadyCallback = nullptr;
                 mSyncTransaction = nullptr;
             }
-        } else if (!mWaitForTransactionCallback) {
+        } else if (!waitForTransactionCallback) {
             acquireNextBufferLocked(std::nullopt);
         }
     }
@@ -786,15 +848,6 @@
     return mSize != bufferSize;
 }
 
-// Check if we have acquired the maximum number of buffers.
-// Consumer can acquire an additional buffer if that buffer is not droppable. Set
-// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
-// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
-bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
-    int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
-    return mNumAcquired >= maxAcquiredBuffers;
-}
-
 class BBQSurface : public Surface {
 private:
     std::mutex mMutex;
@@ -831,12 +884,13 @@
         return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
     }
 
-    status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+    status_t setFrameTimelineInfo(uint64_t frameNumber,
+                                  const FrameTimelineInfo& frameTimelineInfo) override {
         std::unique_lock _lock{mMutex};
         if (mDestroyed) {
             return DEAD_OBJECT;
         }
-        return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+        return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo);
     }
 
     void destroy() override {
@@ -858,9 +912,12 @@
     return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
 }
 
-status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber,
+                                                const FrameTimelineInfo& frameTimelineInfo) {
+    ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(),
+                  frameNumber, frameTimelineInfo.vsyncId);
     std::unique_lock _lock{mMutex};
-    mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+    mPendingFrameTimelines.push({frameNumber, frameTimelineInfo});
     return OK;
 }
 
@@ -1001,8 +1058,9 @@
 // can be non-blocking when the producer is in the client process.
 class BBQBufferQueueProducer : public BufferQueueProducer {
 public:
-    BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
-          : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+    BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
+          : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
+            mBLASTBufferQueue(std::move(bbq)) {}
 
     status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
                      QueueBufferOutput* output) override {
@@ -1014,6 +1072,26 @@
                                             producerControlledByApp, output);
     }
 
+    // We want to resize the frame history when changing the size of the buffer queue
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
+        int maxBufferCount;
+        status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+                                                                         &maxBufferCount);
+        // if we can't determine the max buffer count, then just skip growing the history size
+        if (status == OK) {
+            size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+            // optimize away resizing the frame history unless it will grow
+            if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+                sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+                if (bbq != nullptr) {
+                    ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+                    bbq->resizeFrameEventHistory(newFrameHistorySize);
+                }
+            }
+        }
+        return status;
+    }
+
     int query(int what, int* value) override {
         if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
             *value = 1;
@@ -1021,6 +1099,9 @@
         }
         return BufferQueueProducer::query(what, value);
     }
+
+private:
+    const wp<BLASTBufferQueue> mBLASTBufferQueue;
 };
 
 // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
@@ -1035,7 +1116,7 @@
     sp<BufferQueueCore> core(new BufferQueueCore());
     LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
 
-    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
     LOG_ALWAYS_FATAL_IF(producer == nullptr,
                         "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
 
@@ -1048,6 +1129,16 @@
     *outConsumer = consumer;
 }
 
+void BLASTBufferQueue::resizeFrameEventHistory(size_t newSize) {
+    // This can be null during creation of the buffer queue, but resizing won't do anything at that
+    // point in time, so just ignore. This can go away once the class relationships and lifetimes of
+    // objects are cleaned up with a major refactor of BufferQueue as a whole.
+    if (mBufferItemConsumer != nullptr) {
+        std::unique_lock _lock{mMutex};
+        mBufferItemConsumer->resizeFrameEventHistory(newSize);
+    }
+}
+
 PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) {
     PixelFormat convertedFormat = format;
     switch (format) {
@@ -1097,9 +1188,9 @@
     }
 
     // Clear sync states
-    if (mWaitForTransactionCallback) {
-        BQA_LOGD("mWaitForTransactionCallback cleared");
-        mWaitForTransactionCallback = false;
+    if (!mSyncedFrameNumbers.empty()) {
+        BQA_LOGD("mSyncedFrameNumbers cleared");
+        mSyncedFrameNumbers.clear();
     }
 
     if (mSyncTransaction != nullptr) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5fe5e71..f934680 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -119,6 +119,12 @@
 
 status_t BufferQueueProducer::setMaxDequeuedBufferCount(
         int maxDequeuedBuffers) {
+    int maxBufferCount;
+    return setMaxDequeuedBufferCount(maxDequeuedBuffers, &maxBufferCount);
+}
+
+status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
+                                                        int* maxBufferCount) {
     ATRACE_CALL();
     BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
             maxDequeuedBuffers);
@@ -134,6 +140,8 @@
             return NO_INIT;
         }
 
+        *maxBufferCount = mCore->getMaxBufferCountLocked();
+
         if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) {
             return NO_ERROR;
         }
@@ -183,6 +191,7 @@
             return BAD_VALUE;
         }
         mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+        *maxBufferCount = mCore->getMaxBufferCountLocked();
         VALIDATE_CONSISTENCY();
         if (delta < 0) {
             listener = mCore->mConsumerListener;
@@ -606,7 +615,8 @@
     BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
             *outSlot,
             mSlots[*outSlot].mFrameNumber,
-            mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+            mSlots[*outSlot].mGraphicBuffer != nullptr ?
+            mSlots[*outSlot].mGraphicBuffer->handle : nullptr, returnFlags);
 
     if (outBufferAge) {
         *outBufferAge = mCore->mBufferAge;
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index e2ea3f9..f3eb4e8 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -168,10 +168,11 @@
 
 }  // namespace
 
-const size_t FrameEventHistory::MAX_FRAME_HISTORY =
+const size_t FrameEventHistory::INITIAL_MAX_FRAME_HISTORY =
         sysprop::LibGuiProperties::frame_event_history_size().value_or(8);
 
-FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {}
+FrameEventHistory::FrameEventHistory()
+      : mFrames(std::vector<FrameEvents>(INITIAL_MAX_FRAME_HISTORY)) {}
 
 FrameEventHistory::~FrameEventHistory() = default;
 
@@ -227,7 +228,6 @@
     }
 }
 
-
 // ============================================================================
 // ProducerFrameEventHistory
 // ============================================================================
@@ -273,6 +273,13 @@
         const FrameEventHistoryDelta& delta) {
     mCompositorTiming = delta.mCompositorTiming;
 
+    // Deltas should have enough reserved capacity for the consumer-side, therefore if there's a
+    // different capacity, we re-sized on the consumer side and now need to resize on the producer
+    // side.
+    if (delta.mDeltas.capacity() > mFrames.capacity()) {
+        resize(delta.mDeltas.capacity());
+    }
+
     for (auto& d : delta.mDeltas) {
         // Avoid out-of-bounds access.
         if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
@@ -349,13 +356,48 @@
     return std::make_shared<FenceTime>(fence);
 }
 
+void ProducerFrameEventHistory::resize(size_t newSize) {
+    // we don't want to drop events by resizing too small, so don't resize in the negative direction
+    if (newSize <= mFrames.size()) {
+        return;
+    }
+
+    // This algorithm for resizing needs to be the same as ConsumerFrameEventHistory::resize,
+    // because the indexes need to match when communicating the FrameEventDeltas.
+
+    // We need to find the oldest frame, because that frame needs to move to index 0 in the new
+    // frame history.
+    size_t oldestFrameIndex = 0;
+    size_t oldestFrameNumber = INT32_MAX;
+    for (size_t i = 0; i < mFrames.size(); ++i) {
+        if (mFrames[i].frameNumber < oldestFrameNumber && mFrames[i].valid) {
+            oldestFrameNumber = mFrames[i].frameNumber;
+            oldestFrameIndex = i;
+        }
+    }
+
+    // move the existing frame information into a new vector, so that the oldest frames are at
+    // index 0, and the latest frames are at the end of the vector
+    std::vector<FrameEvents> newFrames(newSize);
+    size_t oldI = oldestFrameIndex;
+    size_t newI = 0;
+    do {
+        if (mFrames[oldI].valid) {
+            newFrames[newI++] = std::move(mFrames[oldI]);
+        }
+        oldI = (oldI + 1) % mFrames.size();
+    } while (oldI != oldestFrameIndex);
+
+    mFrames = std::move(newFrames);
+    mAcquireOffset = 0; // this is just a hint, so setting this to anything is fine
+}
 
 // ============================================================================
 // ConsumerFrameEventHistory
 // ============================================================================
 
 ConsumerFrameEventHistory::ConsumerFrameEventHistory()
-      : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {}
+      : mFramesDirty(std::vector<FrameEventDirtyFields>(INITIAL_MAX_FRAME_HISTORY)) {}
 
 ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
 
@@ -489,6 +531,36 @@
     }
 }
 
+void ConsumerFrameEventHistory::resize(size_t newSize) {
+    // we don't want to drop events by resizing too small, so don't resize in the negative direction
+    if (newSize <= mFrames.size()) {
+        return;
+    }
+
+    // This algorithm for resizing needs to be the same as ProducerFrameEventHistory::resize,
+    // because the indexes need to match when communicating the FrameEventDeltas.
+
+    // move the existing frame information into a new vector, so that the oldest frames are at
+    // index 0, and the latest frames are towards the end of the vector
+    std::vector<FrameEvents> newFrames(newSize);
+    std::vector<FrameEventDirtyFields> newFramesDirty(newSize);
+    size_t oldestFrameIndex = mQueueOffset;
+    size_t oldI = oldestFrameIndex;
+    size_t newI = 0;
+    do {
+        if (mFrames[oldI].valid) {
+            newFrames[newI] = std::move(mFrames[oldI]);
+            newFramesDirty[newI] = mFramesDirty[oldI];
+            newI += 1;
+        }
+        oldI = (oldI + 1) % mFrames.size();
+    } while (oldI != oldestFrameIndex);
+
+    mFrames = std::move(newFrames);
+    mFramesDirty = std::move(newFramesDirty);
+    mQueueOffset = newI;
+    mCompositionOffset = 0; // this is just a hint, so setting this to anything is fine
+}
 
 // ============================================================================
 // FrameEventsDelta
@@ -558,8 +630,7 @@
         return NO_MEMORY;
     }
 
-    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
-            mIndex > std::numeric_limits<uint16_t>::max()) {
+    if (mIndex >= UINT8_MAX || mIndex < 0) {
         return BAD_VALUE;
     }
 
@@ -601,7 +672,7 @@
     uint16_t temp16 = 0;
     FlattenableUtils::read(buffer, size, temp16);
     mIndex = temp16;
-    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (mIndex >= UINT8_MAX) {
         return BAD_VALUE;
     }
     uint8_t temp8 = 0;
@@ -627,6 +698,25 @@
     return NO_ERROR;
 }
 
+uint64_t FrameEventsDelta::getFrameNumber() const {
+    return mFrameNumber;
+}
+
+bool FrameEventsDelta::getLatchTime(nsecs_t* latchTime) const {
+    if (mLatchTime == FrameEvents::TIMESTAMP_PENDING) {
+        return false;
+    }
+    *latchTime = mLatchTime;
+    return true;
+}
+
+bool FrameEventsDelta::getDisplayPresentFence(sp<Fence>* fence) const {
+    if (mDisplayPresentFence.fence == Fence::NO_FENCE) {
+        return false;
+    }
+    *fence = mDisplayPresentFence.fence;
+    return true;
+}
 
 // ============================================================================
 // FrameEventHistoryDelta
@@ -665,7 +755,7 @@
 
 status_t FrameEventHistoryDelta::flatten(
             void*& buffer, size_t& size, int*& fds, size_t& count) const {
-    if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (mDeltas.size() > UINT8_MAX) {
         return BAD_VALUE;
     }
     if (size < getFlattenedSize()) {
@@ -695,7 +785,7 @@
 
     uint32_t deltaCount = 0;
     FlattenableUtils::read(buffer, size, deltaCount);
-    if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (deltaCount > UINT8_MAX) {
         return BAD_VALUE;
     }
     mDeltas.resize(deltaCount);
@@ -708,5 +798,12 @@
     return NO_ERROR;
 }
 
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::begin() const {
+    return mDeltas.begin();
+}
+
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::end() const {
+    return mDeltas.end();
+}
 
 } // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 502031c..74e6ae6 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -353,6 +353,27 @@
     }
 }
 
+void DisplayState::sanitize(int32_t permissions) {
+    if (what & DisplayState::eLayerStackChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eLayerStackChanged;
+            ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+        }
+    }
+    if (what & DisplayState::eDisplayProjectionChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eDisplayProjectionChanged;
+            ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+        }
+    }
+    if (what & DisplayState::eSurfaceChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eSurfaceChanged;
+            ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+        }
+    }
+}
+
 void layer_state_t::sanitize(int32_t permissions) {
     // TODO: b/109894387
     //
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 05b5533..826a418 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,12 +1,7 @@
-adyabr@google.com
-alecmouri@google.com
-chaviw@google.com
 chrisforbes@google.com
 jreck@google.com
-lpy@google.com
-pdwilliams@google.com
-racarr@google.com
-vishnun@google.com
+
+file:/services/surfaceflinger/OWNERS
 
 per-file EndToEndNativeInputTest.cpp = svv@google.com
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 3b13708..16edfd4 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -792,11 +792,15 @@
         return result;
     }
 
-    std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested);
+    std::vector<CancelBufferInput> cancelBufferInputs;
+    cancelBufferInputs.reserve(numBufferRequested);
     std::vector<status_t> cancelBufferOutputs;
     for (size_t i = 0; i < numBufferRequested; i++) {
-        cancelBufferInputs[i].slot = dequeueOutput[i].slot;
-        cancelBufferInputs[i].fence = dequeueOutput[i].fence;
+        if (dequeueOutput[i].result >= 0) {
+            CancelBufferInput& input = cancelBufferInputs.emplace_back();
+            input.slot = dequeueOutput[i].slot;
+            input.fence = dequeueOutput[i].fence;
+        }
     }
 
     for (const auto& output : dequeueOutput) {
@@ -1865,12 +1869,13 @@
 
 int Surface::dispatchSetFrameTimelineInfo(va_list args) {
     ATRACE_CALL();
+    auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
     auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
     auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
     auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
 
     ALOGV("Surface::%s", __func__);
-    return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
+    return setFrameTimelineInfo(frameNumber, {frameTimelineVsyncId, inputEventId, startTimeNanos});
 }
 
 bool Surface::transformToDisplayInverse() const {
@@ -2644,7 +2649,8 @@
                                            changeFrameRateStrategy);
 }
 
-status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/,
+                                       const FrameTimelineInfo& frameTimelineInfo) {
     return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 47d801a..05beb07 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -352,7 +352,8 @@
                                       transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats);
+                                      surfaceStats.eventStats,
+                                      surfaceStats.currentMaxAcquiredBufferCount);
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -377,7 +378,8 @@
                                       transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats);
+                                      surfaceStats.eventStats,
+                                      surfaceStats.currentMaxAcquiredBufferCount);
                 if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
                     callbacksMap[callbackId]
                             .surfaceControls[surfaceStats.surfaceControl]
@@ -897,6 +899,10 @@
     mApplyToken = nullptr;
 }
 
+uint64_t SurfaceComposerClient::Transaction::getId() {
+    return mId;
+}
+
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
@@ -1268,8 +1274,11 @@
         mStatus = BAD_INDEX;
         return *this;
     }
+    if (alpha < 0.0f || alpha > 1.0f) {
+        ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha);
+    }
     s->what |= layer_state_t::eAlphaChanged;
-    s->alpha = alpha;
+    s->alpha = std::clamp(alpha, 0.f, 1.f);
 
     registerSurfaceControlForCallback(sc);
     return *this;
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 55462c3..9667954 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -725,12 +725,7 @@
     // These were written as uint8_t for alignment.
     uint8_t temp = 0;
     FlattenableUtils::read(buffer, size, temp);
-    size_t index = static_cast<size_t>(temp);
-    if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
-        return BAD_VALUE;
-    }
-    t->index = static_cast<uint32_t>(index);
-
+    t->index = static_cast<uint32_t>(temp);
     FlattenableUtils::read(buffer, size, temp);
     t->addPostCompositeCalled = static_cast<bool>(temp);
     FlattenableUtils::read(buffer, size, temp);
@@ -786,8 +781,7 @@
 status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
         void*& buffer, size_t& size, int*& fds, size_t numFds) {
     // Check that t.index is within a valid range.
-    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
-            || t.index > std::numeric_limits<uint8_t>::max()) {
+    if (t.index > UINT8_MAX || t.index < 0) {
         return BAD_VALUE;
     }
 
@@ -887,8 +881,7 @@
 
     uint32_t deltaCount = 0;
     FlattenableUtils::read(buffer, size, deltaCount);
-    if (static_cast<size_t>(deltaCount) >
-            ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (deltaCount > UINT8_MAX) {
         return BAD_VALUE;
     }
     t->deltas.resize(deltaCount);
@@ -919,7 +912,7 @@
 status_t flatten(
         HGraphicBufferProducer::FrameEventHistoryDelta const& t,
         void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (t.deltas.size() > UINT8_MAX) {
         return BAD_VALUE;
     }
     if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index cee1b81..f684874 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -725,8 +725,7 @@
         std::vector<native_handle_t*>* nh,
         void*& buffer, size_t& size, int*& fds, size_t numFds) {
     // Check that t.index is within a valid range.
-    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
-            || t.index > std::numeric_limits<uint8_t>::max()) {
+    if (t.index > UINT8_MAX || t.index < 0) {
         return BAD_VALUE;
     }
 
@@ -829,7 +828,7 @@
         HGraphicBufferProducer::FrameEventHistoryDelta const& t,
         std::vector<std::vector<native_handle_t*> >* nh,
         void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+    if (t.deltas.size() > UINT8_MAX) {
         return BAD_VALUE;
     }
     if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 9328a54..40ffea6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -54,6 +54,8 @@
                                nsecs_t dequeueReadyTime) REQUIRES(mMutex);
     void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
 
+    void resizeFrameEventHistory(size_t newSize);
+
 protected:
     void onSidebandStreamChanged() override REQUIRES(mMutex);
 
@@ -95,9 +97,13 @@
                                      const std::vector<SurfaceControlStats>& stats);
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
                                std::optional<uint32_t> currentMaxAcquiredBufferCount);
+    void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+                                     std::optional<uint32_t> currentMaxAcquiredBufferCount,
+                                     bool fakeRelease);
     void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
                              bool acquireSingleBuffer = true);
     void stopContinuousSyncTransaction();
+
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void applyPendingTransactions(uint64_t frameNumber);
     SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
@@ -105,7 +111,7 @@
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
-    status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
 
     void setSidebandStream(const sp<NativeHandle>& stream);
 
@@ -125,6 +131,7 @@
 
 private:
     friend class BLASTBufferQueueHelper;
+    friend class BBQBufferQueueProducer;
 
     // can't be copied
     BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
@@ -132,12 +139,13 @@
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                            sp<IGraphicBufferConsumer>* outConsumer);
 
-    void acquireNextBufferLocked(
+    void resizeFrameEventHistory(size_t newSize);
+
+    status_t acquireNextBufferLocked(
             const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
     Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
     // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
     bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
-    bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
     static PixelFormat convertBufferFormat(PixelFormat& format);
     void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
             REQUIRES(mMutex);
@@ -146,7 +154,6 @@
     void acquireAndReleaseBuffer() REQUIRES(mMutex);
     void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
             REQUIRES(mMutex);
-    void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
 
     std::string mName;
     // Represents the queued buffer count from buffer queue,
@@ -177,6 +184,12 @@
     struct ReleasedBuffer {
         ReleaseCallbackId callbackId;
         sp<Fence> releaseFence;
+        bool operator==(const ReleasedBuffer& rhs) const {
+            // Only compare Id so if we somehow got two callbacks
+            // with different fences we don't decrement mNumAcquired
+            // too far.
+            return rhs.callbackId == callbackId;
+        }
     };
     std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
 
@@ -229,7 +242,7 @@
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
-    std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+    std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
 
     // Tracks the last acquired frame number
     uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
@@ -251,7 +264,6 @@
     std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
 
     uint32_t mCurrentMaxAcquiredBufferCount;
-    bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
 
     // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
     // continue to acquire buffers until explicitly cleared
@@ -279,6 +291,8 @@
     uint64_t mLastAppliedFrameNumber = 0;
 
     std::function<void(bool)> mTransactionHangCallback;
+
+    std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 0ad3075..1d13dab 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -202,6 +202,11 @@
     // See IGraphicBufferProducer::setAutoPrerotation
     virtual status_t setAutoPrerotation(bool autoPrerotation);
 
+protected:
+    // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
+    // total maximum buffer count for the buffer queue (dequeued AND acquired)
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index dd3de58..968aa2b 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -114,9 +114,11 @@
     void checkFencesForCompletion();
     void dump(std::string& outString) const;
 
-    static const size_t MAX_FRAME_HISTORY;
+    static const size_t INITIAL_MAX_FRAME_HISTORY;
 
 protected:
+    void resize(size_t newSize);
+
     std::vector<FrameEvents> mFrames;
 
     CompositorTiming mCompositorTiming;
@@ -155,6 +157,8 @@
     virtual std::shared_ptr<FenceTime> createFenceTime(
             const sp<Fence>& fence) const;
 
+    void resize(size_t newSize);
+
     size_t mAcquireOffset{0};
 
     // The consumer updates it's timelines in Layer and SurfaceFlinger since
@@ -225,6 +229,8 @@
 
     void getAndResetDelta(FrameEventHistoryDelta* delta);
 
+    void resize(size_t newSize);
+
 private:
     void getFrameDelta(FrameEventHistoryDelta* delta,
                        const std::vector<FrameEvents>::iterator& frame);
@@ -267,6 +273,10 @@
     status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
             size_t& count);
 
+    uint64_t getFrameNumber() const;
+    bool getLatchTime(nsecs_t* latchTime) const;
+    bool getDisplayPresentFence(sp<Fence>* fence) const;
+
 private:
     static constexpr size_t minFlattenedSize();
 
@@ -327,6 +337,9 @@
     status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
             size_t& count);
 
+    std::vector<FrameEventsDelta>::const_iterator begin() const;
+    std::vector<FrameEventsDelta>::const_iterator end() const;
+
 private:
     static constexpr size_t minFlattenedSize();
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 0a9b75a..0071d48 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -320,6 +320,7 @@
 
     DisplayState();
     void merge(const DisplayState& other);
+    void sanitize(int32_t permissions);
 
     uint32_t what = 0;
     uint32_t flags = 0;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 862a4ad..4a552b6 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -111,6 +111,24 @@
         return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr;
     }
 
+    static sp<IGraphicBufferProducer> getIGraphicBufferProducer(ANativeWindow* window) {
+        int val;
+        if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 &&
+            val == NATIVE_WINDOW_SURFACE) {
+            return ((Surface*) window)->mGraphicBufferProducer;
+        }
+        return nullptr;
+    }
+
+    static sp<IBinder> getSurfaceControlHandle(ANativeWindow* window) {
+        int val;
+        if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 &&
+            val == NATIVE_WINDOW_SURFACE) {
+            return ((Surface*) window)->mSurfaceControlHandle;
+        }
+        return nullptr;
+    }
+
     /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer.
      *
      * A sideband stream is a device-specific mechanism for passing buffers
@@ -193,7 +211,7 @@
 
     virtual status_t setFrameRate(float frameRate, int8_t compatibility,
                                   int8_t changeFrameRateStrategy);
-    virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
 
 protected:
     virtual ~Surface();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index efbdb36..9033e17 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -65,14 +65,16 @@
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
                         std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
                         const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
-                        uint32_t hint, FrameEventHistoryStats eventStats)
+                        uint32_t hint, FrameEventHistoryStats eventStats,
+                        uint32_t currentMaxAcquiredBufferCount)
           : surfaceControl(sc),
             latchTime(latchTime),
             acquireTimeOrFence(std::move(acquireTimeOrFence)),
             presentFence(presentFence),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
-            frameEventStats(eventStats) {}
+            frameEventStats(eventStats),
+            currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
 
     sp<SurfaceControl> surfaceControl;
     nsecs_t latchTime = -1;
@@ -81,6 +83,7 @@
     sp<Fence> previousReleaseFence;
     uint32_t transformHint = 0;
     FrameEventHistoryStats frameEventStats;
+    uint32_t currentMaxAcquiredBufferCount = 0;
 };
 
 using TransactionCompletedCallbackTakesContext =
@@ -461,6 +464,10 @@
         // Clears the contents of the transaction without applying it.
         void clear();
 
+        // Returns the current id of the transaction.
+        // The id is updated every time the transaction is applied.
+        uint64_t getId();
+
         status_t apply(bool synchronous = false, bool oneWay = false);
         // Merge another transaction in to this one, clearing other
         // as if it had been applied.
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index e5d2684..0009615 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -27,6 +27,8 @@
 
 #define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
 
+#define ATRACE_FORMAT_INSTANT(fmt, ...) TraceUtils::intantFormat(fmt, ##__VA_ARGS__)
+
 namespace android {
 
 class TraceUtils {
@@ -50,6 +52,20 @@
         ATRACE_BEGIN(buf);
     }
 
+    static void intantFormat(const char* fmt, ...) {
+        if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+        const int BUFFER_SIZE = 256;
+        va_list ap;
+        char buf[BUFFER_SIZE];
+
+        va_start(ap, fmt);
+        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+        va_end(ap);
+
+        ATRACE_INSTANT(buf);
+    }
+
 }; // class TraceUtils
 
 } /* namespace android */
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 62d1496..08785b4 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,8 @@
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
                                        nsecs_t latchTime) const {
             const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
-                         previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
+                         previousReleaseFence, transformHint, frameEvents, ignore] =
+                surfaceControlStats;
 
             ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
             ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index fa54c7d..0702e0f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -90,6 +90,9 @@
     compile_multilib: "both",
 
     header_libs: ["libsurfaceflinger_headers"],
+    data: [
+        ":libgui_test",
+    ],
 }
 
 cc_test {
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index cb7e94c..b993289 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -161,6 +161,10 @@
         ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
     }
 
+    void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) {
+        mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
+    }
+
 private:
     sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
 };
@@ -1111,6 +1115,39 @@
     ASSERT_TRUE(receivedCallback);
 }
 
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    Transaction sync;
+    adapter.setSyncTransaction(sync);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    // Merge a transaction that has a complete callback into the next frame so we can get notified
+    // when to take a screenshot
+    CallbackHelper transactionCallback;
+    Transaction t;
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    adapter.mergeWithNextTransaction(&t, 2);
+    queueBuffer(igbProducer, r, g, b, 0);
+
+    // Drop the buffer, but ensure the next one continues to get processed.
+    sync.setBuffer(mSurfaceControl, nullptr);
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
 // This test will currently fail because the old surfacecontrol will steal the last presented buffer
 // until the old surface control is destroyed. This is not necessarily a bug but to document a
 // limitation with the update API and to test any changes to make the api more robust. The current
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c6cdeb7..2637f59 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -1184,18 +1184,23 @@
     std::vector<sp<IGraphicBufferProducer>> mProducers;
 };
 
-TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) {
     ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
     // Do not create a display associated with the LayerStack.
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
     surface->showAt(100, 100);
 
+    // Touches should be dropped if the layer is on an invalid display.
     injectTapOnDisplay(101, 101, layerStack.id);
-    surface->requestFocus(layerStack.id);
-    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
-
     EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    // However, we still let the window be focused and receive keys.
+    surface->requestFocus(layerStack.id);
+    surface->assertFocusChange(true);
+
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+    surface->expectKey(AKEYCODE_V);
 }
 
 TEST_F(MultiDisplayTests, virtual_display_receives_input) {
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
 
 #define LOG_TAG "Surface"
 
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
 #include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
 
 namespace android {
 namespace view {
 
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+                                                       Parcel* _Nonnull parcel) {
+    int value;
+    int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+    if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+        ALOGE("Error: ANativeWindow is not backed by Surface");
+        return STATUS_BAD_VALUE;
+    }
+    // Use a android::view::Surface to parcelize the window
+    android::view::Surface shimSurface;
+    shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+    shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+    return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+        const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+    // Use a android::view::Surface to unparcel the window
+    android::view::Surface shimSurface;
+    status_t ret = shimSurface.readFromParcel(parcel);
+    if (ret != OK) {
+        ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+        return STATUS_BAD_VALUE;
+    }
+    auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+                                              shimSurface.surfaceControlHandle);
+    ANativeWindow* anw = surface.get();
+    ANativeWindow_acquire(anw);
+    *outWindow = anw;
+    return STATUS_OK;
+}
+
+#endif
+
 status_t Surface::writeToParcel(Parcel* parcel) const {
     return writeToParcel(parcel, false);
 }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 4127f7c..2581668 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -64,9 +64,10 @@
 }
 
 bool shouldDisregardTransformation(uint32_t source) {
-    // Do not apply any transformations to axes from joysticks or touchpads.
+    // Do not apply any transformations to axes from joysticks, touchpads, or relative mice.
     return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
-            isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+            isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) ||
+            isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE);
 }
 
 bool shouldDisregardOffset(uint32_t source) {
@@ -89,6 +90,25 @@
     }
 }
 
+const char* motionToolTypeToString(int32_t toolType) {
+    switch (toolType) {
+        case AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
+            return "UNKNOWN";
+        case AMOTION_EVENT_TOOL_TYPE_FINGER:
+            return "FINGER";
+        case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+            return "STYLUS";
+        case AMOTION_EVENT_TOOL_TYPE_MOUSE:
+            return "MOUSE";
+        case AMOTION_EVENT_TOOL_TYPE_ERASER:
+            return "ERASER";
+        case AMOTION_EVENT_TOOL_TYPE_PALM:
+            return "PALM";
+        default:
+            return "INVALID";
+    }
+}
+
 // --- IdGenerator ---
 IdGenerator::IdGenerator(Source source) : mSource(source) {}
 
@@ -117,10 +137,23 @@
 
 // --- InputEvent ---
 
+// Due to precision limitations when working with floating points, transforming - namely
+// scaling - floating points can lead to minute errors. We round transformed values to approximately
+// three decimal places so that values like 0.99997 show up as 1.0.
+inline float roundTransformedCoords(float val) {
+    // Use a power to two to approximate three decimal places to potentially reduce some cycles.
+    // This should be at least as precise as MotionEvent::ROUNDING_PRECISION.
+    return std::round(val * 1024.f) / 1024.f;
+}
+
+inline vec2 roundTransformedCoords(vec2 p) {
+    return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)};
+}
+
 vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
     const vec2 transformedXy = transform.transform(xy);
     const vec2 transformedOrigin = transform.transform(0, 0);
-    return transformedXy - transformedOrigin;
+    return roundTransformedCoords(transformedXy - transformedOrigin);
 }
 
 const char* inputEventTypeToString(int32_t type) {
@@ -528,12 +561,12 @@
 
 float MotionEvent::getXCursorPosition() const {
     vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
-    return vals.x;
+    return roundTransformedCoords(vals.x);
 }
 
 float MotionEvent::getYCursorPosition() const {
     vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
-    return vals.y;
+    return roundTransformedCoords(vals.y);
 }
 
 void MotionEvent::setCursorPosition(float x, float y) {
@@ -855,7 +888,7 @@
 static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
                                                    const vec2& xy) {
     return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
-                                         : transform.transform(xy);
+                                         : roundTransformedCoords(transform.transform(xy));
 }
 
 vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index c0aa2e2..2d768ce 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -314,7 +314,23 @@
     DEFINE_KEYCODE(REFRESH), \
     DEFINE_KEYCODE(THUMBS_UP), \
     DEFINE_KEYCODE(THUMBS_DOWN), \
-    DEFINE_KEYCODE(PROFILE_SWITCH)
+    DEFINE_KEYCODE(PROFILE_SWITCH), \
+    DEFINE_KEYCODE(VIDEO_APP_1), \
+    DEFINE_KEYCODE(VIDEO_APP_2), \
+    DEFINE_KEYCODE(VIDEO_APP_3), \
+    DEFINE_KEYCODE(VIDEO_APP_4), \
+    DEFINE_KEYCODE(VIDEO_APP_5), \
+    DEFINE_KEYCODE(VIDEO_APP_6), \
+    DEFINE_KEYCODE(VIDEO_APP_7), \
+    DEFINE_KEYCODE(VIDEO_APP_8), \
+    DEFINE_KEYCODE(FEATURED_APP_1), \
+    DEFINE_KEYCODE(FEATURED_APP_2), \
+    DEFINE_KEYCODE(FEATURED_APP_3), \
+    DEFINE_KEYCODE(FEATURED_APP_4), \
+    DEFINE_KEYCODE(DEMO_APP_1), \
+    DEFINE_KEYCODE(DEMO_APP_2), \
+    DEFINE_KEYCODE(DEMO_APP_3), \
+    DEFINE_KEYCODE(DEMO_APP_4)
 
 // 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.
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index d6b4579..250c0dd 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -230,6 +230,16 @@
     return scanCodes;
 }
 
+std::vector<int32_t> KeyLayoutMap::findUsageCodesForKey(int32_t keyCode) const {
+    std::vector<int32_t> usageCodes;
+    for (const auto& [usageCode, key] : mKeysByUsageCode) {
+        if (keyCode == key.keyCode) {
+            usageCodes.push_back(usageCode);
+        }
+    }
+    return usageCodes;
+}
+
 std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const {
     auto it = mAxes.find(scanCode);
     if (it == mAxes.end()) {
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a92016b..597b389 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -29,6 +29,8 @@
 // Default display id.
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
 class BaseTest : public testing::Test {
 protected:
     static constexpr std::array<uint8_t, 32> HMAC = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
@@ -231,95 +233,107 @@
     static constexpr float RAW_X_OFFSET = 12;
     static constexpr float RAW_Y_OFFSET = -41.1;
 
+    void SetUp() override;
+
     int32_t mId;
     ui::Transform mTransform;
     ui::Transform mRawTransform;
+    PointerProperties mPointerProperties[2];
+    struct Sample {
+        PointerCoords pointerCoords[2];
+    };
+    std::array<Sample, 3> mSamples{};
 
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
-void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+void MotionEventTest::SetUp() {
     mId = InputEvent::nextId();
     mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
     mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
 
-    PointerProperties pointerProperties[2];
-    pointerProperties[0].clear();
-    pointerProperties[0].id = 1;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-    pointerProperties[1].clear();
-    pointerProperties[1].id = 2;
-    pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mPointerProperties[0].clear();
+    mPointerProperties[0].id = 1;
+    mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+    mPointerProperties[1].clear();
+    mPointerProperties[1].id = 2;
+    mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
 
-    PointerCoords pointerCoords[2];
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
-    pointerCoords[1].clear();
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+    mSamples[0].pointerCoords[0].clear();
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
+    mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
+    mSamples[0].pointerCoords[1].clear();
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
+    mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+
+    mSamples[1].pointerCoords[0].clear();
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
+    mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
+    mSamples[1].pointerCoords[1].clear();
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
+    mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
+
+    mSamples[2].pointerCoords[0].clear();
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
+    mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
+    mSamples[2].pointerCoords[1].clear();
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
+    mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
+}
+
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     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, mTransform, 2.0f, 2.1f,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                       mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
-                      pointerProperties, pointerCoords);
-
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
-    event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
-    pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
-    event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+                      mPointerProperties, mSamples[0].pointerCoords);
+    event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
+    event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
 }
 
 void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
@@ -356,51 +370,65 @@
     ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
     ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
 
-    ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    // Ensure the underlying PointerCoords are identical.
+    for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) {
+        for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) {
+            ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx],
+                      event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]);
+        }
+    }
 
-    ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
-              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
-    ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
-              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
-    ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
-              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
-    ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
-              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
-    ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
-    ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+    ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+                EPSILON);
+    ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+                EPSILON);
+    ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+                EPSILON);
+    ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+                EPSILON);
+    ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
+    ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
 
-    ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
-    ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
-    ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
-    ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
-    ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
-    ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
+    ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+                event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+                event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+                event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+                event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0),
+                EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1),
+                EPSILON);
 
-    ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
-    ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
-    ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
-    ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
-    ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
-    ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
+    ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON);
+    ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON);
+    ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON);
+    ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON);
+    ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON);
+    ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON);
 
-    ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
-    ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
-    ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
-    ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
-    ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
-    ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
+    ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON);
+    ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON);
 
-    ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
-    ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
-    ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
-    ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
-    ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
-    ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
+    ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON);
+    ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON);
+    ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON);
+    ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON);
+    ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON);
+    ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON);
+
+    ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON);
+    ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON);
+    ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON);
+    ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON);
+    ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON);
+    ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON);
 
     ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
     ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -532,10 +560,10 @@
     ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
     ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
 
-    ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
-    ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
-    ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
-    ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
+    ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
+    ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
+    ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON);
+    ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON);
     ASSERT_EQ(212, event.getPressure(0));
     ASSERT_EQ(213, event.getSize(0));
     ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -715,10 +743,10 @@
 }
 
 TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
-    constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
-                                                                    AMOTION_EVENT_ACTION_DOWN),
-                                                          std::pair(AINPUT_SOURCE_JOYSTICK,
-                                                                    AMOTION_EVENT_ACTION_MOVE)};
+    constexpr static std::array kNonTransformedSources =
+            {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN),
+             std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE),
+             std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)};
     // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
     ui::Transform transform(ui::Transform::ROT_90, 800, 400);
     transform.set(transform.tx() + 20, transform.ty() + 40);
@@ -738,7 +766,7 @@
 TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
     constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
                                                                 AMOTION_EVENT_ACTION_DOWN),
-                                                      std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+                                                      std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION,
                                                                 AMOTION_EVENT_ACTION_MOVE)};
     // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
     ui::Transform transform(ui::Transform::ROT_90, 800, 400);
@@ -773,18 +801,18 @@
 
     // The x and y axes should have the window transform applied.
     const auto newPoint = transform.transform(60, 100);
-    ASSERT_EQ(newPoint.x, event.getX(0));
-    ASSERT_EQ(newPoint.y, event.getY(0));
+    ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON);
+    ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON);
 
     // The raw values should have the display transform applied.
     const auto raw = rawTransform.transform(60, 100);
-    ASSERT_EQ(raw.x, event.getRawX(0));
-    ASSERT_EQ(raw.y, event.getRawY(0));
+    ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON);
+    ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON);
 
     // Relative values should have the window transform applied without any translation.
     const auto rel = transformWithoutTranslation(transform, 42, 96);
-    ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-    ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
+    ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON);
+    ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON);
 }
 
 TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -851,4 +879,42 @@
     ASSERT_EQ(4, event.getYCursorPosition());
 }
 
+TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) {
+    // These are specifically integral values, since we are testing for rounding.
+    const vec2 EXPECTED{400.f, 700.f};
+
+    // Pick a transform such that transforming the point with its inverse and bringing that
+    // back to the original coordinate space results in a non-zero error amount due to the
+    // nature of floating point arithmetics. This can happen when the display is scaled.
+    // For example, the 'adb shell wm size' command can be used to set an override for the
+    // logical display size, which could result in the display being scaled.
+    constexpr float scale = 720.f / 1080.f;
+    ui::Transform transform;
+    transform.set(scale, 0, 0, scale);
+    ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED)));
+
+    // Store the inverse-transformed values in the motion event.
+    const vec2 rawCoords = transform.inverse().transform(EXPECTED);
+    PointerCoords pc{};
+    pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x);
+    pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y);
+    PointerProperties pp{};
+    MotionEvent event;
+    event.initialize(InputEvent::nextId(), 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, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y,
+                     transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc);
+
+    // When using the getters from the MotionEvent to obtain the coordinates, the transformed
+    // values should be rounded by an appropriate amount so that they now precisely equal the
+    // original coordinates.
+    ASSERT_EQ(EXPECTED.x, event.getX(0));
+    ASSERT_EQ(EXPECTED.y, event.getY(0));
+    ASSERT_EQ(EXPECTED.x, event.getRawX(0));
+    ASSERT_EQ(EXPECTED.y, event.getRawY(0));
+    ASSERT_EQ(EXPECTED.x, event.getXCursorPosition());
+    ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 05bc0bc..8393e99 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -32,6 +32,8 @@
 
 namespace android {
 
+constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
 class InputPublisherAndConsumerTest : public testing::Test {
 protected:
     std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
@@ -233,10 +235,10 @@
     EXPECT_EQ(yOffset, motionEvent->getYOffset());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
-    EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
-    EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
-    EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
-    EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+    EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON);
+    EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON);
+    EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON);
+    EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON);
     EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
     EXPECT_EQ(eventTime, motionEvent->getEventTime());
@@ -249,10 +251,12 @@
         EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
 
         const auto& pc = pointerCoords[i];
-        EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
-        EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
-        EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
-        EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+        EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]);
+
+        EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON);
+        EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON);
+        EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON);
+        EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON);
         EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
         EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
         EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 4a1784e..2e0add5 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "AHardwareBuffer"
 
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
+#include <android/binder_libbinder.h>
 #include <vndk/hardware_buffer.h>
 
 #include <errno.h>
@@ -412,6 +415,25 @@
     return OK;
 }
 
+binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel,
+        AHardwareBuffer* _Nullable* _Nonnull outBuffer) {
+    if (!parcel || !outBuffer) return STATUS_BAD_VALUE;
+    auto buffer = sp<GraphicBuffer>::make();
+    status_t status = AParcel_viewPlatformParcel(parcel)->read(*buffer);
+    if (status != STATUS_OK) return status;
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get());
+    AHardwareBuffer_acquire(*outBuffer);
+    return STATUS_OK;
+}
+
+binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer,
+        AParcel* _Nonnull parcel) {
+    const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+    if (!gb) return STATUS_BAD_VALUE;
+    if (!parcel) return STATUS_BAD_VALUE;
+    return AParcel_viewPlatformParcel(parcel)->write(*gb);
+}
+
 // ----------------------------------------------------------------------------
 // VNDK functions
 // ----------------------------------------------------------------------------
@@ -593,15 +615,27 @@
     static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
                           AHARDWAREBUFFER_FORMAT_R8_UNORM,
             "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
+                          AHARDWAREBUFFER_FORMAT_R16_UINT,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
+                          AHARDWAREBUFFER_FORMAT_R16G16_UINT,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
+                          AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
 
     switch (format) {
         case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R16_UINT:
+        case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
         case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
         case AHARDWAREBUFFER_FORMAT_BLOB:
         case AHARDWAREBUFFER_FORMAT_D16_UNORM:
         case AHARDWAREBUFFER_FORMAT_D24_UNORM:
@@ -653,6 +687,7 @@
           return 1;
       case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
       case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+      case AHARDWAREBUFFER_FORMAT_R16_UINT:
           return 2;
       case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
       case AHARDWAREBUFFER_FORMAT_D24_UNORM:
@@ -662,8 +697,10 @@
       case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
       case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
       case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+      case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
           return 4;
       case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+      case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
           return 8;
       default:
           return 0;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 18a4b2d..73a05fc 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -20,13 +20,53 @@
 // from nativewindow/includes/system/window.h
 // (not to be confused with the compatibility-only window.h from system/core/includes)
 #include <system/window.h>
+#include <android/native_window_aidl.h>
 
 #include <private/android/AHardwareBufferHelpers.h>
 
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
+#include <log/log.h>
 #include <ui/GraphicBuffer.h>
 
 using namespace android;
 
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+                                                        Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+        status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+    android_view_Surface_writeToParcel write = nullptr;
+    android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+    static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+        SurfaceParcelables ret;
+        void* dl = dlopen("libgui.so", RTLD_NOW);
+        LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+        ret.write =
+                (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+        LOG_ALWAYS_FATAL_IF(!ret.write,
+                            "libgui.so missing android_view_Surface_writeToParcel; "
+                            "loaded wrong libgui?");
+        ret.read =
+                (android_view_Surface_readFromParcel)dlsym(dl,
+                                                           "android_view_Surface_readFromParcel");
+        LOG_ALWAYS_FATAL_IF(!ret.read,
+                            "libgui.so missing android_view_Surface_readFromParcel; "
+                            "loaded wrong libgui?");
+        return ret;
+    }();
+    return &funcs;
+}
+
 static int32_t query(ANativeWindow* window, int what) {
     int value;
     int res = window->query(window, what, &value);
@@ -334,6 +374,28 @@
     return native_window_set_auto_prerotation(window, autoPrerotation);
 }
 
+binder_status_t ANativeWindow_readFromParcel(
+        const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+    auto funcs = getSurfaceParcelFunctions();
+    if (funcs->read == nullptr) {
+        ALOGE("Failed to load Surface_readFromParcel implementation");
+        return STATUS_FAILED_TRANSACTION;
+    }
+    const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+    return funcs->read(nativeParcel, outWindow);
+}
+
+binder_status_t ANativeWindow_writeToParcel(
+        ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
+    auto funcs = getSurfaceParcelFunctions();
+    if (funcs->write == nullptr) {
+        ALOGE("Failed to load Surface_writeToParcel implementation");
+        return STATUS_FAILED_TRANSACTION;
+    }
+    Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+    return funcs->write(window, nativeParcel);
+}
+
 /**************************************************************************************************
  * apex-stable
  **************************************************************************************************/
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index cedc522..bc0bfc5 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -102,15 +102,19 @@
         "liblog",
         "libutils",
         "libui",
+        "libbinder",
+        "libbinder_ndk",
         "android.hardware.graphics.common@1.1",
     ],
 
     static_libs: [
         "libarect",
         "libgrallocusage",
+        "libgui_aidl_static",
     ],
 
     header_libs: [
+        "libgui_headers",
         "libarect_headers",
         "libnativebase_headers",
         "libnativewindow_headers",
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index c35507b..21798d0 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -173,6 +173,27 @@
      *   OpenGL ES: GR_GL_R8
      */
     AHARDWAREBUFFER_FORMAT_R8_UNORM                 = 0x38,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R16_UINT
+     *   OpenGL ES: GL_R16UI
+     */
+    AHARDWAREBUFFER_FORMAT_R16_UINT                 = 0x39,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R16G16_UINT
+     *   OpenGL ES: GL_RG16UI
+     */
+    AHARDWAREBUFFER_FORMAT_R16G16_UINT              = 0x3a,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16
+     *   OpenGL ES: N/A
+     */
+    AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM       = 0x3b,
 };
 
 /**
@@ -290,6 +311,16 @@
      */
     AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE   = 1UL << 26,
 
+    /**
+     * Usage: The buffer is used for front-buffer rendering. When
+     * front-buffering rendering is specified, different usages may adjust their
+     * behavior as a result. For example, when used as GPU_COLOR_OUTPUT the buffer
+     * will behave similar to a single-buffered window. When used with
+     * COMPOSER_OVERLAY, the system will try to prioritize the buffer receiving
+     * an overlay plane & avoid caching it in intermediate composition buffers.
+     */
+    AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32,
+
     AHARDWAREBUFFER_USAGE_VENDOR_0  = 1ULL << 28,
     AHARDWAREBUFFER_USAGE_VENDOR_1  = 1ULL << 29,
     AHARDWAREBUFFER_USAGE_VENDOR_2  = 1ULL << 30,
diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h
new file mode 100644
index 0000000..e269f0d
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file hardware_buffer_aidl.h
+ * @brief HardwareBuffer NDK AIDL glue code
+ */
+
+/**
+ * @addtogroup AHardwareBuffer
+ *
+ * Parcelable support for AHardwareBuffer. Can be used with libbinder_ndk
+ *
+ * @{
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_AIDL_H
+#define ANDROID_HARDWARE_BUFFER_AIDL_H
+
+#include <android/binder_parcel.h>
+#include <android/hardware_buffer.h>
+#include <sys/cdefs.h>
+
+#ifdef __cplusplus
+#include <string>
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * Read an AHardwareBuffer from a AParcel. The output buffer will have an
+ * initial reference acquired and will need to be released with
+ * AHardwareBuffer_release.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success
+ *         STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an
+ *                          issue deserializing (eg, corrupted parcel)
+ *         STATUS_BAD_TYPE if the parcel's current data position is not that of
+ *                         an AHardwareBuffer type
+ *         STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel,
+        AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(34);
+
+/**
+ * Write an AHardwareBuffer to an AParcel.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success.
+ *         STATUS_BAD_VALUE if either buffer or parcel is null, or if the AHardwareBuffer*
+ *                          fails to serialize (eg, internally corrupted)
+ *         STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is
+ *                          unable to allocate more
+ *         STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer,
+        AParcel* _Nonnull parcel) __INTRODUCED_IN(34);
+
+__END_DECLS
+
+// Only enable the AIDL glue helper if this is C++
+#ifdef __cplusplus
+
+namespace aidl::android::hardware {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * Takes ownership of the AHardwareBuffer* given to it in reset() and will automatically
+ * destroy it in the destructor, similar to a smart pointer container
+ */
+class HardwareBuffer {
+public:
+    HardwareBuffer() noexcept {}
+    HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {}
+
+    ~HardwareBuffer() {
+        reset();
+    }
+
+    binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+        reset();
+        return AHardwareBuffer_readFromParcel(parcel, &mBuffer);
+    }
+
+    binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+        if (!mBuffer) {
+            return STATUS_BAD_VALUE;
+        }
+        return AHardwareBuffer_writeToParcel(mBuffer, parcel);
+    }
+
+    /**
+     * Destroys any currently owned AHardwareBuffer* and takes ownership of the given
+     * AHardwareBuffer*
+     *
+     * @param buffer The buffer to take ownership of
+     */
+    void reset(AHardwareBuffer* _Nullable buffer = nullptr) noexcept {
+        if (mBuffer) {
+            AHardwareBuffer_release(mBuffer);
+            mBuffer = nullptr;
+        }
+        mBuffer = buffer;
+    }
+
+    inline AHardwareBuffer* _Nullable operator-> () const { return mBuffer;  }
+    inline AHardwareBuffer* _Nullable get() const { return mBuffer; }
+    inline explicit operator bool () const { return mBuffer != nullptr; }
+
+    inline bool operator!=(const HardwareBuffer& rhs) const { return get() != rhs.get(); }
+    inline bool operator<(const HardwareBuffer& rhs) const { return get() < rhs.get(); }
+    inline bool operator<=(const HardwareBuffer& rhs) const { return get() <= rhs.get(); }
+    inline bool operator==(const HardwareBuffer& rhs) const { return get() == rhs.get(); }
+    inline bool operator>(const HardwareBuffer& rhs) const { return get() > rhs.get(); }
+    inline bool operator>=(const HardwareBuffer& rhs) const { return get() >= rhs.get(); }
+
+    HardwareBuffer& operator=(HardwareBuffer&& other) noexcept {
+        reset(other.release());
+        return *this;
+    }
+
+    /**
+     * Stops managing any contained AHardwareBuffer*, returning it to the caller. Ownership
+     * is released.
+     * @return AHardwareBuffer* or null if this was empty
+     */
+    [[nodiscard]] AHardwareBuffer* _Nullable release() noexcept {
+        AHardwareBuffer* _Nullable ret = mBuffer;
+        mBuffer = nullptr;
+        return ret;
+    }
+
+    inline std::string toString() const {
+        if (!mBuffer) {
+            return "<HardwareBuffer: Invalid>";
+        }
+        uint64_t id = 0;
+        AHardwareBuffer_getId(mBuffer, &id);
+        return "<HardwareBuffer " + std::to_string(id) + ">";
+    }
+
+private:
+    HardwareBuffer(const HardwareBuffer& other) = delete;
+    HardwareBuffer& operator=(const HardwareBuffer& other) = delete;
+
+    AHardwareBuffer* _Nullable mBuffer = nullptr;
+};
+
+} // aidl::android::hardware
+
+#endif // __cplusplus
+
+#endif // ANDROID_HARDWARE_BUFFER_AIDL_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index f0e1c4d..a593cd4 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -333,7 +333,7 @@
         __INTRODUCED_IN(31);
 
 #ifdef __cplusplus
-};
+}
 #endif
 
 #endif // ANDROID_NATIVE_WINDOW_H
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
new file mode 100644
index 0000000..a252245
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file native_window_aidl.h
+ * @brief NativeWindow NDK AIDL glue code
+ */
+
+/**
+ * @addtogroup ANativeWindow
+ *
+ * Parcelable support for ANativeWindow. Can be used with libbinder_ndk
+ *
+ * @{
+ */
+
+#ifndef ANDROID_NATIVE_WINDOW_AIDL_H
+#define ANDROID_NATIVE_WINDOW_AIDL_H
+
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Read an ANativeWindow from a AParcel. The output buffer will have an
+ * initial reference acquired and will need to be released with
+ * ANativeWindow_release.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success
+ *         STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an
+ *                          issue deserializing (eg, corrupted parcel)
+ *         STATUS_BAD_TYPE if the parcel's current data position is not that of
+ *                         an ANativeWindow type
+ *         STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t ANativeWindow_readFromParcel(const AParcel* _Nonnull parcel,
+        ANativeWindow* _Nullable* _Nonnull outWindow) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Write an ANativeWindow to an AParcel.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success.
+ *         STATUS_BAD_VALUE if either buffer or parcel is null, or if the ANativeWindow*
+ *                          fails to serialize (eg, internally corrupted)
+ *         STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is
+ *                          unable to allocate more
+ *         STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t ANativeWindow_writeToParcel(ANativeWindow* _Nonnull window,
+        AParcel* _Nonnull parcel) __INTRODUCED_IN(__ANDROID_API_U__);
+
+__END_DECLS
+
+// Only enable the AIDL glue helper if this is C++
+#ifdef __cplusplus
+
+namespace aidl::android::hardware {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * Takes ownership of the ANativeWindow* given to it in reset() and will automatically
+ * destroy it in the destructor, similar to a smart pointer container
+ */
+class NativeWindow {
+public:
+    NativeWindow() noexcept {}
+    explicit NativeWindow(ANativeWindow* _Nullable window) {
+        reset(window);
+    }
+
+    explicit NativeWindow(NativeWindow&& other) noexcept {
+        mWindow = other.release(); // steal ownership from r-value
+    }
+
+    ~NativeWindow() {
+        reset();
+    }
+
+    binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+        reset();
+        return ANativeWindow_readFromParcel(parcel, &mWindow);
+    }
+
+    binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+        if (!mWindow) {
+            return STATUS_BAD_VALUE;
+        }
+        return ANativeWindow_writeToParcel(mWindow, parcel);
+    }
+
+    /**
+     * Destroys any currently owned ANativeWindow* and takes ownership of the given
+     * ANativeWindow*
+     *
+     * @param buffer The buffer to take ownership of
+     */
+    void reset(ANativeWindow* _Nullable window = nullptr) noexcept {
+        if (mWindow) {
+            ANativeWindow_release(mWindow);
+            mWindow = nullptr;
+        }
+        if (window != nullptr) {
+            ANativeWindow_acquire(window);
+        }
+        mWindow = window;
+    }
+    inline ANativeWindow* _Nullable operator-> () const { return mWindow;  }
+    inline ANativeWindow* _Nullable get() const { return mWindow; }
+    inline explicit operator bool () const { return mWindow != nullptr; }
+
+    NativeWindow& operator=(NativeWindow&& other) noexcept {
+        mWindow = other.release(); // steal ownership from r-value
+        return *this;
+    }
+
+    /**
+     * Stops managing any contained ANativeWindow*, returning it to the caller. Ownership
+     * is released.
+     * @return ANativeWindow* or null if this was empty
+     */
+    [[nodiscard]] ANativeWindow* _Nullable release() noexcept {
+        ANativeWindow* _Nullable ret = mWindow;
+        mWindow = nullptr;
+        return ret;
+    }
+private:
+    ANativeWindow* _Nullable mWindow = nullptr;
+    NativeWindow(const NativeWindow &other) = delete;
+    NativeWindow& operator=(const NativeWindow &other) = delete;
+};
+
+} // aidl::android::hardware
+  //
+namespace aidl::android::view {
+    using Surface = aidl::android::hardware::NativeWindow;
+}
+
+#endif // __cplusplus
+
+#endif // ANDROID_NATIVE_WINDOW_AIDL_H
+
+/** @} */
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index a54af1f..86e76c4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1043,11 +1043,12 @@
 }
 
 static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+                                                        uint64_t frameNumber,
                                                         int64_t frameTimelineVsyncId,
                                                         int32_t inputEventId,
                                                         int64_t startTimeNanos) {
-    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
-                           inputEventId, startTimeNanos);
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
+                           frameTimelineVsyncId, inputEventId, startTimeNanos);
 }
 
 // ------------------------------------------------------------------------------------------------
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index da42a96..d9ac568 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -14,6 +14,8 @@
     AHardwareBuffer_release;
     AHardwareBuffer_sendHandleToUnixSocket;
     AHardwareBuffer_unlock;
+    AHardwareBuffer_readFromParcel; # introduced=34
+    AHardwareBuffer_writeToParcel; # introduced=34
     ANativeWindowBuffer_getHardwareBuffer; # llndk
     ANativeWindow_OemStorageGet; # llndk
     ANativeWindow_OemStorageSet; # llndk
@@ -53,6 +55,8 @@
     ANativeWindow_setUsage; # llndk
     ANativeWindow_tryAllocateBuffers; # introduced=30
     ANativeWindow_unlockAndPost;
+    ANativeWindow_readFromParcel; # introduced=UpsideDownCake
+    ANativeWindow_writeToParcel; # introduced=UpsideDownCake
   local:
     *;
 };
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
 
     // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
     // and the middle part without rounded corners.
-    const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+    const int32_t radius = ceil(
+            (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
     const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
     setScissor(topRect);
     drawMesh(mesh);
@@ -1266,23 +1267,24 @@
 
         const half3 solidColor = layer.source.solidColor;
         const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+        const float radius =
+                (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+                2.0f;
         // Buffer sources will have a black solid color ignored in the shader,
         // so in that scenario the solid color passed here is arbitrary.
-        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                           layer.geometry.roundedCornersRadius);
+        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
         if (layer.disableBlending) {
             glDisable(GL_BLEND);
         }
         setSourceDataSpace(layer.sourceDataspace);
 
         if (layer.shadow.length > 0.0f) {
-            handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
-                         layer.shadow);
+            handleShadow(layer.geometry.boundaries, radius, layer.shadow);
         }
         // We only want to do a special handling for rounded corners when having rounded corners
         // is the only reason it needs to turn on blending, otherwise, we handle it like the
         // usual way since it needs to turn on blending anyway.
-        else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+        else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
             handleRoundedCorners(display, layer, mesh);
         } else {
             drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
     // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
     // in local layer coordinate space, so we have to take the layer transform into account when
     // walking up the tree.
-    float roundedCornersRadius = 0.0;
+    vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
 
     // Rectangle within which corners will be rounded.
     FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
     PrintTo(settings.boundaries, os);
     *os << "\n    .positionTransform = ";
     PrintMatrix(settings.positionTransform, os);
-    *os << "\n    .roundedCornersRadius = " << settings.roundedCornersRadius;
+    *os << "\n    .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+    *os << "\n    .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
     *os << "\n    .roundedCornersCrop = ";
     PrintTo(settings.roundedCornersCrop, os);
     *os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
                     Geometry{
                             .boundaries = rect,
                             .roundedCornersCrop = rect,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                     },
             // drawShadow ignores alpha
             .shadow =
@@ -87,7 +87,7 @@
                     Geometry{
                             .boundaries = smallerRect,
                             .roundedCornersCrop = rect,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                     },
             .source =
                     PixelSource{
@@ -148,7 +148,7 @@
         // In reduced shader mode, all non-zero round rect radii get the same code path.
         for (float roundedCornersRadius : {0.0f, 50.0f}) {
             // roundedCornersCrop is always set, but the radius triggers the behavior
-            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
             for (bool isOpaque : {true, false}) {
                 layer.source.buffer.isOpaque = isOpaque;
                 for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
-            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
             auto layers = std::vector<LayerSettings>{layer};
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                      base::unique_fd());
@@ -238,7 +238,7 @@
             .geometry =
                     Geometry{
                             .boundaries = rect,
-                            .roundedCornersRadius = 27, // larger than the 20 above.
+                            .roundedCornersRadius = {27.f, 27.f},
                             .roundedCornersCrop =
                                     FloatRect(0, 0, displayRect.width(), displayRect.height()),
                     },
@@ -275,9 +275,9 @@
                             // larger than the layer bounds.
                             .positionTransform = kFlip,
                             .boundaries = rect,
-                            .roundedCornersRadius = 94.2551,
-                            .roundedCornersCrop = FloatRect(
-                                -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+                            .roundedCornersRadius = {94.2551f, 94.2551f},
+                            .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+                                                            displayRect.height()),
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
@@ -307,10 +307,11 @@
                             // the boundaries have to be smaller than the rounded crop so that
                             // clipRRect is used instead of drawRRect
                             .boundaries = small,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                             .roundedCornersCrop = rect,
                     },
-            .source = PixelSource{
+            .source =
+                    PixelSource{
                             .solidColor = half3(0.f, 0.f, 0.f),
                     },
             .sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 97271cb..0caa9f2 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1276,7 +1276,7 @@
  *  produce the insected roundRect. If false, the returned state of the radii param is undefined.
  */
 static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
-                                    const SkRect& insetCrop, float cornerRadius,
+                                    const SkRect& insetCrop, const vec2& cornerRadius,
                                     SkVector radii[4]) {
     const bool leftEqual = bounds.fLeft == crop.fLeft;
     const bool topEqual = bounds.fTop == crop.fTop;
@@ -1288,8 +1288,8 @@
     // In particular the round rect implementation will scale the value of all corner radii
     // if the sum of the radius along any edge is greater than the length of that edge.
     // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
-    const bool requiredWidth = bounds.width() > (cornerRadius * 2);
-    const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+    const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+    const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
     if (!requiredWidth || !requiredHeight) {
         return false;
     }
@@ -1298,7 +1298,7 @@
     // contained within the cropped shape and does not need rounded.
     // compute the UpperLeft corner radius
     if (leftEqual && topEqual) {
-        radii[0].set(cornerRadius, cornerRadius);
+        radii[0].set(cornerRadius.x, cornerRadius.y);
     } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
                (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
         radii[0].set(0, 0);
@@ -1307,7 +1307,7 @@
     }
     // compute the UpperRight corner radius
     if (rightEqual && topEqual) {
-        radii[1].set(cornerRadius, cornerRadius);
+        radii[1].set(cornerRadius.x, cornerRadius.y);
     } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
                (topEqual && bounds.fRight <= insetCrop.fRight)) {
         radii[1].set(0, 0);
@@ -1316,7 +1316,7 @@
     }
     // compute the BottomRight corner radius
     if (rightEqual && bottomEqual) {
-        radii[2].set(cornerRadius, cornerRadius);
+        radii[2].set(cornerRadius.x, cornerRadius.y);
     } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
                (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
         radii[2].set(0, 0);
@@ -1325,7 +1325,7 @@
     }
     // compute the BottomLeft corner radius
     if (leftEqual && bottomEqual) {
-        radii[3].set(cornerRadius, cornerRadius);
+        radii[3].set(cornerRadius.x, cornerRadius.y);
     } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
                (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
         radii[3].set(0, 0);
@@ -1338,22 +1338,22 @@
 
 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
                                                                         const FloatRect& cropRect,
-                                                                        const float cornerRadius) {
+                                                                        const vec2& cornerRadius) {
     const SkRect bounds = getSkRect(boundsRect);
     const SkRect crop = getSkRect(cropRect);
 
     SkRRect clip;
-    if (cornerRadius > 0) {
+    if (cornerRadius.x > 0 && cornerRadius.y > 0) {
         // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
         if (bounds == crop || crop.isEmpty()) {
-            return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+            return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
         }
 
         // This makes an effort to speed up common, simple bounds + clip combinations by
         // converting them to a single RRect draw. It is possible there are other cases
         // that can be converted.
         if (crop.contains(bounds)) {
-            const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+            const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
             if (insetCrop.contains(bounds)) {
                 return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
             }
@@ -1367,7 +1367,7 @@
         }
 
         // we didn't hit any of our fast paths so set the clip to the cropRect
-        clip.setRectXY(crop, cornerRadius, cornerRadius);
+        clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
     }
 
     // if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 5ef9944..68c3363 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -90,7 +90,8 @@
     inline SkRect getSkRect(const FloatRect& layer);
     inline SkRect getSkRect(const Rect& layer);
     inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
-                                                        const FloatRect& crop, float cornerRadius);
+                                                        const FloatRect& crop,
+                                                        const vec2& cornerRadius);
     inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7c70a74..8889f76 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
                            const ubyte4& backgroundColor) {
         const Rect casterRect(castingLayer.geometry.boundaries);
         Region casterRegion = Region(casterRect);
-        const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+        const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+                                          castingLayer.geometry.roundedCornersRadius.y) /
+                2.0;
         if (casterCornerRadius > 0.0f) {
             // ignore the corners if a corner radius is set
             Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
-    layer.geometry.roundedCornersRadius = 5.0f;
+    layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
@@ -2131,7 +2133,7 @@
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
     renderengine::LayerSettings castingLayer;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
-    castingLayer.geometry.roundedCornersRadius = 3.0f;
+    castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
     castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
     renderengine::ShadowSettings settings =
@@ -2219,7 +2221,8 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    redLayer.geometry.roundedCornersRadius = 5.0f;
+    redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
     redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2231,7 +2234,7 @@
     renderengine::LayerSettings greenLayer;
     greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    greenLayer.geometry.roundedCornersRadius = 5.0f;
+    greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     // Bottom right corner is not going to be rounded.
     greenLayer.geometry.roundedCornersCrop =
             Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2268,7 +2271,7 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    redLayer.geometry.roundedCornersRadius = 5.0f;
+    redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2313,7 +2316,7 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
-    redLayer.geometry.roundedCornersRadius = 64;
+    redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
     redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2334,6 +2337,49 @@
     expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+    if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+        GTEST_SKIP();
+    }
+
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+    redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(redLayer);
+
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+    // Y-axis draws a larger radius, check that its untouched as well
+    expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+    expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+    //  middle should be red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index a6cacad..93c95b9 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -66,7 +66,11 @@
         v.setCapacity(n);
         while (n) {
             n--;
-            reply.read(s);
+            if(reply.read(s) != OK) {
+                ALOGE("Failed to read reply from getSensorList");
+                v.clear();
+                break;
+            }
             v.add(s);
         }
         return v;
@@ -84,7 +88,11 @@
         v.setCapacity(n);
         while (n) {
             n--;
-            reply.read(s);
+            if(reply.read(s) != OK) {
+                ALOGE("Failed to read reply from getDynamicSensorList");
+                v.clear();
+                break;
+            }
             v.add(s);
         }
         return v;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index ec0ced8..b865c4d 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -632,7 +632,13 @@
         return false;
     }
     outputString8.setTo(static_cast<char const*>(buffer), len);
+
+    if (size < FlattenableUtils::align<4>(len)) {
+        ALOGE("Malformed Sensor String8 field. Should be in a 4-byte aligned buffer but is not.");
+        return false;
+    }
     FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+
     return true;
 }
 
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 0ba9704..40061cd 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -92,6 +92,16 @@
     return *sensorManager;
 }
 
+void SensorManager::removeInstanceForPackage(const String16& packageName) {
+    Mutex::Autolock _l(sLock);
+    auto iterator = sPackageInstances.find(packageName);
+    if (iterator != sPackageInstances.end()) {
+        SensorManager* sensorManager = iterator->second;
+        delete sensorManager;
+        sPackageInstances.erase(iterator);
+    }
+}
+
 SensorManager::SensorManager(const String16& opPackageName)
     : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
     Mutex::Autolock _l(mLock);
@@ -166,6 +176,11 @@
 
         mSensors = mSensorServer->getSensorList(mOpPackageName);
         size_t count = mSensors.size();
+        if (count == 0) {
+            ALOGE("Failed to get Sensor list");
+            mSensorServer.clear();
+            return UNKNOWN_ERROR;
+        }
         mSensorList =
                 static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
         LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 8d0a8a4..7c9d604 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -54,6 +54,7 @@
 {
 public:
     static SensorManager& getInstanceForPackage(const String16& packageName);
+    static void removeInstanceForPackage(const String16& packageName);
     ~SensorManager();
 
     ssize_t getSensorList(Sensor const* const** list);
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 6b936de..8477479 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -28,7 +28,7 @@
     local_include_dirs: ["include"],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.common@1.2",
         "libnativewindow",
diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp
index cf671bc..718d37b 100644
--- a/libs/shaders/tests/Android.bp
+++ b/libs/shaders/tests/Android.bp
@@ -31,7 +31,7 @@
         "libtonemap_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.common@1.2",
         "libnativewindow",
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 37c9824..eca051d 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -28,7 +28,7 @@
     local_include_dirs: ["include"],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer3-V1-ndk",
         "liblog",
         "libnativewindow",
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index 58851b4..0002d3a 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -31,7 +31,7 @@
         "libtonemap_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer3-V1-ndk",
         "libnativewindow",
     ],
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index f6ab7b2..53372c9 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -22,6 +22,8 @@
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android/binder_enums.h>
 #include <android/binder_manager.h>
+#include <cutils/android_filesystem_config.h>
+#include <cutils/multiuser.h>
 #include <gralloctypes/Gralloc4.h>
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
@@ -1195,8 +1197,15 @@
     mAllocator = IAllocator::getService();
     if (__builtin_available(android 31, *)) {
         if (hasIAllocatorAidl()) {
-            mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
-                    AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+            // TODO(b/269517338): Perform the isolated checking for this in service manager instead.
+            uid_t aid = multiuser_get_app_id(getuid());
+            if (aid >= AID_ISOLATED_START && aid <= AID_ISOLATED_END) {
+                mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+                        AServiceManager_getService(kAidlAllocatorServiceName.c_str())));
+            } else {
+                mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+                        AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+            }
             ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
         }
     }
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index a98e697..f582423 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -109,6 +109,11 @@
     return NO_ERROR;
 }
 
+status_t GraphicBufferMapper::importBufferNoValidate(const native_handle_t* rawHandle,
+                                                     buffer_handle_t* outHandle) {
+    return mMapper->importBuffer(rawHandle, outHandle);
+}
+
 void GraphicBufferMapper::getTransportSize(buffer_handle_t handle,
             uint32_t* outTransportNumFds, uint32_t* outTransportNumInts)
 {
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 507fa35..37a2e1c 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -59,6 +59,8 @@
             PixelFormat format, uint64_t usage, uint32_t stride,
             buffer_handle_t* outHandle);
 
+    status_t importBufferNoValidate(const native_handle_t* rawHandle, buffer_handle_t* outHandle);
+
     status_t freeBuffer(buffer_handle_t handle);
 
     void getTransportSize(buffer_handle_t handle,
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index f422ce4..cf5c2e8 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -53,16 +53,19 @@
 
     // real pixel formats supported for rendering -----------------------------
 
-    PIXEL_FORMAT_RGBA_8888    = HAL_PIXEL_FORMAT_RGBA_8888,    // 4x8-bit RGBA
-    PIXEL_FORMAT_RGBX_8888    = HAL_PIXEL_FORMAT_RGBX_8888,    // 4x8-bit RGB0
-    PIXEL_FORMAT_RGB_888      = HAL_PIXEL_FORMAT_RGB_888,      // 3x8-bit RGB
-    PIXEL_FORMAT_RGB_565      = HAL_PIXEL_FORMAT_RGB_565,      // 16-bit RGB
-    PIXEL_FORMAT_BGRA_8888    = HAL_PIXEL_FORMAT_BGRA_8888,    // 4x8-bit BGRA
-    PIXEL_FORMAT_RGBA_5551    = 6,                             // 16-bit ARGB
-    PIXEL_FORMAT_RGBA_4444    = 7,                             // 16-bit ARGB
-    PIXEL_FORMAT_RGBA_FP16    = HAL_PIXEL_FORMAT_RGBA_FP16,    // 64-bit RGBA
-    PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
-    PIXEL_FORMAT_R_8          = 0x38,
+    PIXEL_FORMAT_RGBA_8888     = HAL_PIXEL_FORMAT_RGBA_8888,    // 4x8-bit RGBA
+    PIXEL_FORMAT_RGBX_8888     = HAL_PIXEL_FORMAT_RGBX_8888,    // 4x8-bit RGB0
+    PIXEL_FORMAT_RGB_888       = HAL_PIXEL_FORMAT_RGB_888,      // 3x8-bit RGB
+    PIXEL_FORMAT_RGB_565       = HAL_PIXEL_FORMAT_RGB_565,      // 16-bit RGB
+    PIXEL_FORMAT_BGRA_8888     = HAL_PIXEL_FORMAT_BGRA_8888,    // 4x8-bit BGRA
+    PIXEL_FORMAT_RGBA_5551     = 6,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_4444     = 7,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_FP16     = HAL_PIXEL_FORMAT_RGBA_FP16,    // 64-bit RGBA
+    PIXEL_FORMAT_RGBA_1010102  = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+    PIXEL_FORMAT_R_8           = 0x38,
+    PIXEL_FORMAT_R_16_UINT     = 0x39,
+    PIXEL_FORMAT_RG_1616_UINT  = 0x3a,
+    PIXEL_FORMAT_RGBA_10101010 = 0x3b,
 };
 
 typedef int32_t PixelFormat;
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
index 0a4873c..3fb33b4 100644
--- a/libs/ui/tests/colorspace_test.cpp
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -111,6 +111,7 @@
     EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
     EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
 
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
         ASSERT_TRUE(v >= sRGB.getEOTF()(v));
         ASSERT_TRUE(v <= sRGB.getOETF()(v));
@@ -118,6 +119,7 @@
 
     float previousEOTF = std::numeric_limits<float>::lowest();
     float previousOETF = std::numeric_limits<float>::lowest();
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
         ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
         previousEOTF = sRGB.getEOTF()(v);
@@ -131,6 +133,7 @@
           {0.3127f, 0.3290f}
           // linear transfer functions
     );
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
         ASSERT_EQ(v, sRGB2.getEOTF()(v));
         ASSERT_EQ(v, sRGB2.getOETF()(v));
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
deleted file mode 100644
index 0bda798..0000000
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "buffer_hub_queue_client.cpp",
-    "buffer_hub_queue_parcelable.cpp",
-]
-
-includeFiles = [
-    "include",
-]
-
-staticLibraries = [
-    "libbufferhub",
-]
-
-sharedLibraries = [
-    "libbinder",
-    "libcutils",
-    "liblog",
-    "libui",
-    "libutils",
-    "libpdx_default_transport",
-]
-
-headerLibraries = [
-    "libdvr_headers",
-    "libnativebase_headers",
-]
-
-cc_library_shared {
-    name: "libbufferhubqueue",
-    cflags: [
-        "-DLOG_TAG=\"libbufferhubqueue\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-Wall",
-        "-Werror",
-        "-Wno-format",
-        "-Wno-unused-parameter",
-        "-Wno-unused-variable",
-    ],
-    srcs: sourceFiles,
-    export_include_dirs: includeFiles,
-    export_static_lib_headers: staticLibraries,
-    static_libs: staticLibraries,
-    shared_libs: sharedLibraries,
-    header_libs: headerLibraries,
-}
-
-subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
deleted file mode 100644
index 2d3fa4a..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ /dev/null
@@ -1,823 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_client.h"
-
-#include <inttypes.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-
-#include <array>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <pdx/trace.h>
-
-#define RETRY_EINTR(fnc_call)                 \
-  ([&]() -> decltype(fnc_call) {              \
-    decltype(fnc_call) result;                \
-    do {                                      \
-      result = (fnc_call);                    \
-    } while (result == -1 && errno == EINTR); \
-    return result;                            \
-  })()
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-std::pair<int32_t, int32_t> Unstuff(uint64_t value) {
-  return {static_cast<int32_t>(value >> 32),
-          static_cast<int32_t>(value & ((1ull << 32) - 1))};
-}
-
-uint64_t Stuff(int32_t a, int32_t b) {
-  const uint32_t ua = static_cast<uint32_t>(a);
-  const uint32_t ub = static_cast<uint32_t>(b);
-  return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub);
-}
-
-}  // anonymous namespace
-
-BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
-    : Client{pdx::default_transport::ClientChannel::Create(
-          std::move(channel_handle))} {
-  Initialize();
-}
-
-BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
-    : Client{
-          pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} {
-  Initialize();
-}
-
-void BufferHubQueue::Initialize() {
-  int ret = epoll_fd_.Create();
-  if (ret < 0) {
-    ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
-          strerror(-ret));
-    return;
-  }
-
-  epoll_event event = {
-      .events = EPOLLIN | EPOLLET,
-      .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
-  ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
-  if (ret < 0) {
-    ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
-          strerror(-ret));
-  }
-}
-
-Status<void> BufferHubQueue::ImportQueue() {
-  auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
-  if (!status) {
-    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  } else {
-    SetupQueue(status.get());
-    return {};
-  }
-}
-
-void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) {
-  is_async_ = queue_info.producer_config.is_async;
-  default_width_ = queue_info.producer_config.default_width;
-  default_height_ = queue_info.producer_config.default_height;
-  default_format_ = queue_info.producer_config.default_format;
-  user_metadata_size_ = queue_info.producer_config.user_metadata_size;
-  id_ = queue_info.id;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle(/*silent*/ false))
-    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  else
-    return nullptr;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle(/*silent*/ true))
-    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  else
-    return nullptr;
-}
-
-Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle(
-    bool silent) {
-  auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent);
-  if (!status) {
-    ALOGE(
-        "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
-        "%s",
-        status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  }
-
-  return status;
-}
-
-pdx::Status<ConsumerQueueParcelable>
-BufferHubQueue::CreateConsumerQueueParcelable(bool silent) {
-  auto status = CreateConsumerQueueHandle(silent);
-  if (!status)
-    return status.error_status();
-
-  // A temporary consumer queue client to pull its channel parcelable.
-  auto consumer_queue =
-      std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  ConsumerQueueParcelable queue_parcelable(
-      consumer_queue->GetChannel()->TakeChannelParcelable());
-
-  if (!queue_parcelable.IsValid()) {
-    ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  return {std::move(queue_parcelable)};
-}
-
-bool BufferHubQueue::WaitForBuffers(int timeout) {
-  ATRACE_NAME("BufferHubQueue::WaitForBuffers");
-  std::array<epoll_event, kMaxEvents> events;
-
-  // Loop at least once to check for hangups.
-  do {
-    ALOGD_IF(
-        TRACE,
-        "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
-        id(), count(), capacity());
-
-    // If there is already a buffer then just check for hangup without waiting.
-    const int ret = epoll_fd_.Wait(events.data(), events.size(),
-                                   count() == 0 ? timeout : 0);
-
-    if (ret == 0) {
-      ALOGI_IF(TRACE,
-               "BufferHubQueue::WaitForBuffers: No events before timeout: "
-               "queue_id=%d",
-               id());
-      return count() != 0;
-    }
-
-    if (ret < 0 && ret != -EINTR) {
-      ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
-      return false;
-    }
-
-    const int num_events = ret;
-
-    // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
-    // one for each buffer in the queue, and one extra event for the queue
-    // client itself.
-    for (int i = 0; i < num_events; i++) {
-      int32_t event_fd;
-      int32_t index;
-      std::tie(event_fd, index) = Unstuff(events[i].data.u64);
-
-      PDX_TRACE_FORMAT(
-          "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;"
-          "slot=%d|",
-          id(), num_events, i, event_fd, index);
-
-      ALOGD_IF(TRACE,
-               "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d",
-               i, event_fd, index);
-
-      if (is_buffer_event_index(index)) {
-        HandleBufferEvent(static_cast<size_t>(index), event_fd,
-                          events[i].events);
-      } else if (is_queue_event_index(index)) {
-        HandleQueueEvent(events[i].events);
-      } else {
-        ALOGW(
-            "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d "
-            "index=%d",
-            event_fd, index);
-      }
-    }
-  } while (count() == 0 && capacity() > 0 && !hung_up());
-
-  return count() != 0;
-}
-
-Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd,
-                                               int poll_events) {
-  ATRACE_NAME("BufferHubQueue::HandleBufferEvent");
-  if (!buffers_[slot]) {
-    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
-    return ErrorStatus(ENOENT);
-  }
-
-  auto status = buffers_[slot]->GetEventMask(poll_events);
-  if (!status) {
-    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  const int events = status.get();
-  PDX_TRACE_FORMAT(
-      "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;"
-      "events=%d|",
-      id(), buffers_[slot]->id(), slot, event_fd, poll_events, events);
-
-  if (events & EPOLLIN) {
-    return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()});
-  } else if (events & EPOLLHUP) {
-    ALOGW(
-        "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu "
-        "event_fd=%d buffer_id=%d",
-        slot, buffers_[slot]->event_fd(), buffers_[slot]->id());
-    return RemoveBuffer(slot);
-  } else {
-    ALOGW(
-        "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
-        "events=%d",
-        slot, events);
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) {
-  ATRACE_NAME("BufferHubQueue::HandleQueueEvent");
-  auto status = GetEventMask(poll_event);
-  if (!status) {
-    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  const int events = status.get();
-  if (events & EPOLLIN) {
-    // Note that after buffer imports, if |count()| still returns 0, epoll
-    // wait will be tried again to acquire the newly imported buffer.
-    auto buffer_status = OnBufferAllocated();
-    if (!buffer_status) {
-      ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
-            buffer_status.GetErrorMessage().c_str());
-    }
-  } else if (events & EPOLLHUP) {
-    ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
-    hung_up_ = true;
-  } else {
-    ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::AddBuffer(
-    const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
-           slot);
-
-  if (is_full()) {
-    ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
-    return ErrorStatus(E2BIG);
-  }
-
-  if (buffers_[slot]) {
-    // Replace the buffer if the slot is occupied. This could happen when the
-    // producer side replaced the slot with a newly allocated buffer. Remove the
-    // buffer before setting up with the new one.
-    auto remove_status = RemoveBuffer(slot);
-    if (!remove_status)
-      return remove_status.error_status();
-  }
-
-  for (const auto& event_source : buffer->GetEventSources()) {
-    epoll_event event = {.events = event_source.event_mask | EPOLLET,
-                         .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
-    const int ret =
-        epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
-    if (ret < 0) {
-      ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
-            strerror(-ret));
-      return ErrorStatus(-ret);
-    }
-  }
-
-  buffers_[slot] = buffer;
-  capacity_++;
-  return {};
-}
-
-Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
-  ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
-
-  if (buffers_[slot]) {
-    for (const auto& event_source : buffers_[slot]->GetEventSources()) {
-      const int ret =
-          epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
-      if (ret < 0) {
-        ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
-              strerror(-ret));
-        return ErrorStatus(-ret);
-      }
-    }
-
-    // Trigger OnBufferRemoved callback if registered.
-    if (on_buffer_removed_)
-      on_buffer_removed_(buffers_[slot]);
-
-    buffers_[slot] = nullptr;
-    capacity_--;
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::Enqueue(Entry entry) {
-  if (!is_full()) {
-    // Find and remove the enqueued buffer from unavailable_buffers_slot if
-    // exist.
-    auto enqueued_buffer_iter = std::find_if(
-        unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
-        [&entry](size_t slot) -> bool { return slot == entry.slot; });
-    if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
-      unavailable_buffers_slot_.erase(enqueued_buffer_iter);
-    }
-
-    available_buffers_.push(std::move(entry));
-
-    // Trigger OnBufferAvailable callback if registered.
-    if (on_buffer_available_)
-      on_buffer_available_();
-
-    return {};
-  } else {
-    ALOGE("%s: Buffer queue is full!", __FUNCTION__);
-    return ErrorStatus(E2BIG);
-  }
-}
-
-Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
-                                                               size_t* slot) {
-  ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
-
-  PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
-
-  if (count() == 0) {
-    if (!WaitForBuffers(timeout))
-      return ErrorStatus(ETIMEDOUT);
-  }
-
-  auto& entry = available_buffers_.top();
-  PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
-                   entry.slot);
-
-  std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
-  *slot = entry.slot;
-
-  available_buffers_.pop();
-  unavailable_buffers_slot_.push_back(*slot);
-
-  return {std::move(buffer)};
-}
-
-void BufferHubQueue::SetBufferAvailableCallback(
-    BufferAvailableCallback callback) {
-  on_buffer_available_ = callback;
-}
-
-void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) {
-  on_buffer_removed_ = callback;
-}
-
-pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
-  // Clear all available buffers.
-  while (!available_buffers_.empty())
-    available_buffers_.pop();
-
-  pdx::Status<void> last_error;  // No error.
-  // Clear all buffers this producer queue is tracking.
-  for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
-    if (buffers_[slot] != nullptr) {
-      auto status = RemoveBuffer(slot);
-      if (!status) {
-        ALOGE(
-            "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
-            "slot=%zu.",
-            slot);
-        last_error = status.error_status();
-      }
-    }
-  }
-
-  return last_error;
-}
-
-ProducerQueue::ProducerQueue(LocalChannelHandle handle)
-    : BASE(std::move(handle)) {
-  auto status = ImportQueue();
-  if (!status) {
-    ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-  }
-}
-
-ProducerQueue::ProducerQueue(const ProducerQueueConfig& config,
-                             const UsagePolicy& usage)
-    : BASE(BufferHubRPC::kClientPath) {
-  auto status =
-      InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage);
-  if (!status) {
-    ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-    return;
-  }
-
-  SetupQueue(status.get());
-}
-
-Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
-    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-    uint64_t usage, size_t buffer_count) {
-  if (buffer_count == 0) {
-    return {std::vector<size_t>()};
-  }
-
-  if (capacity() + buffer_count > kMaxQueueCapacity) {
-    ALOGE(
-        "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
-        "allocate %zu more buffer(s).",
-        capacity(), buffer_count);
-    return ErrorStatus(E2BIG);
-  }
-
-  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
-          width, height, layer_count, format, usage, buffer_count);
-  if (!status) {
-    ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto buffer_handle_slots = status.take();
-  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count,
-                      "BufferHubRPC::ProducerQueueAllocateBuffers should "
-                      "return %zu buffer handle(s), but returned %zu instead.",
-                      buffer_count, buffer_handle_slots.size());
-
-  std::vector<size_t> buffer_slots;
-  buffer_slots.reserve(buffer_count);
-
-  // Bookkeeping for each buffer.
-  for (auto& hs : buffer_handle_slots) {
-    auto& buffer_handle = hs.first;
-    size_t buffer_slot = hs.second;
-
-    // Note that import might (though very unlikely) fail. If so, buffer_handle
-    // will be closed and included in returned buffer_slots.
-    if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
-                  buffer_slot)) {
-      ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
-               buffer_slot);
-      buffer_slots.push_back(buffer_slot);
-    }
-  }
-
-  if (buffer_slots.size() != buffer_count) {
-    // Error out if the count of imported buffer(s) is not correct.
-    ALOGE(
-        "ProducerQueue::AllocateBuffers: requested to import %zu "
-        "buffers, but actually imported %zu buffers.",
-        buffer_count, buffer_slots.size());
-    return ErrorStatus(ENOMEM);
-  }
-
-  return {std::move(buffer_slots)};
-}
-
-Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
-                                             uint32_t layer_count,
-                                             uint32_t format, uint64_t usage) {
-  // We only allocate one buffer at a time.
-  constexpr size_t buffer_count = 1;
-  auto status =
-      AllocateBuffers(width, height, layer_count, format, usage, buffer_count);
-  if (!status) {
-    ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return {status.get()[0]};
-}
-
-Status<void> ProducerQueue::AddBuffer(
-    const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
-           id(), buffer->id(), slot);
-  // For producer buffer, we need to enqueue the newly added buffer
-  // immediately. Producer queue starts with all buffers in available state.
-  auto status = BufferHubQueue::AddBuffer(buffer, slot);
-  if (!status)
-    return status;
-
-  return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
-}
-
-Status<size_t> ProducerQueue::InsertBuffer(
-    const std::shared_ptr<ProducerBuffer>& buffer) {
-  if (buffer == nullptr ||
-      !BufferHubDefs::isClientGained(buffer->buffer_state(),
-                                     buffer->client_state_mask())) {
-    ALOGE(
-        "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
-        "gained state.");
-    return ErrorStatus(EINVAL);
-  }
-
-  auto status_or_slot =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
-          buffer->cid());
-  if (!status_or_slot) {
-    ALOGE(
-        "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
-        "buffer_cid=%d, error: %s.",
-        buffer->cid(), status_or_slot.GetErrorMessage().c_str());
-    return status_or_slot.error_status();
-  }
-
-  size_t slot = status_or_slot.get();
-
-  // Note that we are calling AddBuffer() from the base class to explicitly
-  // avoid Enqueue() the ProducerBuffer.
-  auto status = BufferHubQueue::AddBuffer(buffer, slot);
-  if (!status) {
-    ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-  return {slot};
-}
-
-Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
-  auto status =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
-  if (!status) {
-    ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return BufferHubQueue::RemoveBuffer(slot);
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
-    int timeout, size_t* slot, LocalHandle* release_fence) {
-  DvrNativeBufferMetadata canonical_meta;
-  return Dequeue(timeout, slot, &canonical_meta, release_fence);
-}
-
-pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
-    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-    pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
-  ATRACE_NAME("ProducerQueue::Dequeue");
-  if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
-    ALOGE("%s: Invalid parameter.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  std::shared_ptr<ProducerBuffer> buffer;
-  Status<std::shared_ptr<BufferHubBase>> dequeue_status =
-      BufferHubQueue::Dequeue(timeout, slot);
-  if (dequeue_status.ok()) {
-    buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
-  } else {
-    if (gain_posted_buffer) {
-      Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
-          ProducerQueue::DequeueUnacquiredBuffer(slot);
-      if (!dequeue_unacquired_status.ok()) {
-        ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
-              dequeue_unacquired_status.error());
-        return dequeue_unacquired_status.error_status();
-      }
-      buffer = dequeue_unacquired_status.take();
-    } else {
-      return dequeue_status.error_status();
-    }
-  }
-  const int ret =
-      buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
-  if (ret < 0 && ret != -EALREADY)
-    return ErrorStatus(-ret);
-
-  return {std::move(buffer)};
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
-    size_t* slot) {
-  if (unavailable_buffers_slot_.size() < 1) {
-    ALOGE(
-        "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
-        "acquired state if exist.",
-        __FUNCTION__);
-    return ErrorStatus(ENOMEM);
-  }
-
-  // Find the first buffer that is not in acquired state from
-  // unavailable_buffers_slot_.
-  for (auto iter = unavailable_buffers_slot_.begin();
-       iter != unavailable_buffers_slot_.end(); iter++) {
-    std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
-    if (buffer == nullptr) {
-      ALOGE("%s failed. Buffer slot %d is  null.", __FUNCTION__,
-            static_cast<int>(*slot));
-      return ErrorStatus(EIO);
-    }
-    if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
-      *slot = *iter;
-      unavailable_buffers_slot_.erase(iter);
-      unavailable_buffers_slot_.push_back(*slot);
-      ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
-            __FUNCTION__, static_cast<int>(*slot));
-      return {std::move(buffer)};
-    }
-  }
-  ALOGE(
-      "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
-      __FUNCTION__);
-  return ErrorStatus(EBUSY);
-}
-
-pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
-  if (capacity() != 0) {
-    ALOGE(
-        "%s: producer queue can only be taken out as a parcelable when empty. "
-        "Current queue capacity: %zu",
-        __FUNCTION__, capacity());
-    return ErrorStatus(EINVAL);
-  }
-
-  std::unique_ptr<pdx::ClientChannel> channel = TakeChannel();
-  ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable());
-
-  // Here the queue parcelable is returned and holds the underlying system
-  // resources backing the queue; while the original client channel of this
-  // producer queue is destroyed in place so that this client can no longer
-  // provide producer operations.
-  return {std::move(queue_parcelable)};
-}
-
-/*static */
-std::unique_ptr<ConsumerQueue> ConsumerQueue::Import(
-    LocalChannelHandle handle) {
-  return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle)));
-}
-
-ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
-    : BufferHubQueue(std::move(handle)) {
-  auto status = ImportQueue();
-  if (!status) {
-    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-  }
-
-  auto import_status = ImportBuffers();
-  if (import_status) {
-    ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
-  } else {
-    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
-          import_status.GetErrorMessage().c_str());
-  }
-}
-
-Status<size_t> ConsumerQueue::ImportBuffers() {
-  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
-  if (!status) {
-    if (status.error() == EBADR) {
-      ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
-      return {0};
-    } else {
-      ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
-            status.GetErrorMessage().c_str());
-      return status.error_status();
-    }
-  }
-
-  int ret;
-  Status<void> last_error;
-  size_t imported_buffers_count = 0;
-
-  auto buffer_handle_slots = status.take();
-  for (auto& buffer_handle_slot : buffer_handle_slots) {
-    ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
-             buffer_handle_slot.first.value());
-
-    std::unique_ptr<ConsumerBuffer> consumer_buffer =
-        ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
-    if (!consumer_buffer) {
-      ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
-            buffer_handle_slot.second);
-      last_error = ErrorStatus(EPIPE);
-      continue;
-    }
-
-    auto add_status =
-        AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
-    if (!add_status) {
-      ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
-            add_status.GetErrorMessage().c_str());
-      last_error = add_status;
-    } else {
-      imported_buffers_count++;
-    }
-  }
-
-  if (imported_buffers_count > 0)
-    return {imported_buffers_count};
-  else
-    return last_error.error_status();
-}
-
-Status<void> ConsumerQueue::AddBuffer(
-    const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
-           buffer->id(), slot);
-  return BufferHubQueue::AddBuffer(buffer, slot);
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
-    int timeout, size_t* slot, void* meta, size_t user_metadata_size,
-    LocalHandle* acquire_fence) {
-  if (user_metadata_size != user_metadata_size_) {
-    ALOGE(
-        "%s: Metadata size (%zu) for the dequeuing buffer does not match "
-        "metadata size (%zu) for the queue.",
-        __FUNCTION__, user_metadata_size, user_metadata_size_);
-    return ErrorStatus(EINVAL);
-  }
-
-  DvrNativeBufferMetadata canonical_meta;
-  auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence);
-  if (!status)
-    return status.error_status();
-
-  if (meta && user_metadata_size) {
-    void* metadata_src =
-        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
-    if (metadata_src) {
-      memcpy(meta, metadata_src, user_metadata_size);
-    } else {
-      ALOGW("%s: no user-defined metadata.", __FUNCTION__);
-    }
-  }
-
-  return status;
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
-    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-    pdx::LocalHandle* acquire_fence) {
-  ATRACE_NAME("ConsumerQueue::Dequeue");
-  if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
-    ALOGE("%s: Invalid parameter.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  auto status = BufferHubQueue::Dequeue(timeout, slot);
-  if (!status)
-    return status.error_status();
-
-  auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
-  const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
-  if (ret < 0)
-    return ErrorStatus(-ret);
-
-  return {std::move(buffer)};
-}
-
-Status<void> ConsumerQueue::OnBufferAllocated() {
-  ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
-
-  auto status = ImportBuffers();
-  if (!status) {
-    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  } else if (status.get() == 0) {
-    ALOGW("%s: No new buffers allocated!", __FUNCTION__);
-    return ErrorStatus(ENOBUFS);
-  } else {
-    ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
-             status.get());
-    return {};
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
deleted file mode 100644
index f705749..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_parcelable.h"
-
-#include <binder/Parcel.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-namespace android {
-namespace dvr {
-
-template <BufferHubQueueParcelableMagic Magic>
-bool BufferHubQueueParcelable<Magic>::IsValid() const {
-  return !!channel_parcelable_ && channel_parcelable_->IsValid();
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() {
-  if (!IsValid()) {
-    ALOGE(
-        "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel.");
-    return {};  // Returns an empty channel handle.
-  }
-
-  // Take channel handle out of the parcelable and reset the parcelable.
-  pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle();
-  // Now channel_parcelable_ should already be invalid, but reset it to release
-  // the invalid parcelable object from unique_ptr.
-  channel_parcelable_ = nullptr;
-  return handle;
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const {
-  if (!IsValid()) {
-    ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel.");
-    return -EINVAL;
-  }
-
-  status_t res = parcel->writeUint32(Magic);
-  if (res != OK) {
-    ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
-    return res;
-  }
-
-  return channel_parcelable_->writeToParcel(parcel);
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) {
-  if (IsValid()) {
-    ALOGE(
-        "BufferHubQueueParcelable::readFromParcel: This parcelable object has "
-        "been initialized already.");
-    return -EINVAL;
-  }
-
-  uint32_t out_magic = 0;
-  status_t res = OK;
-
-  res = parcel->readUint32(&out_magic);
-  if (res != OK)
-    return res;
-
-  if (out_magic != Magic) {
-    ALOGE(
-        "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, "
-        "epxected: 0x%x",
-        out_magic, Magic);
-    return -EINVAL;
-  }
-
-  // (Re)Alocate channel parcelable object.
-  channel_parcelable_ =
-      std::make_unique<pdx::default_transport::ChannelParcelable>();
-  return channel_parcelable_->readFromParcel(parcel);
-}
-
-template class BufferHubQueueParcelable<
-    BufferHubQueueParcelableMagic::Producer>;
-template class BufferHubQueueParcelable<
-    BufferHubQueueParcelableMagic::Consumer>;
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
deleted file mode 100644
index 74b4b3d..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ /dev/null
@@ -1,476 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-
-#include <ui/BufferQueueDefs.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <private/dvr/producer_buffer.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <queue>
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-class ConsumerQueue;
-
-// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
-// automatically re-requeued when released by the remote side.
-class BufferHubQueue : public pdx::Client {
- public:
-  using BufferAvailableCallback = std::function<void()>;
-  using BufferRemovedCallback =
-      std::function<void(const std::shared_ptr<BufferHubBase>&)>;
-
-  virtual ~BufferHubQueue() {}
-
-  // Creates a new consumer queue that is attached to the producer. Returns
-  // a new consumer queue client or nullptr on failure.
-  std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
-
-  // Creates a new consumer queue that is attached to the producer. This queue
-  // sets each of its imported consumer buffers to the ignored state to avoid
-  // participation in lifecycle events.
-  std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
-
-  // Returns whether the buffer queue is in async mode.
-  bool is_async() const { return is_async_; }
-
-  // Returns the default buffer width of this buffer queue.
-  uint32_t default_width() const { return default_width_; }
-
-  // Returns the default buffer height of this buffer queue.
-  uint32_t default_height() const { return default_height_; }
-
-  // Returns the default buffer format of this buffer queue.
-  uint32_t default_format() const { return default_format_; }
-
-  // Creates a new consumer in handle form for immediate transport over RPC.
-  pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
-      bool silent = false);
-
-  // Creates a new consumer in parcelable form for immediate transport over
-  // Binder.
-  pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable(
-      bool silent = false);
-
-  // Returns the number of buffers avaiable for dequeue.
-  size_t count() const { return available_buffers_.size(); }
-
-  // Returns the total number of buffers that the queue is tracking.
-  size_t capacity() const { return capacity_; }
-
-  // Returns the size of metadata structure associated with this queue.
-  size_t metadata_size() const { return user_metadata_size_; }
-
-  // Returns whether the buffer queue is full.
-  bool is_full() const {
-    return available_buffers_.size() >= kMaxQueueCapacity;
-  }
-
-  // Returns whether the buffer queue is connected to bufferhubd.
-  bool is_connected() const { return !!GetChannel(); }
-
-  int GetBufferId(size_t slot) const {
-    return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
-                                                      : -1;
-  }
-
-  std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
-    return buffers_[slot];
-  }
-
-  pdx::Status<int> GetEventMask(int events) {
-    if (auto* client_channel = GetChannel()) {
-      return client_channel->GetEventMask(events);
-    } else {
-      return pdx::ErrorStatus(EINVAL);
-    }
-  }
-
-  // Returns an fd that signals pending queue events using
-  // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
-  // called to handle pending queue events.
-  int queue_fd() const { return epoll_fd_.Get(); }
-
-  // Handles any pending events, returning available buffers to the queue and
-  // reaping disconnected buffers. Returns true if successful, false if an error
-  // occurred.
-  bool HandleQueueEvents() { return WaitForBuffers(0); }
-
-  // Set buffer event callbacks, which are std::function wrappers. The caller is
-  // responsible for ensuring the validity of these callbacks' callable targets.
-  void SetBufferAvailableCallback(BufferAvailableCallback callback);
-  void SetBufferRemovedCallback(BufferRemovedCallback callback);
-
-  // The queue tracks at most this many buffers.
-  static constexpr size_t kMaxQueueCapacity =
-      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
-
-  static constexpr int kNoTimeOut = -1;
-
-  int id() const { return id_; }
-  bool hung_up() const { return hung_up_; }
-
- protected:
-  explicit BufferHubQueue(pdx::LocalChannelHandle channel);
-  explicit BufferHubQueue(const std::string& endpoint_path);
-
-  // Imports the queue parameters by querying BufferHub for the parameters for
-  // this channel.
-  pdx::Status<void> ImportQueue();
-
-  // Sets up the queue with the given parameters.
-  void SetupQueue(const QueueInfo& queue_info);
-
-  // Register a buffer for management by the queue. Used by subclasses to add a
-  // buffer to internal bookkeeping.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
-                              size_t slot);
-
-  // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
-  // to deregister a buffer for epoll and internal bookkeeping.
-  virtual pdx::Status<void> RemoveBuffer(size_t slot);
-
-  // Free all buffers that belongs to this queue. Can only be called from
-  // producer side.
-  virtual pdx::Status<void> FreeAllBuffers();
-
-  // Dequeue a buffer from the free queue, blocking until one is available. The
-  // timeout argument specifies the number of milliseconds that |Dequeue()| will
-  // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
-  // while specifying a timeout equal to zero cause Dequeue() to return
-  // immediately, even if no buffers are available.
-  pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
-                                                      size_t* slot);
-
-  // Waits for buffers to become available and adds them to the available queue.
-  bool WaitForBuffers(int timeout);
-
-  pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd,
-                                      int poll_events);
-  pdx::Status<void> HandleQueueEvent(int poll_events);
-
-  // Entry in the priority queue of available buffers that stores related
-  // per-buffer data.
-  struct Entry {
-    Entry() : slot(0) {}
-    Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
-          uint64_t in_index)
-        : buffer(in_buffer), slot(in_slot), index(in_index) {}
-    Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
-          std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
-          size_t in_slot)
-        : buffer(in_buffer),
-          metadata(std::move(in_metadata)),
-          fence(std::move(in_fence)),
-          slot(in_slot) {}
-    Entry(Entry&&) = default;
-    Entry& operator=(Entry&&) = default;
-
-    std::shared_ptr<BufferHubBase> buffer;
-    std::unique_ptr<uint8_t[]> metadata;
-    pdx::LocalHandle fence;
-    size_t slot;
-    uint64_t index;
-  };
-
-  struct EntryComparator {
-    bool operator()(const Entry& lhs, const Entry& rhs) {
-      return lhs.index > rhs.index;
-    }
-  };
-
-  // Enqueues a buffer to the available list (Gained for producer or Acquireed
-  // for consumer).
-  pdx::Status<void> Enqueue(Entry entry);
-
-  // Called when a buffer is allocated remotely.
-  virtual pdx::Status<void> OnBufferAllocated() { return {}; }
-
-  // Size of the metadata that buffers in this queue cary.
-  size_t user_metadata_size_{0};
-
-  // Buffers and related data that are available for dequeue.
-  std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
-      available_buffers_;
-
-  // Slot of the buffers that are not available for normal dequeue. For example,
-  // the slot of posted or acquired buffers in the perspective of a producer.
-  std::vector<size_t> unavailable_buffers_slot_;
-
- private:
-  void Initialize();
-
-  // Special epoll data field indicating that the epoll event refers to the
-  // queue.
-  static constexpr int64_t kEpollQueueEventIndex = -1;
-
-  static constexpr size_t kMaxEvents = 128;
-
-  // The u64 data field of an epoll event is interpreted as int64_t:
-  // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
-  // element of |buffers_| as a direct index;
-  static bool is_buffer_event_index(int64_t index) {
-    return index >= 0 &&
-           index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
-  }
-
-  // When |index| == kEpollQueueEventIndex it refers to the queue itself.
-  static bool is_queue_event_index(int64_t index) {
-    return index == BufferHubQueue::kEpollQueueEventIndex;
-  }
-
-  // Whether the buffer queue is operating in Async mode.
-  // From GVR's perspective of view, this means a buffer can be acquired
-  // asynchronously by the compositor.
-  // From Android Surface's perspective of view, this is equivalent to
-  // IGraphicBufferProducer's async mode. When in async mode, a producer
-  // will never block even if consumer is running slow.
-  bool is_async_{false};
-
-  // Default buffer width that is set during ProducerQueue's creation.
-  uint32_t default_width_{1};
-
-  // Default buffer height that is set during ProducerQueue's creation.
-  uint32_t default_height_{1};
-
-  // Default buffer format that is set during ProducerQueue's creation.
-  uint32_t default_format_{1};  // PIXEL_FORMAT_RGBA_8888
-
-  // Tracks the buffers belonging to this queue. Buffers are stored according to
-  // "slot" in this vector. Each slot is a logical id of the buffer within this
-  // queue regardless of its queue position or presence in the ring buffer.
-  std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
-
-  // Keeps track with how many buffers have been added into the queue.
-  size_t capacity_{0};
-
-  // Epoll fd used to manage buffer events.
-  EpollFileDescriptor epoll_fd_;
-
-  // Flag indicating that the other side hung up. For ProducerQueues this
-  // triggers when BufferHub dies or explicitly closes the queue channel. For
-  // ConsumerQueues this can either mean the same or that the ProducerQueue on
-  // the other end hung up.
-  bool hung_up_{false};
-
-  // Global id for the queue that is consistent across processes.
-  int id_{-1};
-
-  // Buffer event callbacks
-  BufferAvailableCallback on_buffer_available_;
-  BufferRemovedCallback on_buffer_removed_;
-
-  BufferHubQueue(const BufferHubQueue&) = delete;
-  void operator=(BufferHubQueue&) = delete;
-};
-
-class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
- public:
-  // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
-  // in |usage_clear_mask| will be automatically masked off. Note that
-  // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
-  // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
-  // allocation through this producer queue shall not have any of the usage bits
-  // in |usage_deny_set_mask| set. Allocation calls violating this will be
-  // rejected. All buffer allocation through this producer queue must have all
-  // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
-  // this will be rejected. Note that |usage_deny_set_mask| and
-  // |usage_deny_clear_mask| shall not conflict with each other. Such
-  // configuration will be treated as invalid input on creation.
-  static std::unique_ptr<ProducerQueue> Create(
-      const ProducerQueueConfig& config, const UsagePolicy& usage) {
-    return BASE::Create(config, usage);
-  }
-
-  // Import a ProducerQueue from a channel handle.
-  static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) {
-    return BASE::Create(std::move(handle));
-  }
-
-  // Get a producer buffer. Note that the method doesn't check whether the
-  // buffer slot has a valid buffer that has been allocated already. When no
-  // buffer has been imported before it returns nullptr; otherwise it returns
-  // a shared pointer to a ProducerBuffer.
-  std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<ProducerBuffer>(
-        BufferHubQueue::GetBuffer(slot));
-  }
-
-  // Batch allocate buffers. Once allocated, producer buffers are automatically
-  // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED
-  // state). Upon success, returns a list of slots for each buffer allocated.
-  pdx::Status<std::vector<size_t>> AllocateBuffers(
-      uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-      uint64_t usage, size_t buffer_count);
-
-  // Allocate producer buffer to populate the queue. Once allocated, a producer
-  // buffer is automatically enqueue'd into the ProducerQueue and available to
-  // use (i.e. in GAINED state). Upon success, returns the slot number for the
-  // buffer allocated.
-  pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height,
-                                     uint32_t layer_count, uint32_t format,
-                                     uint64_t usage);
-
-  // Add a producer buffer to populate the queue. Once added, a producer buffer
-  // is available to use (i.e. in GAINED state).
-  pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
-                              size_t slot);
-
-  // Inserts a ProducerBuffer into the queue. On success, the method returns the
-  // |slot| number where the new buffer gets inserted. Note that the buffer
-  // being inserted should be in Gain'ed state prior to the call and it's
-  // considered as already Dequeued when the function returns.
-  pdx::Status<size_t> InsertBuffer(
-      const std::shared_ptr<ProducerBuffer>& buffer);
-
-  // Remove producer buffer from the queue.
-  pdx::Status<void> RemoveBuffer(size_t slot) override;
-
-  // Free all buffers on this producer queue.
-  pdx::Status<void> FreeAllBuffers() override {
-    return BufferHubQueue::FreeAllBuffers();
-  }
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  // @return a buffer in gained state, which was originally in released state.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
-      int timeout, size_t* slot, pdx::LocalHandle* release_fence);
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  //
-  // @param timeout to dequeue a buffer.
-  // @param slot is the slot of the output ProducerBuffer.
-  // @param release_fence for gaining a buffer.
-  // @param out_meta metadata of the output buffer.
-  // @param gain_posted_buffer whether to gain posted buffer if no released
-  //     buffer is available to gain.
-  // @return a buffer in gained state, which was originally in released state if
-  //     gain_posted_buffer is false, or in posted/released state if
-  //     gain_posted_buffer is true.
-  // TODO(b/112007999): gain_posted_buffer true is only used to prevent
-  // libdvrtracking from starving when there are non-responding clients. This
-  // gain_posted_buffer param can be removed once libdvrtracking start to use
-  // the new AHardwareBuffer API.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
-      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-      pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
-
-  // Enqueues a producer buffer in the queue.
-  pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
-                            size_t slot, uint64_t index) {
-    return BufferHubQueue::Enqueue({buffer, slot, index});
-  }
-
-  // Takes out the current producer queue as a binder parcelable object. Note
-  // that the queue must be empty to be exportable. After successful export, the
-  // producer queue client should no longer be used.
-  pdx::Status<ProducerQueueParcelable> TakeAsParcelable();
-
- private:
-  friend BASE;
-
-  // Constructors are automatically exposed through ProducerQueue::Create(...)
-  // static template methods inherited from ClientBase, which take the same
-  // arguments as the constructors.
-  explicit ProducerQueue(pdx::LocalChannelHandle handle);
-  ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  //
-  // @param slot the slot of the returned buffer.
-  // @return a buffer in gained state, which was originally in posted state or
-  //     released state.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
-      size_t* slot);
-};
-
-class ConsumerQueue : public BufferHubQueue {
- public:
-  // Get a consumer buffer. Note that the method doesn't check whether the
-  // buffer slot has a valid buffer that has been imported already. When no
-  // buffer has been imported before it returns nullptr; otherwise returns a
-  // shared pointer to a ConsumerBuffer.
-  std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<ConsumerBuffer>(
-        BufferHubQueue::GetBuffer(slot));
-  }
-
-  // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
-  // whether or not buffers are set to be ignored when imported. This may be
-  // used to avoid participation in the buffer lifecycle by a consumer queue
-  // that is only used to spawn other consumer queues, such as in an
-  // intermediate service.
-  static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle);
-
-  // Import newly created buffers from the service side.
-  // Returns number of buffers successfully imported or an error.
-  pdx::Status<size_t> ImportBuffers();
-
-  // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
-  // mode, and caller should call Releasse() once it's done writing to release
-  // the buffer to the producer side. |meta| is passed along from BufferHub,
-  // The user of ProducerBuffer is responsible with making sure that the
-  // Dequeue() is done with the corect metadata type and size with those used
-  // when the buffer is orignally created.
-  template <typename Meta>
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
-    return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
-  }
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
-    return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
-  }
-
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, void* meta, size_t user_metadata_size,
-      pdx::LocalHandle* acquire_fence);
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-      pdx::LocalHandle* acquire_fence);
-
- private:
-  friend BufferHubQueue;
-
-  explicit ConsumerQueue(pdx::LocalChannelHandle handle);
-
-  // Add a consumer buffer to populate the queue. Once added, a consumer buffer
-  // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
-  // will catch the |Post| and |Acquire| the buffer to make it available for
-  // consumer.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
-                              size_t slot);
-
-  pdx::Status<void> OnBufferAllocated() override;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
deleted file mode 100644
index 36ab5f6..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/channel_parcelable.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-namespace android {
-namespace dvr {
-
-enum BufferHubQueueParcelableMagic : uint32_t {
-  Producer = 0x62687170,  // 'bhqp'
-  Consumer = 0x62687163,  // 'bhqc'
-};
-
-template <BufferHubQueueParcelableMagic Magic>
-class BufferHubQueueParcelable : public Parcelable {
- public:
-  BufferHubQueueParcelable() = default;
-
-  BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default;
-  BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept {
-    channel_parcelable_ = std::move(other.channel_parcelable_);
-    return *this;
-  }
-
-  // Constructs an parcelable contains the channel parcelable.
-  explicit BufferHubQueueParcelable(
-      std::unique_ptr<pdx::ChannelParcelable> channel_parcelable)
-      : channel_parcelable_(std::move(channel_parcelable)) {}
-
-  BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete;
-  void operator=(const BufferHubQueueParcelable&) = delete;
-
-  bool IsValid() const;
-
-  // Returns a channel handle constructed from this parcelable object and takes
-  // the ownership of all resources from the parcelable object.
-  pdx::LocalChannelHandle TakeChannelHandle();
-
-  // Serializes the queue parcelable into the given parcel. Note that no system
-  // resources are getting duplicated, nor did the parcel takes ownership of the
-  // queue parcelable. Thus, the parcelable object must remain valid for the
-  // lifetime of the parcel.
-  status_t writeToParcel(Parcel* parcel) const override;
-
-  // Deserialize the queue parcelable from the given parcel. Note that system
-  // resources are duplicated from the parcel into the queue parcelable. Returns
-  // error if the targeting parcelable object is already valid.
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_;
-};
-
-using ProducerQueueParcelable =
-    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>;
-using ConsumerQueueParcelable =
-    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>;
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
deleted file mode 100644
index 2f14f7c..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-
-#include <android-base/unique_fd.h>
-#include <log/log.h>
-#include <sys/epoll.h>
-
-namespace android {
-namespace dvr {
-
-class EpollFileDescriptor {
- public:
-  static const int CTL_ADD = EPOLL_CTL_ADD;
-  static const int CTL_MOD = EPOLL_CTL_MOD;
-  static const int CTL_DEL = EPOLL_CTL_DEL;
-
-  EpollFileDescriptor() : fd_(-1) {}
-
-  // Constructs an EpollFileDescriptor from an integer file descriptor and
-  // takes ownership.
-  explicit EpollFileDescriptor(int fd) : fd_(fd) {}
-
-  bool IsValid() const { return fd_.get() >= 0; }
-
-  int Create() {
-    if (IsValid()) {
-      ALOGW("epoll fd has already been created.");
-      return -EALREADY;
-    }
-
-    fd_.reset(epoll_create1(EPOLL_CLOEXEC));
-
-    if (fd_.get() < 0)
-      return -errno;
-    else
-      return 0;
-  }
-
-  int Control(int op, int target_fd, epoll_event* ev) {
-    if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
-      return -errno;
-    else
-      return 0;
-  }
-
-  int Wait(epoll_event* events, int maxevents, int timeout) {
-    int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
-
-    if (ret < 0)
-      return -errno;
-    else
-      return ret;
-  }
-
-  int Get() const { return fd_.get(); }
-
- private:
-  base::unique_fd fd_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
deleted file mode 100644
index e373376..0000000
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-header_libraries = [
-    "libdvr_headers",
-]
-
-shared_libraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libgui",
-    "liblog",
-    "libhardware",
-    "libui",
-    "libutils",
-    "libnativewindow",
-    "libpdx_default_transport",
-]
-
-static_libraries = [
-    "libchrome",
-    "libdvrcommon",
-    "libperformance",
-]
-
-cc_test {
-    srcs: ["buffer_hub_queue-test.cpp"],
-    header_libs: header_libraries,
-    static_libs: static_libraries,
-    shared_libs: shared_libraries,
-    cflags: [
-        "-DLOG_TAG=\"buffer_hub_queue-test\"",
-        "-DTRACE=0",
-        "-O0",
-        "-g",
-        "-Wall",
-        "-Werror",
-        "-Wno-error=sign-compare", // to fix later
-    ],
-    name: "buffer_hub_queue-test",
-}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
deleted file mode 100644
index 6ae603b..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ /dev/null
@@ -1,1083 +0,0 @@
-#include <base/logging.h>
-#include <binder/Parcel.h>
-#include <dvr/dvr_api.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-
-#include <gtest/gtest.h>
-#include <poll.h>
-#include <sys/eventfd.h>
-
-#include <vector>
-
-// Enable/disable debug logging.
-#define TRACE 0
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalChannelHandle;
-using pdx::LocalHandle;
-
-namespace {
-
-constexpr uint32_t kBufferWidth = 100;
-constexpr uint32_t kBufferHeight = 1;
-constexpr uint32_t kBufferLayerCount = 1;
-constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
-constexpr int kTimeoutMs = 100;
-constexpr int kNoTimeout = 0;
-
-class BufferHubQueueTest : public ::testing::Test {
- public:
-  bool CreateProducerQueue(const ProducerQueueConfig& config,
-                           const UsagePolicy& usage) {
-    producer_queue_ = ProducerQueue::Create(config, usage);
-    return producer_queue_ != nullptr;
-  }
-
-  bool CreateConsumerQueue() {
-    if (producer_queue_) {
-      consumer_queue_ = producer_queue_->CreateConsumerQueue();
-      return consumer_queue_ != nullptr;
-    } else {
-      return false;
-    }
-  }
-
-  bool CreateQueues(const ProducerQueueConfig& config,
-                    const UsagePolicy& usage) {
-    return CreateProducerQueue(config, usage) && CreateConsumerQueue();
-  }
-
-  void AllocateBuffer(size_t* slot_out = nullptr) {
-    // Create producer buffer.
-    auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                                  kBufferLayerCount,
-                                                  kBufferFormat, kBufferUsage);
-
-    ASSERT_TRUE(status.ok());
-    size_t slot = status.take();
-    if (slot_out)
-      *slot_out = slot;
-  }
-
-  bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) {
-    pollfd pfd{queue->queue_fd(), POLLIN, 0};
-    int ret;
-    do {
-      ret = poll(&pfd, 1, timeout_ms);
-    } while (ret == -1 && errno == EINTR);
-
-    if (ret < 0) {
-      ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(),
-            strerror(errno));
-      return false;
-    } else if (ret == 0) {
-      return false;
-    }
-    return queue->HandleQueueEvents();
-  }
-
- protected:
-  ProducerQueueConfigBuilder config_builder_;
-  std::unique_ptr<ProducerQueue> producer_queue_;
-  std::unique_ptr<ConsumerQueue> consumer_queue_;
-};
-
-TEST_F(BufferHubQueueTest, TestDequeue) {
-  const int64_t nb_dequeue_times = 16;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate only one buffer.
-  AllocateBuffer();
-
-  // But dequeue multiple times.
-  for (int64_t i = 0; i < nb_dequeue_times; i++) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    // Producer gains a buffer.
-    auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-
-    // Consumer acquires a buffer.
-    auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-    auto c1 = c1_status.take();
-    ASSERT_NE(c1, nullptr);
-    EXPECT_EQ(mi.index, i);
-    EXPECT_EQ(mo.index, i);
-
-    // Consumer releases the buffer.
-    EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
-  }
-}
-
-TEST_F(BufferHubQueueTest,
-       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate 3 buffers to use.
-  const size_t test_queue_capacity = 3;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    AllocateBuffer();
-  }
-  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
-  size_t producer_slot, consumer_slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-
-  // Producer posts 2 buffers and remember their posted sequence.
-  std::deque<size_t> posted_slots;
-  for (int64_t i = 0; i < 2; i++) {
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer should not be gaining posted buffer when there are still
-    // available buffers to gain.
-    auto found_iter =
-        std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
-    EXPECT_EQ(found_iter, posted_slots.end());
-    posted_slots.push_back(producer_slot);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-  }
-
-  // Consumer acquires one buffer.
-  auto c1_status =
-      consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
-  EXPECT_TRUE(c1_status.ok());
-  auto c1 = c1_status.take();
-  ASSERT_NE(c1, nullptr);
-  // Consumer should get the oldest posted buffer. No checks here.
-  // posted_slots[0] should be in acquired state now.
-  EXPECT_EQ(mo.index, 0);
-  // Consumer releases the buffer.
-  EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
-  // posted_slots[0] should be in released state now.
-
-  // Producer gain and post 2 buffers.
-  for (int64_t i = 0; i < 2; i++) {
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // The gained buffer should be the one in released state or the one haven't
-    // been use.
-    EXPECT_NE(posted_slots[1], producer_slot);
-
-    mi.index = i + 2;
-    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-  }
-
-  // Producer gains a buffer.
-  auto p1_status =
-      producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-  EXPECT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // The gained buffer should be the oldest posted buffer.
-  EXPECT_EQ(posted_slots[1], producer_slot);
-
-  // Producer posts the buffer.
-  mi.index = 4;
-  EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-}
-
-TEST_F(BufferHubQueueTest,
-       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate 4 buffers to use.
-  const size_t test_queue_capacity = 4;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    AllocateBuffer();
-  }
-  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
-  // Post all allowed buffers and remember their posted sequence.
-  std::deque<size_t> posted_slots;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    // Producer gains a buffer.
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer should not be gaining posted buffer when there are still
-    // available buffers to gain.
-    auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
-    EXPECT_EQ(found_iter, posted_slots.end());
-    posted_slots.push_back(slot);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-  }
-
-  // Gain posted buffers in sequence.
-  const int64_t nb_dequeue_all_times = 2;
-  for (int j = 0; j < nb_dequeue_all_times; ++j) {
-    for (int i = 0; i < test_queue_capacity; ++i) {
-      size_t slot;
-      LocalHandle fence;
-      DvrNativeBufferMetadata mi, mo;
-
-      // Producer gains a buffer.
-      auto p1_status =
-          producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
-      EXPECT_TRUE(p1_status.ok());
-      auto p1 = p1_status.take();
-      ASSERT_NE(p1, nullptr);
-
-      // The gained buffer should be the oldest posted buffer.
-      EXPECT_EQ(posted_slots[i], slot);
-
-      // Producer posts the buffer.
-      mi.index = i + test_queue_capacity * (j + 1);
-      EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-    }
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestProducerConsumer) {
-  const size_t kBufferCount = 16;
-  size_t slot;
-  DvrNativeBufferMetadata mi, mo;
-  LocalHandle fence;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-
-    // Producer queue has all the available buffers on initialize.
-    ASSERT_EQ(producer_queue_->count(), i + 1);
-    ASSERT_EQ(producer_queue_->capacity(), i + 1);
-
-    // Consumer queue has no avaiable buffer on initialize.
-    ASSERT_EQ(consumer_queue_->count(), 0U);
-    // Consumer queue does not import buffers until a dequeue is issued.
-    ASSERT_EQ(consumer_queue_->capacity(), i);
-    // Dequeue returns timeout since no buffer is ready to consumer, but
-    // this implicitly triggers buffer import and bump up |capacity|.
-    auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
-    ASSERT_FALSE(status.ok());
-    ASSERT_EQ(ETIMEDOUT, status.error());
-    ASSERT_EQ(consumer_queue_->capacity(), i + 1);
-  }
-
-  // Use eventfd as a stand-in for a fence.
-  LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    // First time there is no buffer available to dequeue.
-    auto consumer_status =
-        consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
-    ASSERT_FALSE(consumer_status.ok());
-    ASSERT_EQ(consumer_status.error(), ETIMEDOUT);
-
-    // Make sure Producer buffer is POSTED so that it's ready to Accquire
-    // in the consumer's Dequeue() function.
-    auto producer_status =
-        producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    auto producer = producer_status.take();
-    ASSERT_NE(nullptr, producer);
-
-    mi.index = static_cast<int64_t>(i);
-    ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0);
-
-    // Second time the just the POSTED buffer should be dequeued.
-    consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok());
-    EXPECT_TRUE(fence.IsValid());
-
-    auto consumer = consumer_status.take();
-    ASSERT_NE(nullptr, consumer);
-    ASSERT_EQ(mi.index, mo.index);
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestInsertBuffer) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  consumer_queue_ = producer_queue_->CreateConsumerQueue();
-  ASSERT_TRUE(consumer_queue_ != nullptr);
-  EXPECT_EQ(producer_queue_->capacity(), 0);
-  EXPECT_EQ(consumer_queue_->capacity(), 0);
-
-  std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
-      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
-  ASSERT_TRUE(p1 != nullptr);
-  ASSERT_EQ(p1->GainAsync(), 0);
-
-  // Inserting a posted buffer will fail.
-  DvrNativeBufferMetadata meta;
-  EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
-  auto status_or_slot = producer_queue_->InsertBuffer(p1);
-  EXPECT_FALSE(status_or_slot.ok());
-  EXPECT_EQ(status_or_slot.error(), EINVAL);
-
-  // Inserting a gained buffer will succeed.
-  std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
-      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
-  ASSERT_EQ(p2->GainAsync(), 0);
-  ASSERT_TRUE(p2 != nullptr);
-  status_or_slot = producer_queue_->InsertBuffer(p2);
-  EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
-  // This is the first buffer inserted, should take slot 0.
-  size_t slot = status_or_slot.get();
-  EXPECT_EQ(slot, 0);
-
-  // Wait and expect the consumer to kick up the newly inserted buffer.
-  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
-}
-
-TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-  DvrNativeBufferMetadata mo;
-
-  // Allocate buffers.
-  const size_t kBufferCount = 4u;
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-  }
-  ASSERT_EQ(kBufferCount, producer_queue_->count());
-  ASSERT_EQ(kBufferCount, producer_queue_->capacity());
-
-  consumer_queue_ = producer_queue_->CreateConsumerQueue();
-  ASSERT_NE(nullptr, consumer_queue_);
-
-  // Check that buffers are correctly imported on construction.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_EQ(0u, consumer_queue_->count());
-
-  // Dequeue all the buffers and keep track of them in an array. This prevents
-  // the producer queue ring buffer ref counts from interfering with the tests.
-  struct Entry {
-    std::shared_ptr<ProducerBuffer> buffer;
-    LocalHandle fence;
-    size_t slot;
-  };
-  std::array<Entry, kBufferCount> buffers;
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    Entry* entry = &buffers[i];
-    auto producer_status =
-        producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
-    ASSERT_TRUE(producer_status.ok());
-    entry->buffer = producer_status.take();
-    ASSERT_NE(nullptr, entry->buffer);
-  }
-
-  // Remove a buffer and make sure both queues reflect the change.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
-  // As long as the removed buffer is still alive the consumer queue won't know
-  // its gone.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Release the removed buffer.
-  buffers[0].buffer = nullptr;
-
-  // Now the consumer queue should know it's gone.
-  EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs));
-  ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-
-  // Allocate a new buffer. This should take the first empty slot.
-  size_t slot;
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[0].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  // The consumer queue should pick up the new buffer.
-  EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Remove and allocate a buffer.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-  buffers[1].buffer = nullptr;
-
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[1].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  // The consumer queue should pick up the new buffer but the count shouldn't
-  // change.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Remove and allocate a buffer, but don't free the buffer right away.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[2].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Release the producer buffer to trigger a POLLHUP event for an already
-  // removed buffer.
-  buffers[2].buffer = nullptr;
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-}
-
-TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
-  // ProducerConfigureBuilder doesn't set Metadata{size}, which means there
-  // is no metadata associated with this BufferQueue's buffer.
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate buffers.
-  const size_t kBufferCount = 4u;
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-  }
-  ASSERT_EQ(kBufferCount, producer_queue_->count());
-
-  // Build a silent consumer queue to test multi-consumer queue features.
-  auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
-  ASSERT_NE(nullptr, silent_queue);
-
-  // Check that silent queue doesn't import buffers on creation.
-  EXPECT_EQ(silent_queue->capacity(), 0U);
-
-  // Dequeue and post a buffer.
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto producer_status =
-      producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(producer_status.ok());
-  auto producer_buffer = producer_status.take();
-  ASSERT_NE(producer_buffer, nullptr);
-  EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-  // After post, check the number of remaining available buffers.
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  // Currently we expect no buffer to be available prior to calling
-  // WaitForBuffers/HandleQueueEvents.
-  // TODO(eieio): Note this behavior may change in the future.
-  EXPECT_EQ(silent_queue->count(), 0U);
-  EXPECT_FALSE(silent_queue->HandleQueueEvents());
-  EXPECT_EQ(silent_queue->count(), 0U);
-
-  // Build a new consumer queue to test multi-consumer queue features.
-  consumer_queue_ = silent_queue->CreateConsumerQueue();
-  ASSERT_NE(consumer_queue_, nullptr);
-
-  // Check that buffers are correctly imported on construction.
-  EXPECT_EQ(consumer_queue_->capacity(), kBufferCount);
-  // Buffers are only imported, but their availability is not checked until
-  // first call to Dequeue().
-  EXPECT_EQ(consumer_queue_->count(), 0U);
-
-  // Reclaim released/ignored buffers.
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  usleep(10000);
-  WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  // Post another buffer.
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(producer_status.ok());
-  producer_buffer = producer_status.take();
-  ASSERT_NE(producer_buffer, nullptr);
-  EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-
-  // Verify that the consumer queue receives it.
-  size_t consumer_queue_count = consumer_queue_->count();
-  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
-  EXPECT_GT(consumer_queue_->count(), consumer_queue_count);
-
-  // Save the current consumer queue buffer count to compare after the dequeue.
-  consumer_queue_count = consumer_queue_->count();
-
-  // Dequeue and acquire/release (discard) buffers on the consumer end.
-  auto consumer_status =
-      consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(consumer_status.ok());
-  auto consumer_buffer = consumer_status.take();
-  ASSERT_NE(consumer_buffer, nullptr);
-  consumer_buffer->Discard();
-
-  // Buffer should be returned to the producer queue without being handled by
-  // the silent consumer queue.
-  EXPECT_LT(consumer_queue_->count(), consumer_queue_count);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 2);
-
-  WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-}
-
-struct TestUserMetadata {
-  char a;
-  int32_t b;
-  int64_t c;
-};
-
-constexpr uint64_t kUserMetadataSize =
-    static_cast<uint64_t>(sizeof(TestUserMetadata));
-
-TEST_F(BufferHubQueueTest, TestUserMetadata) {
-  ASSERT_TRUE(CreateQueues(
-      config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
-  AllocateBuffer();
-
-  std::vector<TestUserMetadata> user_metadata_list = {
-      {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
-
-  for (auto user_metadata : user_metadata_list) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // TODO(b/69469185): Test against metadata from consumer once we implement
-    // release metadata properly.
-    // EXPECT_EQ(mo.user_metadata_ptr, 0U);
-    // EXPECT_EQ(mo.user_metadata_size, 0U);
-
-    mi.user_metadata_size = kUserMetadataSize;
-    mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-    EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-    auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-    auto c1 = c1_status.take();
-    ASSERT_NE(c1, nullptr);
-
-    EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize);
-    auto out_user_metadata =
-        reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr);
-    EXPECT_EQ(user_metadata.a, out_user_metadata->a);
-    EXPECT_EQ(user_metadata.b, out_user_metadata->b);
-    EXPECT_EQ(user_metadata.c, out_user_metadata->c);
-
-    // When release, empty metadata is also legit.
-    mi.user_metadata_size = 0U;
-    mi.user_metadata_ptr = 0U;
-    c1->ReleaseAsync(&mi, {});
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) {
-  ASSERT_TRUE(CreateQueues(
-      config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
-  AllocateBuffer();
-
-  TestUserMetadata user_metadata;
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // Post with mismatched user metadata size will fail. But the producer buffer
-  // itself should stay untouched.
-  mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-  mi.user_metadata_size = kUserMetadataSize + 1;
-  EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG);
-  // Post with the exact same user metdata size can success.
-  mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-  mi.user_metadata_size = kUserMetadataSize;
-  EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestEnqueue) {
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{}));
-  AllocateBuffer();
-
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(nullptr, p1);
-
-  producer_queue_->Enqueue(p1, slot, 0ULL);
-  auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_FALSE(c1_status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  size_t ps1;
-  AllocateBuffer();
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // producer queue is exhausted
-  size_t ps2;
-  auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
-  ASSERT_FALSE(p2_status.ok());
-  ASSERT_EQ(ETIMEDOUT, p2_status.error());
-
-  // dynamically add buffer.
-  AllocateBuffer();
-  ASSERT_EQ(producer_queue_->count(), 1U);
-  ASSERT_EQ(producer_queue_->capacity(), 2U);
-
-  // now we can dequeue again
-  p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
-  ASSERT_TRUE(p2_status.ok());
-  auto p2 = p2_status.take();
-  ASSERT_NE(p2, nullptr);
-  ASSERT_EQ(producer_queue_->count(), 0U);
-  // p1 and p2 should have different slot number
-  ASSERT_NE(ps1, ps2);
-
-  // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
-  // are called. So far consumer_queue_ should be empty.
-  ASSERT_EQ(consumer_queue_->count(), 0U);
-
-  int64_t seq = 1;
-  mi.index = seq;
-  ASSERT_EQ(p1->PostAsync(&mi, {}), 0);
-
-  size_t cs1, cs2;
-  auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
-  ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-  auto c1 = c1_status.take();
-  ASSERT_NE(c1, nullptr);
-  ASSERT_EQ(consumer_queue_->count(), 0U);
-  ASSERT_EQ(consumer_queue_->capacity(), 2U);
-  ASSERT_EQ(cs1, ps1);
-
-  ASSERT_EQ(p2->PostAsync(&mi, {}), 0);
-  auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence);
-  ASSERT_TRUE(c2_status.ok());
-  auto c2 = c2_status.take();
-  ASSERT_NE(c2, nullptr);
-  ASSERT_EQ(cs2, ps2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-  auto status = producer_queue_->AllocateBuffers(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage, /*buffer_count=*/2);
-  ASSERT_TRUE(status.ok());
-  std::vector<size_t> buffer_slots = status.take();
-  ASSERT_EQ(buffer_slots.size(), 2);
-  ASSERT_EQ(producer_queue_->capacity(), 2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-  auto status = producer_queue_->AllocateBuffers(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage, /*buffer_count=*/0);
-  ASSERT_TRUE(status.ok());
-  std::vector<size_t> buffer_slots = status.take();
-  ASSERT_EQ(buffer_slots.size(), 0);
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageSetMask) {
-  const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(
-      CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0}));
-
-  // When allocation, leave out |set_mask| from usage bits on purpose.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~set_mask);
-  ASSERT_TRUE(status.ok());
-
-  LocalHandle fence;
-  size_t slot;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_EQ(p1->usage() & set_mask, set_mask);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageClearMask) {
-  const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(
-      CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0}));
-
-  // When allocation, add |clear_mask| into usage bits on purpose.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | clear_mask);
-  ASSERT_TRUE(status.ok());
-
-  LocalHandle fence;
-  size_t slot;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_EQ(p1->usage() & clear_mask, 0U);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
-  const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{0, 0, deny_set_mask, 0}));
-
-  // Now that |deny_set_mask| is illegal, allocation without those bits should
-  // be able to succeed.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~deny_set_mask);
-  ASSERT_TRUE(status.ok());
-
-  // While allocation with those bits should fail.
-  status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                           kBufferLayerCount, kBufferFormat,
-                                           kBufferUsage | deny_set_mask);
-  ASSERT_FALSE(status.ok());
-  ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
-  const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{0, 0, 0, deny_clear_mask}));
-
-  // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
-  // mandatory), allocation with those bits should be able to succeed.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | deny_clear_mask);
-  ASSERT_TRUE(status.ok());
-
-  // While allocation without those bits should fail.
-  status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                           kBufferLayerCount, kBufferFormat,
-                                           kBufferUsage & ~deny_clear_mask);
-  ASSERT_FALSE(status.ok());
-  ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestQueueInfo) {
-  static const bool kIsAsync = true;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync)
-                               .SetDefaultWidth(kBufferWidth)
-                               .SetDefaultHeight(kBufferHeight)
-                               .SetDefaultFormat(kBufferFormat)
-                               .Build(),
-                           UsagePolicy{}));
-
-  EXPECT_EQ(producer_queue_->default_width(), kBufferWidth);
-  EXPECT_EQ(producer_queue_->default_height(), kBufferHeight);
-  EXPECT_EQ(producer_queue_->default_format(), kBufferFormat);
-  EXPECT_EQ(producer_queue_->is_async(), kIsAsync);
-
-  EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth);
-  EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight);
-  EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat);
-  EXPECT_EQ(consumer_queue_->is_async(), kIsAsync);
-}
-
-TEST_F(BufferHubQueueTest, TestFreeAllBuffers) {
-  constexpr size_t kBufferCount = 2;
-
-#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers)  \
-  EXPECT_EQ(consumer_queue_->count(), 0U);          \
-  EXPECT_EQ(consumer_queue_->capacity(), 0U);       \
-  EXPECT_EQ(producer_queue_->count(), 0U);          \
-  EXPECT_EQ(producer_queue_->capacity(), 0U);       \
-  for (size_t i = 0; i < num_buffers; i++) {        \
-    AllocateBuffer();                               \
-  }                                                 \
-  EXPECT_EQ(producer_queue_->count(), num_buffers); \
-  EXPECT_EQ(producer_queue_->capacity(), num_buffers);
-
-  size_t slot;
-  LocalHandle fence;
-  pdx::Status<void> status;
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
-  pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
-  std::shared_ptr<ConsumerBuffer> consumer_buffer;
-  std::shared_ptr<ProducerBuffer> producer_buffer;
-  DvrNativeBufferMetadata mi, mo;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Free all buffers when buffers are avaible for dequeue.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when one buffer is dequeued.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(producer_status.ok());
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are dequeued.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-  }
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when one buffer is posted.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(producer_status.ok());
-  producer_buffer = producer_status.take();
-  ASSERT_NE(nullptr, producer_buffer);
-  ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence));
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are posted.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    producer_buffer = producer_status.take();
-    ASSERT_NE(producer_buffer, nullptr);
-    ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
-  }
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are acquired.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    producer_buffer = producer_status.take();
-    ASSERT_NE(producer_buffer, nullptr);
-    ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
-    consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
-  }
-
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // In addition to FreeAllBuffers() from the queue, it is also required to
-  // delete all references to the ProducerBuffer (i.e. the PDX client).
-  producer_buffer = nullptr;
-
-  // Crank consumer queue events to pickup EPOLLHUP events on the queue.
-  consumer_queue_->HandleQueueEvents();
-
-  // One last check.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-
-#undef CHECK_NO_BUFFER_THEN_ALLOCATE
-}
-
-TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) {
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
-                           UsagePolicy{}));
-
-  // Allocate only one buffer.
-  AllocateBuffer();
-
-  // Export should fail as the queue is not empty.
-  auto status = producer_queue_->TakeAsParcelable();
-  EXPECT_FALSE(status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  auto s1 = producer_queue_->TakeAsParcelable();
-  EXPECT_TRUE(s1.ok());
-
-  ProducerQueueParcelable output_parcelable = s1.take();
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  Parcel parcel;
-  status_t res;
-  res = output_parcelable.writeToParcel(&parcel);
-  EXPECT_EQ(res, OK);
-
-  // After written into parcelable, the output_parcelable is still valid has
-  // keeps the producer channel alive.
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  // Creating producer buffer should fail.
-  auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                            kBufferLayerCount, kBufferFormat,
-                                            kBufferUsage);
-  ASSERT_FALSE(s2.ok());
-
-  // Reset the data position so that we can read back from the same parcel
-  // without doing actually Binder IPC.
-  parcel.setDataPosition(0);
-  producer_queue_ = nullptr;
-
-  // Recreate the producer queue from the parcel.
-  ProducerQueueParcelable input_parcelable;
-  EXPECT_FALSE(input_parcelable.IsValid());
-
-  res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, OK);
-  EXPECT_TRUE(input_parcelable.IsValid());
-
-  EXPECT_EQ(producer_queue_, nullptr);
-  producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle());
-  EXPECT_FALSE(input_parcelable.IsValid());
-  ASSERT_NE(producer_queue_, nullptr);
-
-  // Newly created queue from the parcel can allocate buffer, post buffer to
-  // consumer.
-  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
-  EXPECT_EQ(producer_queue_->count(), 1U);
-  EXPECT_EQ(producer_queue_->capacity(), 1U);
-
-  size_t slot;
-  DvrNativeBufferMetadata producer_meta;
-  DvrNativeBufferMetadata consumer_meta;
-  LocalHandle fence;
-  auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
-  EXPECT_TRUE(s3.ok());
-
-  std::shared_ptr<ProducerBuffer> p1 = s3.take();
-  ASSERT_NE(p1, nullptr);
-
-  producer_meta.timestamp = 42;
-  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
-  // Make sure the buffer can be dequeued from consumer side.
-  auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
-  EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
-  auto consumer = s4.take();
-  ASSERT_NE(consumer, nullptr);
-  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  auto s1 = producer_queue_->CreateConsumerQueueParcelable();
-  EXPECT_TRUE(s1.ok());
-  ConsumerQueueParcelable output_parcelable = s1.take();
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  // Write to a Parcel new object.
-  Parcel parcel;
-  status_t res;
-  res = output_parcelable.writeToParcel(&parcel);
-
-  // Reset the data position so that we can read back from the same parcel
-  // without doing actually Binder IPC.
-  parcel.setDataPosition(0);
-
-  // No consumer queue created yet.
-  EXPECT_EQ(consumer_queue_, nullptr);
-
-  // If the parcel contains a consumer queue, read into a
-  // ProducerQueueParcelable should fail.
-  ProducerQueueParcelable wrongly_typed_parcelable;
-  EXPECT_FALSE(wrongly_typed_parcelable.IsValid());
-  res = wrongly_typed_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, -EINVAL);
-  parcel.setDataPosition(0);
-
-  // Create the consumer queue from the parcel.
-  ConsumerQueueParcelable input_parcelable;
-  EXPECT_FALSE(input_parcelable.IsValid());
-
-  res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, OK);
-  EXPECT_TRUE(input_parcelable.IsValid());
-
-  consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
-  EXPECT_FALSE(input_parcelable.IsValid());
-  ASSERT_NE(consumer_queue_, nullptr);
-
-  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
-  EXPECT_EQ(producer_queue_->count(), 1U);
-  EXPECT_EQ(producer_queue_->capacity(), 1U);
-
-  size_t slot;
-  DvrNativeBufferMetadata producer_meta;
-  DvrNativeBufferMetadata consumer_meta;
-  LocalHandle fence;
-  auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
-  EXPECT_TRUE(s2.ok());
-
-  std::shared_ptr<ProducerBuffer> p1 = s2.take();
-  ASSERT_NE(p1, nullptr);
-
-  producer_meta.timestamp = 42;
-  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
-  // Make sure the buffer can be dequeued from consumer side.
-  auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
-  EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
-  auto consumer = s3.take();
-  ASSERT_NE(consumer, nullptr);
-  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-}  // namespace
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
deleted file mode 100644
index b0ed950..0000000
--- a/libs/vr/libdisplay/Android.bp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "display_client.cpp",
-    "display_manager_client.cpp",
-    "display_protocol.cpp",
-    "shared_buffer_helpers.cpp",
-    "vsync_service.cpp",
-]
-
-localIncludeFiles = [
-    "include",
-]
-
-sharedLibraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "liblog",
-    "libutils",
-    "libui",
-    "libgui",
-    "libhardware",
-    "libsync",
-    "libnativewindow",
-    "libpdx_default_transport",
-]
-
-staticLibraries = [
-    "libdvrcommon",
-    "libbroadcastring",
-]
-
-headerLibraries = [
-    "vulkan_headers",
-    "libdvr_headers",
-]
-
-cc_library {
-    srcs: sourceFiles,
-    cflags: ["-DLOG_TAG=\"libdisplay\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-DGL_GLEXT_PROTOTYPES",
-        "-DEGL_EGLEXT_PROTOTYPES",
-        "-Wall",
-        "-Werror",
-    ],  // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
-    export_include_dirs: localIncludeFiles,
-    shared_libs: sharedLibraries,
-    static_libs: staticLibraries,
-    header_libs: headerLibraries,
-    export_header_lib_headers: headerLibraries,
-
-    name: "libdisplay",
-}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
deleted file mode 100644
index 62856df..0000000
--- a/libs/vr/libdisplay/display_client.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "include/private/dvr/display_client.h"
-
-#include <cutils/native_handle.h>
-#include <log/log.h>
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/status.h>
-
-#include <mutex>
-
-#include <private/dvr/display_protocol.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-Surface::Surface(LocalChannelHandle channel_handle, int* error)
-    : BASE{pdx::default_transport::ClientChannel::Create(
-          std::move(channel_handle))} {
-  auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
-  if (!status) {
-    ALOGE("Surface::Surface: Failed to get surface info: %s",
-          status.GetErrorMessage().c_str());
-    Close(status.error());
-    if (error)
-      *error = status.error();
-  }
-
-  surface_id_ = status.get().surface_id;
-  z_order_ = status.get().z_order;
-  visible_ = status.get().visible;
-}
-
-Surface::Surface(const SurfaceAttributes& attributes, int* error)
-    : BASE{pdx::default_transport::ClientChannelFactory::Create(
-               DisplayProtocol::kClientPath),
-           kInfiniteTimeout} {
-  auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
-  if (!status) {
-    ALOGE("Surface::Surface: Failed to create display surface: %s",
-          status.GetErrorMessage().c_str());
-    Close(status.error());
-    if (error)
-      *error = status.error();
-  }
-
-  surface_id_ = status.get().surface_id;
-  z_order_ = status.get().z_order;
-  visible_ = status.get().visible;
-}
-
-Status<void> Surface::SetVisible(bool visible) {
-  return SetAttributes(
-      {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
-}
-
-Status<void> Surface::SetZOrder(int z_order) {
-  return SetAttributes(
-      {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
-}
-
-Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
-  if (!status) {
-    ALOGE(
-        "Surface::SetAttributes: Failed to set display surface "
-        "attributes: %s",
-        status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  // Set the local cached copies of the attributes we care about from the full
-  // set of attributes sent to the display service.
-  for (const auto& attribute : attributes) {
-    const auto& key = attribute.first;
-    const auto* variant = &attribute.second;
-    bool invalid_value = false;
-    switch (key) {
-      case SurfaceAttribute::Visible:
-        invalid_value =
-            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
-        break;
-      case SurfaceAttribute::ZOrder:
-        invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
-        break;
-    }
-
-    if (invalid_value) {
-      ALOGW(
-          "Surface::SetAttributes: Failed to set display surface "
-          "attribute %d because of incompatible type: %d",
-          key, variant->index());
-    }
-  }
-
-  return {};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
-    uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) {
-  ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
-  auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(
-      ProducerQueueConfigBuilder()
-          .SetDefaultWidth(width)
-          .SetDefaultHeight(height)
-          .SetDefaultFormat(format)
-          .SetMetadataSize(metadata_size)
-          .Build());
-  if (!status) {
-    ALOGE("Surface::CreateQueue: Failed to create queue: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto producer_queue = ProducerQueue::Import(status.take());
-  if (!producer_queue) {
-    ALOGE("Surface::CreateQueue: Failed to import producer queue!");
-    return ErrorStatus(ENOMEM);
-  }
-
-  return {std::move(producer_queue)};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
-    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-    uint64_t usage, size_t capacity, size_t metadata_size) {
-  ALOGD_IF(TRACE,
-           "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
-           "usage=%" PRIx64 " capacity=%zu",
-           width, height, layer_count, format, usage, capacity);
-  auto status = CreateQueue(width, height, format, metadata_size);
-  if (!status)
-    return status.error_status();
-
-  auto producer_queue = status.take();
-
-  ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
-  auto allocate_status = producer_queue->AllocateBuffers(
-      width, height, layer_count, format, usage, capacity);
-  if (!allocate_status) {
-    ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
-          producer_queue->id(), allocate_status.GetErrorMessage().c_str());
-    return allocate_status.error_status();
-  }
-
-  return {std::move(producer_queue)};
-}
-
-DisplayClient::DisplayClient(int* error)
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-               DisplayProtocol::kClientPath),
-           kInfiniteTimeout) {
-  if (error)
-    *error = Client::error();
-}
-
-Status<Metrics> DisplayClient::GetDisplayMetrics() {
-  return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
-}
-
-Status<std::string> DisplayClient::GetConfigurationData(
-    ConfigFileType config_type) {
-  auto status =
-      InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type);
-  if (!status && status.error() != ENOENT) {
-    ALOGE(
-        "DisplayClient::GetConfigurationData: Unable to get"
-        "configuration data. Error: %s",
-        status.GetErrorMessage().c_str());
-  }
-  return status;
-}
-
-Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
-  return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
-}
-
-Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
-    const SurfaceAttributes& attributes) {
-  int error;
-  if (auto client = Surface::Create(attributes, &error))
-    return {std::move(client)};
-  else
-    return ErrorStatus(error);
-}
-
-pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer(
-    DvrGlobalBufferKey key, size_t size, uint64_t usage) {
-  auto status =
-      InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage);
-  if (!status) {
-    ALOGE(
-        "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer "
-        "%s",
-        status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto ion_buffer = std::make_unique<IonBuffer>();
-  auto native_buffer_handle = status.take();
-  const int ret = native_buffer_handle.Import(ion_buffer.get());
-  if (ret < 0) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
-        "key=%d; error=%s",
-        key, strerror(-ret));
-    return ErrorStatus(-ret);
-  }
-
-  return {std::move(ion_buffer)};
-}
-
-pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key);
-  if (!status) {
-    ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s",
-          status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer(
-    DvrGlobalBufferKey key) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key);
-  if (!status) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; "
-        "error=%s",
-        key, status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto ion_buffer = std::make_unique<IonBuffer>();
-  auto native_buffer_handle = status.take();
-  const int ret = native_buffer_handle.Import(ion_buffer.get());
-  if (ret < 0) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
-        "key=%d; error=%s",
-        key, strerror(-ret));
-    return ErrorStatus(-ret);
-  }
-
-  return {std::move(ion_buffer)};
-}
-
-Status<bool> DisplayClient::IsVrAppRunning() {
-  return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
-}
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
deleted file mode 100644
index fdeeb70..0000000
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "include/private/dvr/display_manager_client.h"
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <utils/Log.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-DisplayManagerClient::DisplayManagerClient()
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-          DisplayManagerProtocol::kClientPath)) {}
-
-DisplayManagerClient::~DisplayManagerClient() {}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerClient::GetSurfaceState() {
-  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
-        status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-pdx::Status<std::unique_ptr<ConsumerQueue>>
-DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
-  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
-      surface_id, queue_id);
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
-        "surface_id=%d queue_id=%d: %s",
-        surface_id, queue_id, status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return {ConsumerQueue::Import(status.take())};
-}
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
deleted file mode 100644
index 773f9a5..0000000
--- a/libs/vr/libdisplay/display_protocol.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "include/private/dvr/display_protocol.h"
-
-namespace android {
-namespace dvr {
-namespace display {
-
-constexpr char DisplayProtocol::kClientPath[];
-constexpr char DisplayManagerProtocol::kClientPath[];
-constexpr char VSyncProtocol::kClientPath[];
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
deleted file mode 100644
index 81546ac..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_CLIENT_H_
-
-#include <dvr/dvr_api.h>
-#include <hardware/hwcomposer.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-namespace display {
-
-class Surface : public pdx::ClientBase<Surface> {
- public:
-  // Utility named constructor. This can be removed once ClientBase::Create is
-  // refactored to return Status<T> types.
-  static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
-      const SurfaceAttributes& attributes) {
-    int error;
-    pdx::Status<std::unique_ptr<Surface>> status;
-    if (auto surface = Create(attributes, &error))
-      status.SetValue(std::move(surface));
-    else
-      status.SetError(error);
-    return status;
-  }
-
-  int surface_id() const { return surface_id_; }
-  int z_order() const { return z_order_; }
-  bool visible() const { return visible_; }
-
-  pdx::Status<void> SetVisible(bool visible);
-  pdx::Status<void> SetZOrder(int z_order);
-  pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
-
-  // Creates an empty queue.
-  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
-                                                          uint32_t height,
-                                                          uint32_t format,
-                                                          size_t metadata_size);
-
-  // Creates a queue and populates it with |capacity| buffers of the specified
-  // parameters.
-  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
-                                                          uint32_t height,
-                                                          uint32_t layer_count,
-                                                          uint32_t format,
-                                                          uint64_t usage,
-                                                          size_t capacity,
-                                                          size_t metadata_size);
-
- private:
-  friend BASE;
-
-  int surface_id_ = -1;
-  int z_order_ = 0;
-  bool visible_ = false;
-
-  // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
-  // returns Status<T>.
-  explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
-  explicit Surface(pdx::LocalChannelHandle channel_handle,
-                   int* error = nullptr);
-
-  Surface(const Surface&) = delete;
-  void operator=(const Surface&) = delete;
-};
-
-class DisplayClient : public pdx::ClientBase<DisplayClient> {
- public:
-  pdx::Status<Metrics> GetDisplayMetrics();
-  pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
-  pdx::Status<uint8_t> GetDisplayIdentificationPort();
-  pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
-      DvrGlobalBufferKey key, size_t size, uint64_t usage);
-  pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
-  pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer(
-      DvrGlobalBufferKey key);
-  pdx::Status<std::unique_ptr<Surface>> CreateSurface(
-      const SurfaceAttributes& attributes);
-
-  // Temporary query for current VR status. Will be removed later.
-  pdx::Status<bool> IsVrAppRunning();
-
- private:
-  friend BASE;
-
-  explicit DisplayClient(int* error = nullptr);
-
-  DisplayClient(const DisplayClient&) = delete;
-  void operator=(const DisplayClient&) = delete;
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
deleted file mode 100644
index 45aef51..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-class ConsumerQueue;
-
-namespace display {
-
-class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
- public:
-  ~DisplayManagerClient() override;
-
-  pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
-  pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
-                                                              int queue_id);
-
-  using Client::event_fd;
-
-  pdx::Status<int> GetEventMask(int events) {
-    if (auto* client_channel = GetChannel())
-      return client_channel->GetEventMask(events);
-    else
-      return pdx::ErrorStatus(EINVAL);
-  }
-
- private:
-  friend BASE;
-
-  DisplayManagerClient();
-
-  DisplayManagerClient(const DisplayManagerClient&) = delete;
-  void operator=(const DisplayManagerClient&) = delete;
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
deleted file mode 100644
index 9f4cc4a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ /dev/null
@@ -1,304 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
-#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
-
-#include <sys/types.h>
-
-#include <array>
-#include <map>
-
-#include <dvr/dvr_display_types.h>
-
-#include <dvr/dvr_api.h>
-#include <pdx/rpc/buffer_wrapper.h>
-#include <pdx/rpc/remote_method.h>
-#include <pdx/rpc/serializable.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/bufferhub_rpc.h>
-
-// RPC protocol definitions for DVR display services (VrFlinger).
-
-namespace android {
-namespace dvr {
-namespace display {
-
-// Native display metrics.
-struct Metrics {
-  // Basic display properties.
-  uint32_t display_width;
-  uint32_t display_height;
-  uint32_t display_x_dpi;
-  uint32_t display_y_dpi;
-  uint32_t vsync_period_ns;
-
-  // HMD metrics.
-  // TODO(eieio): Determine how these fields should be populated. On phones
-  // these values are determined at runtime by VrCore based on which headset the
-  // phone is in. On dedicated hardware this needs to come from somewhere else.
-  // Perhaps these should be moved to a separate structure that is returned by a
-  // separate runtime call.
-  uint32_t distorted_width;
-  uint32_t distorted_height;
-  uint32_t hmd_ipd_mm;
-  float inter_lens_distance_m;
-  std::array<float, 4> left_fov_lrbt;
-  std::array<float, 4> right_fov_lrbt;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
-                           display_x_dpi, display_y_dpi, vsync_period_ns,
-                           distorted_width, distorted_height, hmd_ipd_mm,
-                           inter_lens_distance_m, left_fov_lrbt,
-                           right_fov_lrbt);
-};
-
-// Serializable base type for enum structs. Enum structs are easier to use than
-// enum classes, especially for bitmasks. This base type provides common
-// utilities for flags types.
-template <typename Integer>
-class Flags {
- public:
-  using Base = Flags<Integer>;
-  using Type = Integer;
-
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Flags(const Integer& value) : value_{value} {}
-  Flags(const Flags&) = default;
-  Flags& operator=(const Flags&) = default;
-
-  Integer value() const { return value_; }
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator Integer() const { return value_; }
-
-  bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
-  bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
-
-  void Set(Integer bits) { value_ |= bits; }
-  void Clear(Integer bits) { value_ &= ~bits; }
-
-  Integer operator|(Integer bits) const { return value_ | bits; }
-  Integer operator&(Integer bits) const { return value_ & bits; }
-
-  Flags& operator|=(Integer bits) {
-    value_ |= bits;
-    return *this;
-  }
-  Flags& operator&=(Integer bits) {
-    value_ &= bits;
-    return *this;
-  }
-
- private:
-  Integer value_;
-
-  PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
-};
-
-// Flags indicating what changed since last update.
-struct SurfaceUpdateFlags : public Flags<uint32_t> {
-  enum : Type {
-    None = DVR_SURFACE_UPDATE_FLAGS_NONE,
-    NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
-    BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
-    VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
-    AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
-  };
-
-  SurfaceUpdateFlags() : Base{None} {}
-  using Base::Base;
-};
-
-// Surface attribute key/value types.
-using SurfaceAttributeKey = int32_t;
-using SurfaceAttributeValue =
-    pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
-                      std::array<float, 3>, std::array<float, 4>,
-                      std::array<float, 8>, std::array<float, 16>>;
-
-// Defined surface attribute keys.
-struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
-  enum : Type {
-    // Keys in the negative integer space are interpreted by VrFlinger for
-    // direct surfaces.
-    Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
-    ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
-    Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
-
-    // Invalid key. May be used to terminate C style lists in public API code.
-    Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
-
-    // Positive keys are interpreted by the compositor only.
-    FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
-  };
-
-  SurfaceAttribute() : Base{Invalid} {}
-  using Base::Base;
-};
-
-// Collection of surface attribute key/value pairs.
-using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
-
-struct SurfaceState {
-  int32_t surface_id;
-  int32_t process_id;
-  int32_t user_id;
-
-  SurfaceAttributes surface_attributes;
-  SurfaceUpdateFlags update_flags;
-  std::vector<int32_t> queue_ids;
-
-  // Convenience accessors.
-  bool GetVisible() const {
-    bool bool_value = false;
-    GetAttribute(SurfaceAttribute::Visible, &bool_value,
-                 ValidTypes<int32_t, int64_t, bool, float>{});
-    return bool_value;
-  }
-
-  int GetZOrder() const {
-    int int_value = 0;
-    GetAttribute(SurfaceAttribute::ZOrder, &int_value,
-                 ValidTypes<int32_t, int64_t, float>{});
-    return int_value;
-  }
-
- private:
-  template <typename... Types>
-  struct ValidTypes {};
-
-  template <typename ReturnType, typename... Types>
-  bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
-                    ValidTypes<Types...>) const {
-    auto search = surface_attributes.find(key);
-    if (search != surface_attributes.end())
-      return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
-    else
-      return false;
-  }
-
-  PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
-                           surface_attributes, update_flags, queue_ids);
-};
-
-struct SurfaceInfo {
-  int surface_id;
-  bool visible;
-  int z_order;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
-};
-
-enum class ConfigFileType : uint32_t {
-  kLensMetrics,
-  kDeviceMetrics,
-  kDeviceConfiguration,
-  kDeviceEdid
-};
-
-struct DisplayProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/client";
-
-  // Op codes.
-  enum {
-    kOpGetMetrics = 0,
-    kOpGetConfigurationData,
-    kOpSetupGlobalBuffer,
-    kOpDeleteGlobalBuffer,
-    kOpGetGlobalBuffer,
-    kOpIsVrAppRunning,
-    kOpCreateSurface,
-    kOpGetSurfaceInfo,
-    kOpCreateQueue,
-    kOpSetAttributes,
-    kOpGetDisplayIdentificationPort,
-  };
-
-  // Aliases.
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  using Void = pdx::rpc::Void;
-
-  // Methods.
-  PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
-  PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
-                    std::string(ConfigFileType config_type));
-  PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
-                    kOpGetDisplayIdentificationPort, uint8_t(Void));
-  PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
-                    LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
-                                            uint64_t usage));
-  PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer,
-                    void(DvrGlobalBufferKey key));
-  PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer,
-                    LocalNativeBufferHandle(DvrGlobalBufferKey key));
-  PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
-  PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
-                    SurfaceInfo(const SurfaceAttributes& attributes));
-  PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
-  PDX_REMOTE_METHOD(
-      CreateQueue, kOpCreateQueue,
-      LocalChannelHandle(const ProducerQueueConfig& producer_config));
-  PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
-                    void(const SurfaceAttributes& attributes));
-};
-
-struct DisplayManagerProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/manager";
-
-  // Op codes.
-  enum {
-    kOpGetSurfaceState = 0,
-    kOpGetSurfaceQueue,
-  };
-
-  // Aliases.
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  using Void = pdx::rpc::Void;
-
-  // Methods.
-  PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
-                    std::vector<SurfaceState>(Void));
-  PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
-                    LocalChannelHandle(int surface_id, int queue_id));
-};
-
-struct VSyncSchedInfo {
-  int64_t vsync_period_ns;
-  int64_t timestamp_ns;
-  uint32_t next_vsync_count;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
-                           next_vsync_count);
-};
-
-struct VSyncProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/vsync";
-
-  // Op codes.
-  enum {
-    kOpWait = 0,
-    kOpAck,
-    kOpGetLastTimestamp,
-    kOpGetSchedInfo,
-    kOpAcknowledge,
-  };
-
-  // Aliases.
-  using Void = pdx::rpc::Void;
-  using Timestamp = int64_t;
-
-  // Methods.
-  PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
-  PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
-  PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
-  PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
deleted file mode 100644
index 20541a6..0000000
--- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-
-#include <assert.h>
-#include <tuple>
-
-#include <libbroadcastring/broadcast_ring.h>
-#include <private/dvr/display_client.h>
-
-namespace android {
-namespace dvr {
-
-// The buffer usage type for mapped shared buffers.
-enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY };
-
-// Holds the memory for the mapped shared buffer. Unlocks and releases the
-// underlying IonBuffer in destructor.
-class CPUMappedBuffer {
- public:
-  // This constructor will create a display client and get the buffer from it.
-  CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode);
-
-  // If you already have the IonBuffer, use this. It will take ownership.
-  CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode);
-
-  // Use this if you do not want to take ownership.
-  CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode);
-
-  ~CPUMappedBuffer();
-
-  // Getters.
-  size_t Size() const { return size_; }
-  void* Address() const { return address_; }
-  bool IsMapped() const { return Address() != nullptr; }
-
-  // Attempt mapping this buffer to the CPU addressable space.
-  // This will create a display client and see if the buffer exists.
-  // If the buffer has not been setup yet, you will need to try again later.
-  void TryMapping();
-
- protected:
-  // The memory area if we managed to map it.
-  size_t size_ = 0;
-  void* address_ = nullptr;
-
-  // If we are polling the display client, the buffer key here.
-  DvrGlobalBufferKey buffer_key_;
-
-  // If we just own the IonBuffer outright, it's here.
-  std::unique_ptr<IonBuffer> owned_buffer_ = nullptr;
-
-  // The last time we connected to the display service.
-  int64_t last_display_service_connection_ns_ = 0;
-
-  // If we do not own the IonBuffer, it's here
-  IonBuffer* buffer_ = nullptr;
-
-  // The usage mode.
-  CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN;
-};
-
-// Represents a broadcast ring inside a mapped shared memory buffer.
-// If has the same set of constructors as CPUMappedBuffer.
-// The template argument is the concrete BroadcastRing class that this buffer
-// holds.
-template <class RingType>
-class CPUMappedBroadcastRing : public CPUMappedBuffer {
- public:
-  CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode)
-      : CPUMappedBuffer(key, mode) {}
-
-  CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode)
-      : CPUMappedBuffer(std::move(buffer), mode) {}
-
-  CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode)
-      : CPUMappedBuffer(buffer, mode) {}
-
-  // Helper function for publishing records in the ring.
-  void Publish(const typename RingType::Record& record) {
-    assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) ||
-           (usage_mode_ == CPUUsageMode::WRITE_RARELY));
-
-    auto ring = Ring();
-    if (ring) {
-      ring->Put(record);
-    }
-  }
-
-  // Helper function for getting records from the ring.
-  // Returns true if we were able to retrieve the latest.
-  bool GetNewest(typename RingType::Record* record) {
-    assert((usage_mode_ == CPUUsageMode::READ_OFTEN) ||
-           (usage_mode_ == CPUUsageMode::READ_RARELY));
-
-    auto ring = Ring();
-    if (ring) {
-      return ring->GetNewest(&sequence_, record);
-    }
-
-    return false;
-  }
-
-  // Try obtaining the ring. If the named buffer has not been created yet, it
-  // will return nullptr.
-  RingType* Ring() {
-    // No ring created yet?
-    if (ring_ == nullptr) {
-      // Not mapped the memory yet?
-      if (IsMapped() == false) {
-        TryMapping();
-      }
-
-      // If have the memory mapped, allocate the ring.
-      if (IsMapped()) {
-        switch (usage_mode_) {
-          case CPUUsageMode::READ_OFTEN:
-          case CPUUsageMode::READ_RARELY: {
-            RingType ring;
-            bool import_ok;
-            std::tie(ring, import_ok) = RingType::Import(address_, size_);
-            if (import_ok) {
-              ring_ = std::make_unique<RingType>(ring);
-            }
-          } break;
-          case CPUUsageMode::WRITE_OFTEN:
-          case CPUUsageMode::WRITE_RARELY:
-            ring_ =
-                std::make_unique<RingType>(RingType::Create(address_, size_));
-            break;
-        }
-      }
-    }
-
-    return ring_.get();
-  }
-
- protected:
-  std::unique_ptr<RingType> ring_ = nullptr;
-
-  uint32_t sequence_ = 0;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
deleted file mode 100644
index 152464a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
-#define ANDROID_DVR_VSYNC_SERVICE_H_
-
-#include <binder/IInterface.h>
-
-namespace android {
-namespace dvr {
-
-class IVsyncCallback : public IInterface {
- public:
-  DECLARE_META_INTERFACE(VsyncCallback)
-
-  enum {
-    ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
-  };
-
-  virtual status_t onVsync(int64_t vsync_timestamp) = 0;
-};
-
-class BnVsyncCallback : public BnInterface<IVsyncCallback> {
- public:
-  virtual status_t onTransact(uint32_t code, const Parcel& data,
-                              Parcel* reply, uint32_t flags = 0);
-};
-
-// Register a callback with IVsyncService to be notified of vsync events and
-// timestamps. There's also a shared memory vsync buffer defined in
-// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
-// memory buffer that make it preferable in certain situations:
-//
-// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
-// is always available as long as surface flinger is running.
-//
-// 2. IVsyncService will make a binder callback when a vsync event occurs. This
-// allows the client to not write code to implement periodic "get the latest
-// vsync" calls, which is necessary with the vsync shared memory buffer.
-//
-// 3. The IVsyncService provides the real vsync timestamp reported by hardware
-// composer, whereas the vsync shared memory buffer only has predicted vsync
-// times.
-class IVsyncService : public IInterface {
-public:
-  DECLARE_META_INTERFACE(VsyncService)
-
-  static const char* GetServiceName() { return "vrflinger_vsync"; }
-
-  enum {
-    REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
-    UNREGISTER_CALLBACK
-  };
-
-  virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
-  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
-};
-
-class BnVsyncService : public BnInterface<IVsyncService> {
- public:
-  virtual status_t onTransact(uint32_t code, const Parcel& data,
-                              Parcel* reply, uint32_t flags = 0);
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp
deleted file mode 100644
index 6ebf487..0000000
--- a/libs/vr/libdisplay/shared_buffer_helpers.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-// We will not poll the display service for buffers more frequently than this.
-constexpr size_t kDisplayServiceTriesPerSecond = 2;
-}  // namespace
-
-CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode)
-    : buffer_key_(key), usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer,
-                                 CPUUsageMode mode)
-    : owned_buffer_(std::move(buffer)),
-      buffer_(owned_buffer_.get()),
-      usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode)
-    : buffer_(buffer), usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::~CPUMappedBuffer() {
-  if (IsMapped()) {
-    buffer_->Unlock();
-  }
-}
-
-void CPUMappedBuffer::TryMapping() {
-  // Do we have an IonBuffer for this shared memory object?
-  if (buffer_ == nullptr) {
-    // Has it been too long since we last connected to the display service?
-    const auto current_time_ns = GetSystemClockNs();
-    if ((current_time_ns - last_display_service_connection_ns_) <
-        (1e9 / kDisplayServiceTriesPerSecond)) {
-      // Early exit.
-      return;
-    }
-    last_display_service_connection_ns_ = current_time_ns;
-
-    // Create a display client and get the buffer.
-    auto display_client = display::DisplayClient::Create();
-    if (display_client) {
-      auto get_result = display_client->GetGlobalBuffer(buffer_key_);
-      if (get_result.ok()) {
-        owned_buffer_ = get_result.take();
-        buffer_ = owned_buffer_.get();
-      } else {
-        // The buffer has not been created yet. This is OK, we will keep
-        // retrying.
-      }
-    } else {
-      ALOGE("Unable to create display client for shared buffer access");
-    }
-  }
-
-  if (buffer_) {
-    auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK &
-                 ~GRALLOC_USAGE_SW_WRITE_MASK;
-
-    // Figure out the usage bits.
-    switch (usage_mode_) {
-      case CPUUsageMode::READ_OFTEN:
-        usage |= GRALLOC_USAGE_SW_READ_OFTEN;
-        break;
-      case CPUUsageMode::READ_RARELY:
-        usage |= GRALLOC_USAGE_SW_READ_RARELY;
-        break;
-      case CPUUsageMode::WRITE_OFTEN:
-        usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
-        break;
-      case CPUUsageMode::WRITE_RARELY:
-        usage |= GRALLOC_USAGE_SW_WRITE_RARELY;
-        break;
-    }
-
-    int width = static_cast<int>(buffer_->width());
-    int height = 1;
-    const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_);
-
-    if (ret < 0 || !address_) {
-      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_);
-      buffer_->Unlock();
-    } else {
-      size_ = width;
-    }
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/system/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
deleted file mode 100644
index 04d4f30..0000000
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "include/private/dvr/vsync_service.h"
-
-#include <binder/Parcel.h>
-#include <log/log.h>
-
-namespace android {
-namespace dvr {
-
-status_t BnVsyncCallback::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-  switch (code) {
-    case ON_VSYNC: {
-      CHECK_INTERFACE(IVsyncCallback, data, reply);
-      int64_t vsync_timestamp = 0;
-      status_t result = data.readInt64(&vsync_timestamp);
-      if (result != OK) {
-        ALOGE("onVsync failed to readInt64: %d", result);
-        return result;
-      }
-      onVsync(vsync_timestamp);
-      return OK;
-    }
-    default: {
-      return BBinder::onTransact(code, data, reply, flags);
-    }
-  }
-}
-
-class BpVsyncCallback : public BpInterface<IVsyncCallback> {
-public:
-  explicit BpVsyncCallback(const sp<IBinder>& impl)
-      : BpInterface<IVsyncCallback>(impl) {}
-  virtual ~BpVsyncCallback() {}
-
-  virtual status_t onVsync(int64_t vsync_timestamp) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncCallback::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("onVsync failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeInt64(vsync_timestamp);
-    if (result != OK) {
-      ALOGE("onVsync failed to writeInt64: %d", result);
-      return result;
-    }
-    result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply,
-                                IBinder::FLAG_ONEWAY);
-    if (result != OK) {
-      ALOGE("onVsync failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
-
-
-status_t BnVsyncService::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-  switch (code) {
-    case REGISTER_CALLBACK: {
-      CHECK_INTERFACE(IVsyncService, data, reply);
-      sp<IBinder> callback;
-      status_t result = data.readStrongBinder(&callback);
-      if (result != OK) {
-        ALOGE("registerCallback failed to readStrongBinder: %d", result);
-        return result;
-      }
-      registerCallback(interface_cast<IVsyncCallback>(callback));
-      return OK;
-    }
-    case UNREGISTER_CALLBACK: {
-      CHECK_INTERFACE(IVsyncService, data, reply);
-      sp<IBinder> callback;
-      status_t result = data.readStrongBinder(&callback);
-      if (result != OK) {
-        ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
-        return result;
-      }
-      unregisterCallback(interface_cast<IVsyncCallback>(callback));
-      return OK;
-    }
-    default: {
-      return BBinder::onTransact(code, data, reply, flags);
-    }
-  }
-}
-
-class BpVsyncService : public BpInterface<IVsyncService> {
-public:
-  explicit BpVsyncService(const sp<IBinder>& impl)
-      : BpInterface<IVsyncService>(impl) {}
-  virtual ~BpVsyncService() {}
-
-  virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncService::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != OK) {
-      ALOGE("registerCallback failed to writeStrongBinder: %d", result);
-      return result;
-    }
-    result = remote()->transact(
-        BnVsyncService::REGISTER_CALLBACK, data, &reply);
-    if (result != OK) {
-      ALOGE("registerCallback failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-
-  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncService::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
-      return result;
-    }
-    result = remote()->transact(
-        BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
deleted file mode 100644
index 40a5099..0000000
--- a/libs/vr/libvrsensor/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "pose_client.cpp",
-    "latency_model.cpp",
-]
-
-includeFiles = [
-    "include",
-]
-
-staticLibraries = [
-    "libdisplay",
-    "libdvrcommon",
-    "libbroadcastring",
-]
-
-sharedLibraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libhardware",
-    "liblog",
-    "libutils",
-    "libui",
-    "libpdx_default_transport",
-]
-
-cc_library {
-    srcs: sourceFiles,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-macro-redefined",
-    ],
-    export_include_dirs: includeFiles,
-    static_libs: staticLibraries,
-    shared_libs: sharedLibraries,
-    header_libs: ["libdvr_headers"],
-    name: "libvrsensor",
-}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libvrsensor/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
deleted file mode 100644
index b663a67..0000000
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ /dev/null
@@ -1,176 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_H_
-#define ANDROID_DVR_POSE_CLIENT_H_
-
-#ifdef __ARM_NEON
-#include <arm_neon.h>
-#else
-#ifndef __FLOAT32X4T_86
-#define __FLOAT32X4T_86
-typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
-#endif
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <dvr/dvr_pose.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-// Returned by the async pose ring buffer access API.
-typedef struct DvrPoseRingBufferInfo {
-  // Read-only pointer to the pose ring buffer. The current pose is in this
-  // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
-  // frame's forecasted pose is at element
-  // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
-  // predicted for when 50% of the corresponding frame's pixel data is visible
-  // to the user.
-  // The last value returned by dvrPresent is the count for the next frame,
-  // which is the earliest that the application could display something if they
-  // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
-  volatile const DvrPoseAsync* buffer;
-  // Minimum number of accurate forecasted poses including the current frame's
-  // pose. This is the number of poses that are udpated by the pose service.
-  // If the application reads past this count, they will get a stale prediction
-  // from a previous frame. Guaranteed to be at least 2.
-  uint32_t min_future_count;
-  // Number of elements in buffer. At least 8 and greater than min_future_count.
-  // Guaranteed to be a power of two. The total size of the buffer in bytes is:
-  //   total_count * sizeof(DvrPoseAsync)
-  uint32_t total_count;
-} DvrPoseRingBufferInfo;
-
-typedef enum DvrPoseMode {
-  DVR_POSE_MODE_6DOF = 0,
-  DVR_POSE_MODE_3DOF,
-  DVR_POSE_MODE_MOCK_FROZEN,
-  DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
-  DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
-  DVR_POSE_MODE_MOCK_ROTATE_SLOW,
-  DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
-  DVR_POSE_MODE_MOCK_ROTATE_FAST,
-  DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
-  DVR_POSE_MODE_FLOAT,
-  DVR_POSE_MODE_MOCK_MOTION_SICKNESS,
-
-  // Always last.
-  DVR_POSE_MODE_COUNT,
-} DvrPoseMode;
-
-typedef enum DvrControllerId {
-  DVR_CONTROLLER_0 = 0,
-  DVR_CONTROLLER_1 = 1,
-} DvrControllerId;
-
-// Creates a new pose client.
-//
-// @return Pointer to the created pose client, nullptr on failure.
-DvrPoseClient* dvrPoseClientCreate();
-
-// Destroys a pose client.
-//
-// @param client Pointer to the pose client to be destroyed.
-void dvrPoseClientDestroy(DvrPoseClient* client);
-
-// Gets the pose for the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-//     Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
-                     DvrPoseAsync* out_pose);
-
-// Gets the current vsync count.
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client);
-
-// Gets the pose for the given controller at the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param controller_id The controller id.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-//     Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
-                               uint32_t vsync_count, DvrPoseAsync* out_pose);
-
-// Enables/disables logging for the controller fusion.
-//
-// @param client Pointer to the pose client.
-// @param enable True starts logging, False stops.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable);
-
-// DEPRECATED
-// Polls current pose state.
-//
-// @param client Pointer to the pose client.
-// @param state Struct to store polled state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state);
-
-// Freezes the pose to the provided state.
-//
-// Future poll operations will return this state until a different state is
-// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is
-// not frozen.
-//
-// @param client Pointer to the pose client.
-// @param frozen_state State pose to be frozen to.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state);
-
-// Sets the pose service mode.
-//
-// @param mode The requested pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode);
-
-// Gets the pose service mode.
-//
-// @param mode Return value for the current pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode);
-
-// Get access to the shared memory pose ring buffer.
-// A future pose at vsync <current> + <offset> is accessed at index:
-//   index = (<current> + <offset>) % out_buffer_size
-// Where <current> was the last value returned by dvrPresent and
-// <offset> is less than or equal to |out_min_future_count|.
-// |out_buffer| will be set to a pointer to the buffer.
-// |out_fd| will be set to the gralloc buffer file descriptor, which is
-//   required for binding this buffer for GPU use.
-// Returns 0 on success.
-int dvrPoseClientGetRingBuffer(DvrPoseClient* client,
-                               DvrPoseRingBufferInfo* out_info);
-
-// Sets enabled state for sensors pose processing.
-//
-// @param enabled Whether sensors are enabled or disabled.
-// @return Zero on success
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
-
-// Requests a burst of data samples from pose service. The data samples are
-// passed through a shared memory buffer obtained by calling
-// dvrPoseClientGetDataReader().
-//
-// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
-// @return Zero on success.
-int dvrPoseClientDataCapture(DvrPoseClient* client,
-                             const DvrPoseDataCaptureRequest* request);
-
-// Destroys the write buffer queue for the given |data_type|.
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
deleted file mode 100644
index bf0e687..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_LATENCY_MODEL_H_
-#define ANDROID_DVR_LATENCY_MODEL_H_
-
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-// This class models the latency from sensors. It will look at the first
-// window_size measurements and return their average after that.
-class LatencyModel {
- public:
-  explicit LatencyModel(size_t window_size);
-  ~LatencyModel() = default;
-
-  void AddLatency(int64_t latency_ns);
-  int64_t CurrentLatencyEstimate() const { return latency_; }
-
- private:
-  size_t window_size_;
-  int64_t latency_sum_ = 0;
-  size_t num_summed_ = 0;
-  int64_t latency_ = 0;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
deleted file mode 100644
index 7bf1cd4..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_POSE_IPC_H_
-#define ANDROID_DVR_POSE_IPC_H_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DVR_POSE_SERVICE_BASE "system/vr/pose"
-#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
-
-enum {
-  DVR_POSE_FREEZE = 0,
-  DVR_POSE_SET_MODE,
-  DVR_POSE_GET_MODE,
-  DVR_POSE_GET_CONTROLLER_RING_BUFFER,
-  DVR_POSE_LOG_CONTROLLER,
-  DVR_POSE_SENSORS_ENABLE,
-  DVR_POSE_GET_TANGO_READER,
-  DVR_POSE_DATA_CAPTURE,
-  DVR_POSE_TANGO_READER_DESTROY,
-};
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
deleted file mode 100644
index 39592bb..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-
-#include <private/dvr/buffer_hub_queue_client.h>
-
-using android::dvr::ConsumerQueue;
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-namespace android {
-namespace dvr {
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
-                                     ConsumerQueue **queue_out);
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
deleted file mode 100644
index d3a4521..0000000
--- a/libs/vr/libvrsensor/latency_model.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <private/dvr/latency_model.h>
-
-#include <cmath>
-
-namespace android {
-namespace dvr {
-
-LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
-
-void LatencyModel::AddLatency(int64_t latency_ns) {
-  // Not enough samples yet?
-  if (num_summed_ < window_size_) {
-    // Accumulate.
-    latency_sum_ += latency_ns;
-
-    // Have enough samples for latency estimate?
-    if (++num_summed_ == window_size_) {
-      latency_ = latency_sum_ / window_size_;
-    }
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
deleted file mode 100644
index 4ff6a09..0000000
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-#define LOG_TAG "PoseClient"
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/pose_client.h>
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <pdx/client.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/pose-ipc.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-using android::dvr::ConsumerQueue;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace {
-
-typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing;
-
-constexpr static int32_t MAX_CONTROLLERS = 2;
-}  // namespace
-
-// PoseClient is a remote interface to the pose service in sensord.
-class PoseClient : public pdx::ClientBase<PoseClient> {
- public:
-  ~PoseClient() override {}
-
-  // Casts C handle into an instance of this class.
-  static PoseClient* FromC(DvrPoseClient* client) {
-    return reinterpret_cast<PoseClient*>(client);
-  }
-
-  // Polls the pose service for the current state and stores it in *state.
-  // Returns zero on success, a negative error code otherwise.
-  int Poll(DvrPose* state) {
-    // Allocate the helper class to access the sensor pose buffer.
-    if (sensor_pose_buffer_ == nullptr) {
-      sensor_pose_buffer_ = std::make_unique<SensorPoseRing>(
-          DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY);
-    }
-
-    if (state) {
-      if (sensor_pose_buffer_->GetNewest(state)) {
-        return 0;
-      } else {
-        return -EAGAIN;
-      }
-    }
-
-    return -EINVAL;
-  }
-
-  int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      *out_pose =
-          vsync_buffer
-              ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
-      return 0;
-    } else {
-      return -EAGAIN;
-    }
-  }
-
-  uint32_t GetVsyncCount() {
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      return vsync_buffer->vsync_count;
-    }
-
-    return 0;
-  }
-
-  int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
-                        DvrPoseAsync* out_pose) {
-    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
-      return -EINVAL;
-    }
-    if (!controllers_[controller_id].mapped_pose_buffer) {
-      int ret = GetControllerRingBuffer(controller_id);
-      if (ret < 0)
-        return ret;
-    }
-    *out_pose =
-        controllers_[controller_id]
-            .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
-    return 0;
-  }
-
-  int LogController(bool enable) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
-                                         sizeof(enable), nullptr, 0);
-    ALOGE_IF(!status, "Pose LogController() failed because: %s",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Freezes the pose to the provided state. Future poll operations will return
-  // this state until a different state is frozen or SetMode() is called with a
-  // different mode.
-  // Returns zero on success, a negative error code otherwise.
-  int Freeze(const DvrPose& frozen_state) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
-                                         sizeof(frozen_state), nullptr, 0);
-    ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Sets the data mode for the pose service.
-  int SetMode(DvrPoseMode mode) {
-    Transaction trans{*this};
-    Status<int> status =
-        trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
-    ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Gets the data mode for the pose service.
-  int GetMode(DvrPoseMode* out_mode) {
-    int mode;
-    Transaction trans{*this};
-    Status<int> status =
-        trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
-    ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
-             status.GetErrorMessage().c_str());
-    if (status)
-      *out_mode = DvrPoseMode(mode);
-    return ReturnStatusOrError(status);
-  }
-
-  int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
-    // Get buffer.
-    Transaction trans{*this};
-    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
-        DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
-
-    if (!status) {
-      ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
-            status.GetErrorMessage().c_str());
-      *queue_out = nullptr;
-      return -status.error();
-    }
-
-    std::unique_ptr<ConsumerQueue> consumer_queue =
-        ConsumerQueue::Import(status.take());
-    *queue_out = consumer_queue.release();
-    return 0;
-  }
-
-  int DataCapture(const DvrPoseDataCaptureRequest* request) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
-                                         sizeof(*request), nullptr, 0);
-    ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  int DataReaderDestroy(uint64_t data_type) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
-                                         &data_type, sizeof(data_type), nullptr,
-                                         0);
-    ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Enables or disables all pose processing from sensors
-  int EnableSensors(bool enabled) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled,
-                                         sizeof(enabled), nullptr, 0);
-    ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
-    // First time mapping the buffer?
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      if (out_info) {
-        out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount;
-        out_info->total_count = DvrVsyncPoseBuffer::kSize;
-        out_info->buffer = vsync_buffer->vsync_poses;
-      }
-      return -EINVAL;
-    }
-
-    return -EAGAIN;
-  }
-
-  int GetControllerRingBuffer(int32_t controller_id) {
-    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
-      return -EINVAL;
-    }
-    ControllerClientState& client_state = controllers_[controller_id];
-    if (client_state.pose_buffer.get()) {
-      return 0;
-    }
-
-    Transaction trans{*this};
-    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
-        DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
-        sizeof(controller_id), nullptr, 0);
-    if (!status) {
-      return -status.error();
-    }
-
-    auto buffer = ConsumerBuffer::Import(status.take());
-    if (!buffer) {
-      ALOGE("Pose failed to import ring buffer");
-      return -EIO;
-    }
-    constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
-    void* addr = nullptr;
-    int ret = buffer->GetBlobReadWritePointer(size, &addr);
-    if (ret < 0 || !addr) {
-      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
-      return -EIO;
-    }
-    client_state.pose_buffer.swap(buffer);
-    client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
-    ALOGI(
-        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
-        controller_id, client_state.mapped_pose_buffer[0].position[0],
-        client_state.mapped_pose_buffer[0].position[1],
-        client_state.mapped_pose_buffer[0].position[2],
-        client_state.mapped_pose_buffer[0].orientation[0],
-        client_state.mapped_pose_buffer[0].orientation[1],
-        client_state.mapped_pose_buffer[0].orientation[2],
-        client_state.mapped_pose_buffer[0].orientation[3]);
-    return 0;
-  }
-
- private:
-  friend BASE;
-
-  // Set up a channel to the pose service.
-  PoseClient()
-      : BASE(pdx::default_transport::ClientChannelFactory::Create(
-            DVR_POSE_SERVICE_CLIENT)) {
-    // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
-    // block while waiting for the pose service to come back up.
-    EnableAutoReconnect(kInfiniteTimeout);
-  }
-
-  PoseClient(const PoseClient&) = delete;
-  PoseClient& operator=(const PoseClient&) = delete;
-
-  const DvrVsyncPoseBuffer* GetVsyncBuffer() {
-    if (mapped_vsync_pose_buffer_ == nullptr) {
-      if (vsync_pose_buffer_ == nullptr) {
-        // The constructor tries mapping it so we do not need TryMapping after.
-        vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>(
-            DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN);
-      } else if (vsync_pose_buffer_->IsMapped() == false) {
-        vsync_pose_buffer_->TryMapping();
-      }
-
-      if (vsync_pose_buffer_->IsMapped()) {
-        mapped_vsync_pose_buffer_ =
-            static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address());
-      }
-    }
-
-    return mapped_vsync_pose_buffer_;
-  }
-
-  // The vsync pose buffer if already mapped.
-  std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_;
-
-  // The direct sensor pose buffer.
-  std::unique_ptr<SensorPoseRing> sensor_pose_buffer_;
-
-  const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
-
-  struct ControllerClientState {
-    std::unique_ptr<ConsumerBuffer> pose_buffer;
-    const DvrPoseAsync* mapped_pose_buffer = nullptr;
-  };
-  ControllerClientState controllers_[MAX_CONTROLLERS];
-};
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
-                                     ConsumerQueue** queue_out) {
-  return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
-}
-
-}  // namespace dvr
-}  // namespace android
-
-using android::dvr::PoseClient;
-
-extern "C" {
-
-DvrPoseClient* dvrPoseClientCreate() {
-  auto* client = PoseClient::Create().release();
-  return reinterpret_cast<DvrPoseClient*>(client);
-}
-
-void dvrPoseClientDestroy(DvrPoseClient* client) {
-  delete PoseClient::FromC(client);
-}
-
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
-                     DvrPoseAsync* out_pose) {
-  return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
-}
-
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) {
-  return PoseClient::FromC(client)->GetVsyncCount();
-}
-
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
-                               uint32_t vsync_count, DvrPoseAsync* out_pose) {
-  return PoseClient::FromC(client)->GetControllerPose(controller_id,
-                                                      vsync_count, out_pose);
-}
-
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable) {
-  return PoseClient::FromC(client)->LogController(enable);
-}
-
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) {
-  return PoseClient::FromC(client)->Poll(state);
-}
-
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) {
-  return PoseClient::FromC(client)->Freeze(*frozen_state);
-}
-
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) {
-  return PoseClient::FromC(client)->SetMode(mode);
-}
-
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) {
-  return PoseClient::FromC(client)->GetMode(mode);
-}
-
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
-  return PoseClient::FromC(client)->EnableSensors(enabled);
-}
-
-int dvrPoseClientDataCapture(DvrPoseClient* client,
-                             const DvrPoseDataCaptureRequest* request) {
-  return PoseClient::FromC(client)->DataCapture(request);
-}
-
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
-  return PoseClient::FromC(client)->DataReaderDestroy(data_type);
-}
-
-}  // extern "C"
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 379f763..3d60a1d 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,11 +1,6 @@
-abdolrashidi@google.com
-cclao@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 ianelliott@google.com
 jessehall@google.com
-lfy@google.com
 lpy@google.com
-romanl@google.com
 vantablack@google.com
-yuxinhu@google.com
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 86c788d..aecfc6b 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -231,7 +231,7 @@
 
 int BlobCache::unflatten(void const* buffer, size_t size) {
     // All errors should result in the BlobCache being in an empty state.
-    mCacheEntries.clear();
+    clear();
 
     // Read the cache header
     if (size < sizeof(Header)) {
@@ -258,7 +258,7 @@
     size_t numEntries = header->mNumEntries;
     for (size_t i = 0; i < numEntries; i++) {
         if (byteOffset + sizeof(EntryHeader) > size) {
-            mCacheEntries.clear();
+            clear();
             ALOGE("unflatten: not enough room for cache entry headers");
             return -EINVAL;
         }
@@ -270,7 +270,7 @@
 
         size_t totalSize = align4(entrySize);
         if (byteOffset + totalSize > size) {
-            mCacheEntries.clear();
+            clear();
             ALOGE("unflatten: not enough room for cache entry headers");
             return -EINVAL;
         }
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index ff03d30..52078ff 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -117,7 +117,10 @@
 
     // clear flushes out all contents of the cache then the BlobCache, leaving
     // it in an empty state.
-    void clear() { mCacheEntries.clear(); }
+    void clear() {
+        mCacheEntries.clear();
+        mTotalSize = 0;
+    }
 
 protected:
     // mMaxTotalSize is the maximum size that all cache entries can occupy. This
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index ceea0fb..450c128 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -466,4 +466,31 @@
     ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
 }
 
+// Test for a divide by zero bug (b/239862516). Before the fix, unflatten() would not reset
+// mTotalSize when it encountered an error, which would trigger division by 0 in clean() in the
+// right conditions.
+TEST_F(BlobCacheFlattenTest, SetAfterFailedUnflatten) {
+    // isCleanable() must be true, so mTotalSize must be > mMaxTotalSize / 2 after unflattening
+    // after one entry is lost. To make this the case, MaxTotalSize is 30 and three 10 sized
+    // entries are used. One of those entries is lost, resulting in mTotalSize=20
+    const size_t kMaxKeySize = 10;
+    const size_t kMaxValueSize = 10;
+    const size_t kMaxTotalSize = 30;
+    mBC.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize));
+    mBC2.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize));
+    mBC->set("aaaaa", 5, "aaaaa", 5);
+    mBC->set("bbbbb", 5, "bbbbb", 5);
+    mBC->set("ccccc", 5, "ccccc", 5);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size - 10));
+    delete[] flat;
+
+    // This line will trigger clean() which caused a crash.
+    mBC2->set("dddddddddd", 10, "dddddddddd", 10);
+}
+
 } // namespace android
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index dd14bcf..6ea4007 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/properties.h>
 #include <android/dlext.h>
+#include <cutils/properties.h>
 #include <dirent.h>
 #include <dlfcn.h>
 #include <graphicsenv/GraphicsEnv.h>
@@ -236,29 +237,22 @@
             LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
                              android::GraphicsEnv::getInstance().getDriverPath().c_str());
         }
-        // Finally, try to load system driver.  If ANGLE is the system driver
-        // (i.e. we are forcing the legacy system driver instead of ANGLE), use
-        // the driver suffix that was passed down from above.
-        if (shouldForceLegacyDriver) {
-            std::string suffix = android::GraphicsEnv::getInstance().getLegacySuffix();
-            hnd = attempt_to_load_system_driver(cnx, suffix.c_str(), true);
-        } else {
-            // Start by searching for the library name appended by the system
-            // properties of the GLES userspace driver in both locations.
-            // i.e.:
-            //      libGLES_${prop}.so, or:
-            //      libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
-            for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
-                auto prop = base::GetProperty(key, "");
-                if (prop.empty()) {
-                    continue;
-                }
-                hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
-                if (hnd) {
-                    break;
-                } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
-                    failToLoadFromDriverSuffixProperty = true;
-                }
+        // Finally, try to load system driver.
+        // Start by searching for the library name appended by the system
+        // properties of the GLES userspace driver in both locations.
+        // i.e.:
+        //      libGLES_${prop}.so, or:
+        //      libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
+        for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
+            auto prop = base::GetProperty(key, "");
+            if (prop.empty()) {
+                continue;
+            }
+            hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
+            if (hnd) {
+                break;
+            } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
+                failToLoadFromDriverSuffixProperty = true;
             }
         }
     }
@@ -272,7 +266,10 @@
         hnd = attempt_to_load_system_driver(cnx, nullptr, true);
     }
 
-    if (!hnd && !failToLoadFromDriverSuffixProperty) {
+    if (!hnd && !failToLoadFromDriverSuffixProperty &&
+        property_get_int32("ro.vendor.api_level", 0) < __ANDROID_API_U__) {
+        // Still can't find the graphics drivers with the exact name. This time try to use wildcard
+        // matching if the device is launched before Android 14.
         hnd = attempt_to_load_system_driver(cnx, nullptr, false);
     }
 
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index d38f2ef..c0ead50 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -145,6 +145,7 @@
 
     if (!angleGetDisplayPlatform) {
         ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
+        dlclose(so);
         return false;
     }
 
@@ -155,6 +156,7 @@
     if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
                                     &platformMethods))) {
         ALOGE("ANGLEGetDisplayPlatform call failed!");
+        dlclose(so);
         return false;
     }
     if (platformMethods) {
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0b755aa..6593c1b 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -322,6 +322,16 @@
 
         mExtensionString = gBuiltinExtensionString;
 
+        // b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
+        // device's present timestamps are reliable (which may not be the case on emulators).
+        if (cnx->useAngle) {
+            if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
+                mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
+            }
+        } else {
+            mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
+        }
+
         hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
 
         // Note: CDD requires that devices supporting wide color and/or HDR color also support
@@ -361,6 +371,7 @@
                     findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
                     mExtensionString.append("EGL_EXT_image_gl_colorspace ");
                 }
+
                 if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
                     mExtensionString.append(ext + " ");
                 }
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 7619a50..5f441ee 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -84,7 +84,8 @@
 // Extensions implemented by the EGL wrapper.
 const char* const gBuiltinExtensionString =
         "EGL_ANDROID_front_buffer_auto_refresh "
-        "EGL_ANDROID_get_frame_timestamps "
+        // b/269060366 Conditionally enabled during display initialization:
+        //"EGL_ANDROID_get_frame_timestamps "
         "EGL_ANDROID_get_native_client_buffer "
         "EGL_ANDROID_presentation_time "
         "EGL_EXT_surface_CTA861_3_metadata "
@@ -2183,6 +2184,10 @@
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
+    if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
     SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2216,6 +2221,10 @@
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
+    if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
     SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2270,6 +2279,10 @@
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
+    if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
     SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2300,6 +2313,10 @@
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
+    if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
     SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2385,6 +2402,10 @@
         return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
+    if (!dp->haveExtension("EGL_ANDROID_get_frame_timestamps")) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
     SurfaceRef _s(dp, surface);
     if (!_s.get()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 178bc29..bf6189d 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -34,6 +34,10 @@
     BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
     BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
     BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
+    BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY
+    BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
+    BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
+    BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH
 };
 
 struct BatteryProperties {
diff --git a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
index 8a90a12..2d7072d 100644
--- a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
+++ b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
@@ -1,7 +1,5 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-
-#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#ifndef AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
+#define AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
 
 #ifdef __cplusplus
 extern "C" {
@@ -15,6 +13,8 @@
     BATTERY_STATUS_FULL = 5,
 };
 
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryHealth.aidl
 enum {
     BATTERY_HEALTH_UNKNOWN = 1,
     BATTERY_HEALTH_GOOD = 2,
@@ -23,10 +23,23 @@
     BATTERY_HEALTH_OVER_VOLTAGE = 5,
     BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
     BATTERY_HEALTH_COLD = 7,
+    BATTERY_HEALTH_FAIR = 8,
+    BATTERY_HEALTH_NOT_AVAILABLE = 11,
+    BATTERY_HEALTH_INCONSISTENT = 12,
+};
+
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryChargingState.aidl
+enum {
+    BATTERY_STATUS_NORMAL = 1,
+    BATTERY_STATUS_TOO_COLD = 2,
+    BATTERY_STATUS_TOO_HOT = 3,
+    BATTERY_STATUS_LONG_LIFE = 4,
+    BATTERY_STATUS_ADAPTIVE = 5,
 };
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif  // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index 0ff65bf..07c681f 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -4,3 +4,4 @@
 lfy@google.com
 paulthomson@google.com
 pbaiget@google.com
+kocdemir@google.com
diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..6bcc5e8
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+    name: "gpu_service_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    static_libs: [
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "paulthomson@google.com",
+            "pbaiget@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+    include_dirs: ["frameworks/native/services/gpuservice/"],
+    srcs: ["GpuServiceFuzzer.cpp"],
+    shared_libs: [
+        "libgpuservice",
+    ],
+}
diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
new file mode 100644
index 0000000..c2574a3
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+
+#include "GpuService.h"
+
+using ::android::fuzzService;
+using ::android::GpuService;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    sp<GpuService> gpuService = new GpuService();
+    fuzzService(gpuService, FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 86f6c7f..51642f9 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -24,9 +24,6 @@
 cc_test {
     name: "gpuservice_unittest",
     test_suites: ["device-tests"],
-    sanitize: {
-        address: true,
-    },
     srcs: [
         "GpuMemTest.cpp",
         "GpuMemTracerTest.cpp",
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
index 83a40e7..b6362e2 100644
--- a/services/gpuservice/vts/Android.bp
+++ b/services/gpuservice/vts/Android.bp
@@ -21,7 +21,6 @@
     srcs: ["src/**/*.java"],
     libs: [
         "tradefed",
-        "vts-core-tradefed-harness",
     ],
     test_suites: [
         "general-tests",
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index e789052..a63de1c 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 653544
+kocdemir@google.com
 paulthomson@google.com
 pbaiget@google.com
 lfy@google.com
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 2a3924b..dce327e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -196,9 +196,20 @@
         }
         coords += StringPrintf("{%" PRIu32 ": ", i);
         coords +=
-                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id,
+                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
                              pointerCoords[i].getX(), pointerCoords[i].getY(),
                              pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        const int32_t toolType = pointerProperties[i].toolType;
+        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+        }
+        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        if (major != 0 || minor != 0) {
+            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+                                   orientation);
+        }
         coords += "}";
     }
     return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f57ff33..ec41025 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -29,8 +29,40 @@
 
 using android::base::StringPrintf;
 
+/**
+ * This type is declared here to ensure consistency between the instantiated type (used in the
+ * constructor via std::make_unique) and the cast-to type (used in PalmRejector::dump() with
+ * static_cast). Due to the lack of rtti support, dynamic_cast is not available, so this can't be
+ * checked at runtime to avoid undefined behaviour.
+ */
+using PalmFilterImplementation = ::ui::NeuralStylusPalmDetectionFilter;
+
 namespace android {
 
+/**
+ * Log detailed debug messages about each inbound motion event notification to the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerInboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_INBOUND_MOTION =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log detailed debug messages about each outbound motion event processed by the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerOutboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_OUTBOUND_MOTION =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log the data sent to the model and received back from the model.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerModel DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_MODEL =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO);
+
 // Category (=namespace) name for the input settings that are applied at boot time
 static const char* INPUT_NATIVE_BOOT = "input_native_boot";
 /**
@@ -45,13 +77,11 @@
 }
 
 static bool isFromTouchscreen(int32_t source) {
-    return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) &&
-            !isFromSource(source, AINPUT_SOURCE_STYLUS);
+    return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
 }
 
 static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
-    return ::base::TimeTicks::UnixEpoch() +
-            ::base::Milliseconds(static_cast<float>(ns2ms(eventTime)));
+    return ::base::TimeTicks::UnixEpoch() + ::base::TimeDelta::FromNanosecondsD(eventTime);
 }
 
 /**
@@ -61,39 +91,22 @@
 static bool isPalmRejectionEnabled() {
     std::string value = toLower(
             server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT,
-                                                                 PALM_REJECTION_ENABLED, "false"));
-    if (value == "true" || value == "1") {
+                                                                 PALM_REJECTION_ENABLED, "0"));
+    if (value == "1") {
         return true;
     }
     return false;
 }
 
-static int getLinuxToolType(int32_t toolType) {
-    switch (toolType) {
-        case AMOTION_EVENT_TOOL_TYPE_FINGER:
-            return MT_TOOL_FINGER;
-        case AMOTION_EVENT_TOOL_TYPE_STYLUS:
-            return MT_TOOL_PEN;
-        case AMOTION_EVENT_TOOL_TYPE_PALM:
-            return MT_TOOL_PALM;
+static int getLinuxToolCode(int toolType) {
+    if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+        return BTN_TOOL_PEN;
     }
-    ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType);
-    return MT_TOOL_FINGER;
-}
-
-static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) {
-    std::string out;
-    out += StringPrintf("max_x = %.2f\n", info.max_x);
-    out += StringPrintf("max_y = %.2f\n", info.max_y);
-    out += StringPrintf("x_res = %.2f\n", info.x_res);
-    out += StringPrintf("y_res = %.2f\n", info.y_res);
-    out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res);
-    out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res);
-    out += StringPrintf("minor_radius_supported = %s\n",
-                        info.minor_radius_supported ? "true" : "false");
-    out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res);
-    out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res);
-    return out;
+    if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+        return BTN_TOOL_FINGER;
+    }
+    ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+    return BTN_TOOL_FINGER;
 }
 
 static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -128,32 +141,6 @@
     return AMOTION_EVENT_ACTION_MOVE;
 }
 
-std::string toString(const ::ui::InProgressTouchEvdev& touch) {
-    return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu,"
-                        " pressure=%.1f, major=%i, minor=%i, "
-                        "tool_type=%i, altered=%s, was_touching=%s, touching=%s",
-                        touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure,
-                        touch.major, touch.minor, touch.tool_type, toString(touch.altered),
-                        toString(touch.was_touching), toString(touch.touching));
-}
-
-/**
- * Remove the data for the provided pointers from the args. The pointers are identified by their
- * pointerId, not by the index inside the array.
- * Return the new NotifyMotionArgs struct that has the remaining pointers.
- * The only fields that may be different in the returned args from the provided args are:
- *     - action
- *     - pointerCount
- *     - pointerProperties
- *     - pointerCoords
- * Action might change because it contains a pointer index. If another pointer is removed, the
- * active pointer index would be shifted.
- * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer
- * id is the acting pointer id.
- *
- * @param args the args from which the pointers should be removed
- * @param pointerIds the pointer ids of the pointers that should be removed
- */
 NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
                                   const std::set<int32_t>& pointerIds) {
     const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
@@ -199,6 +186,26 @@
     return newArgs;
 }
 
+/**
+ * Remove stylus pointers from the provided NotifyMotionArgs.
+ *
+ * Return NotifyMotionArgs where the stylus pointers have been removed.
+ * If this results in removal of the active pointer, then return nullopt.
+ */
+static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
+    std::set<int32_t> stylusPointerIds;
+    for (uint32_t i = 0; i < args.pointerCount; i++) {
+        if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+            stylusPointerIds.insert(args.pointerProperties[i].id);
+        }
+    }
+    NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
+    if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+        return std::nullopt;
+    }
+    return withoutStylusPointers;
+}
+
 std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
         const InputDeviceInfo& deviceInfo) {
     if (!isFromTouchscreen(deviceInfo.getSources())) {
@@ -326,6 +333,7 @@
 }
 
 void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
+    ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args->dump().c_str());
     { // acquire lock
         std::scoped_lock lock(mLock);
         const std::vector<NotifyMotionArgs> processedArgs =
@@ -339,17 +347,22 @@
     mQueuedListener.flush();
 }
 
+void UnwantedInteractionBlocker::enqueueOutboundMotionLocked(const NotifyMotionArgs& args) {
+    ALOGD_IF(DEBUG_OUTBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
+    mQueuedListener.notifyMotion(&args);
+}
+
 void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) {
     auto it = mPalmRejectors.find(args->deviceId);
     const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
     if (!sendToPalmRejector) {
-        mQueuedListener.notifyMotion(args);
+        enqueueOutboundMotionLocked(*args);
         return;
     }
 
     std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(*args);
     for (const NotifyMotionArgs& loopArgs : processedArgs) {
-        mQueuedListener.notifyMotion(&loopArgs);
+        enqueueOutboundMotionLocked(loopArgs);
     }
 }
 
@@ -428,9 +441,10 @@
     dump += "UnwantedInteractionBlocker:\n";
     dump += "  mPreferStylusOverTouchBlocker:\n";
     dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), "    ");
-    dump += StringPrintf("  mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
+    dump += StringPrintf("  mEnablePalmRejection: %s\n",
+                         std::to_string(mEnablePalmRejection).c_str());
     dump += StringPrintf("  isPalmRejectionEnabled (flag value): %s\n",
-                         toString(isPalmRejectionEnabled()));
+                         std::to_string(isPalmRejectionEnabled()).c_str());
     dump += mPalmRejectors.empty() ? "  mPalmRejectors: None\n" : "  mPalmRejectors:\n";
     for (const auto& [deviceId, palmRejector] : mPalmRejectors) {
         dump += StringPrintf("    deviceId = %" PRId32 ":\n", deviceId);
@@ -512,6 +526,15 @@
     return out;
 }
 
+class AndroidPalmRejectionModel : public ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel {
+public:
+    AndroidPalmRejectionModel()
+          : ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel(/*default version*/ "",
+                                                                     std::vector<float>()) {
+        config_.resample_period = ::ui::kResamplePeriod;
+    }
+};
+
 PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info,
                            std::unique_ptr<::ui::PalmDetectionFilter> filter)
       : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()),
@@ -523,11 +546,9 @@
         return;
     }
     std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model =
-            std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>(
-                    std::vector<float>());
-    mPalmDetectionFilter =
-            std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model),
-                                                                    mSharedPalmState.get());
+            std::make_unique<AndroidPalmRejectionModel>();
+    mPalmDetectionFilter = std::make_unique<PalmFilterImplementation>(mDeviceInfo, std::move(model),
+                                                                      mSharedPalmState.get());
 }
 
 std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
@@ -541,7 +562,7 @@
         touches.emplace_back(::ui::InProgressTouchEvdev());
         touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
         touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-        touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType);
+        // The field 'tool_type' is not used for palm rejection
 
         // Whether there is new information for the touch.
         touches.back().altered = true;
@@ -588,15 +609,57 @@
 
         // The fields 'radius_x' and 'radius_x' are not used for palm rejection
         touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-        touches.back().tool_code = BTN_TOOL_FINGER;
+        touches.back().tool_code = getLinuxToolCode(args.pointerProperties[i].toolType);
         // The field 'orientation' is not used for palm rejection
         // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection
-        touches.back().reported_tool_type = ::ui::EventPointerType::kTouch;
+        // The field 'reported_tool_type' is not used for palm rejection
         touches.back().stylus_button = false;
     }
     return touches;
 }
 
+std::set<int32_t> PalmRejector::detectPalmPointers(const NotifyMotionArgs& args) {
+    std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
+    std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
+
+    // Store the slot state before we call getTouches and update it. This way, we can find
+    // the slots that have been removed due to the incoming event.
+    SlotState oldSlotState = mSlotState;
+    mSlotState.update(args);
+
+    std::vector<::ui::InProgressTouchEvdev> touches =
+            getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
+    ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
+
+    if (DEBUG_MODEL) {
+        std::stringstream touchesStream;
+        for (const ::ui::InProgressTouchEvdev& touch : touches) {
+            touchesStream << touch.tracking_id << " : " << touch << "\n";
+        }
+        ALOGD("Filter: touches = %s", touchesStream.str().c_str());
+    }
+
+    mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
+
+    ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s",
+             slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str());
+
+    // Now that we know which slots should be suppressed, let's convert those to pointer id's.
+    std::set<int32_t> newSuppressedIds;
+    for (size_t i = 0; i < args.pointerCount; i++) {
+        const int32_t pointerId = args.pointerProperties[i].id;
+        std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
+        if (!slot) {
+            slot = mSlotState.getSlotForPointerId(pointerId);
+            LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
+        }
+        if (slotsToSuppress.test(*slot)) {
+            newSuppressedIds.insert(pointerId);
+        }
+    }
+    return newSuppressedIds;
+}
+
 std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
     if (mPalmDetectionFilter == nullptr) {
         return {args};
@@ -614,32 +677,17 @@
     if (args.action == AMOTION_EVENT_ACTION_DOWN) {
         mSuppressedPointerIds.clear();
     }
-    std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
-    std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
 
-    // Store the slot state before we call getTouches and update it. This way, we can find
-    // the slots that have been removed due to the incoming event.
-    SlotState oldSlotState = mSlotState;
-    mSlotState.update(args);
-    std::vector<::ui::InProgressTouchEvdev> touches =
-            getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
-    ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
-
-    mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
-
-    // Now that we know which slots should be suppressed, let's convert those to pointer id's.
     std::set<int32_t> oldSuppressedIds;
     std::swap(oldSuppressedIds, mSuppressedPointerIds);
-    for (size_t i = 0; i < args.pointerCount; i++) {
-        const int32_t pointerId = args.pointerProperties[i].id;
-        std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
-        if (!slot) {
-            slot = mSlotState.getSlotForPointerId(pointerId);
-            LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
-        }
-        if (slotsToSuppress.test(*slot)) {
-            mSuppressedPointerIds.insert(pointerId);
-        }
+
+    std::optional<NotifyMotionArgs> touchOnlyArgs = removeStylusPointerIds(args);
+    if (touchOnlyArgs) {
+        mSuppressedPointerIds = detectPalmPointers(*touchOnlyArgs);
+    } else {
+        // This is a stylus-only event.
+        // We can skip this event and just keep the suppressed pointer ids the same as before.
+        mSuppressedPointerIds = oldSuppressedIds;
     }
 
     std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers =
@@ -648,29 +696,40 @@
         LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str());
     }
 
-    if (mSuppressedPointerIds != oldSuppressedIds) {
-        if (argsWithoutUnwantedPointers.size() != 1 ||
-            argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) {
-            ALOGI("Palm detected, removing pointer ids %s from %s",
-                  dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str());
-        }
+    // Only log if new pointers are getting rejected. That means mSuppressedPointerIds is not a
+    // subset of oldSuppressedIds.
+    if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(),
+                       mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) {
+        ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s",
+              dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
+              args.dump().c_str());
     }
 
     return argsWithoutUnwantedPointers;
 }
 
-const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
+const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const {
     return mDeviceInfo;
 }
 
 std::string PalmRejector::dump() const {
     std::string out;
     out += "mDeviceInfo:\n";
-    out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), "  ");
+    std::stringstream deviceInfo;
+    deviceInfo << mDeviceInfo << ", touch_major_res=" << mDeviceInfo.touch_major_res
+               << ", touch_minor_res=" << mDeviceInfo.touch_minor_res << "\n";
+    out += addLinePrefix(deviceInfo.str(), "  ");
     out += "mSlotState:\n";
     out += addLinePrefix(mSlotState.dump(), "  ");
     out += "mSuppressedPointerIds: ";
     out += dumpSet(mSuppressedPointerIds) + "\n";
+    std::stringstream state;
+    state << *mSharedPalmState;
+    out += "mSharedPalmState: " + state.str() + "\n";
+    std::stringstream filter;
+    filter << static_cast<const PalmFilterImplementation&>(*mPalmDetectionFilter);
+    out += "mPalmDetectionFilter:\n";
+    out += addLinePrefix(filter.str(), "  ") + "\n";
     return out;
 }
 
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index a433764..5d0dde8 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -43,6 +43,25 @@
 
 static constexpr int32_t ACTION_UNKNOWN = -1;
 
+/**
+ * Remove the data for the provided pointers from the args. The pointers are identified by their
+ * pointerId, not by the index inside the array.
+ * Return the new NotifyMotionArgs struct that has the remaining pointers.
+ * The only fields that may be different in the returned args from the provided args are:
+ *     - action
+ *     - pointerCount
+ *     - pointerProperties
+ *     - pointerCoords
+ * Action might change because it contains a pointer index. If another pointer is removed, the
+ * active pointer index would be shifted.
+ *
+ * If the active pointer id is removed (for example, for events like
+ * POINTER_UP or POINTER_DOWN), then the action is set to ACTION_UNKNOWN. It is up to the caller
+ * to set the action appropriately after the call.
+ *
+ * @param args the args from which the pointers should be removed
+ * @param pointerIds the pointer ids of the pointers that should be removed
+ */
 NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
                                   const std::set<int32_t>& pointerIds);
 
@@ -101,6 +120,9 @@
     std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors GUARDED_BY(mLock);
     // TODO(b/210159205): delete this when simultaneous stylus and touch is supported
     void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+
+    // Call this function for outbound events so that they can be logged when logging is enabled.
+    void enqueueOutboundMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
 };
 
 class SlotState {
@@ -144,13 +166,21 @@
     std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args);
 
     // Get the device info of this device, for comparison purposes
-    const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo();
+    const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo() const;
     std::string dump() const;
 
 private:
     PalmRejector(const PalmRejector&) = delete;
     PalmRejector& operator=(const PalmRejector&) = delete;
 
+    /**
+     * Update the slot state and send this event to the palm rejection model for palm detection.
+     * Return the pointer ids that should be suppressed.
+     *
+     * This function is not const because it has side-effects. It will update the slot state using
+     * the incoming args! Also, it will call Filter(..), which has side-effects.
+     */
+    std::set<int32_t> detectPalmPointers(const NotifyMotionArgs& args);
     std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState;
     AndroidPalmFilterDeviceInfo mDeviceInfo;
     std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter;
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 4f8995f..9a2aea6 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -75,11 +75,8 @@
 
 /**
  * Log debug messages about touch occlusion
- * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
- * restart)
  */
-const bool DEBUG_TOUCH_OCCLUSION =
-        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
 
 /**
  * Log debug messages about the app switch latency optimization.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f634ce7..83ada8e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -27,6 +27,7 @@
 #include <ftl/enum.h>
 #include <gui/SurfaceComposerClient.h>
 #include <input/InputDevice.h>
+#include <openssl/mem.h>
 #include <powermanager/PowerManager.h>
 #include <unistd.h>
 #include <utils/Trace.h>
@@ -107,6 +108,8 @@
 constexpr int LOGTAG_INPUT_FOCUS = 62001;
 constexpr int LOGTAG_INPUT_CANCEL = 62003;
 
+const ui::Transform kIdentityTransform;
+
 inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
@@ -459,8 +462,8 @@
 }
 
 // Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
-                          bool isStylus) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
+                          bool isStylus, const ui::Transform& displayTransform) {
     const auto inputConfig = windowInfo.inputConfig;
     if (windowInfo.displayId != displayId ||
         inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -470,7 +473,17 @@
     if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
         return false;
     }
-    if (!windowInfo.touchableRegionContainsPoint(x, y)) {
+
+    // Window Manager works in the logical display coordinate space. When it specifies bounds for a
+    // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
+    // the window. Points on the right and bottom edges should not be inside the window, so we need
+    // to be careful about performing a hit test when the display is rotated, since the "right" and
+    // "bottom" of the window will be different in the display (un-rotated) space compared to in the
+    // logical display in which WM determined the bounds. Perform the hit test in the logical
+    // display space to ensure these edges are considered correctly in all orientations.
+    const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
+    const auto p = displayTransform.transform(x, y);
+    if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
         return false;
     }
     return true;
@@ -928,10 +941,8 @@
     // the touch into the other window.
     if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
         int32_t displayId = motionEntry.displayId;
-        int32_t x = static_cast<int32_t>(
-                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-        int32_t y = static_cast<int32_t>(
-                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        const float x = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+        const float y = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
 
         const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
         sp<WindowInfoHandle> touchedWindowHandle =
@@ -1058,8 +1069,8 @@
     }
 }
 
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                int32_t y, TouchState* touchState,
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
+                                                                TouchState* touchState,
                                                                 bool isStylus,
                                                                 bool addOutsideTargets,
                                                                 bool ignoreDragWindow) {
@@ -1074,7 +1085,8 @@
         }
 
         const WindowInfo& info = *windowHandle->getInfo();
-        if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+        if (!info.isSpy() &&
+            windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
             return windowHandle;
         }
 
@@ -1088,14 +1100,14 @@
 }
 
 std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
-        int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+        int32_t displayId, float x, float y, bool isStylus) const {
     // Traverse windows from front to back and gather the touched spy windows.
     std::vector<sp<WindowInfoHandle>> spyWindows;
     const auto& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         const WindowInfo& info = *windowHandle->getInfo();
 
-        if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+        if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
             continue;
         }
         if (!info.isSpy()) {
@@ -2057,16 +2069,16 @@
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
 
-        int32_t x;
-        int32_t y;
+        float x;
+        float y;
         const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
         // Always dispatch mouse events to cursor position.
         if (isFromMouse) {
-            x = int32_t(entry.xCursorPosition);
-            y = int32_t(entry.yCursorPosition);
+            x = entry.xCursorPosition;
+            y = entry.yCursorPosition;
         } else {
-            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
-            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+            x = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X);
+            y = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y);
         }
         const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2075,8 +2087,7 @@
 
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
-            ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
-                  displayId);
+            ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
             // Try to assign the pointer to the first foreground window we find, if there is one.
             newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
         }
@@ -2123,7 +2134,8 @@
         }
 
         if (newTouchedWindows.empty()) {
-            ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
+            ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
+                  "%d.",
                   x, y, displayId);
             injectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
@@ -2157,7 +2169,8 @@
                         computeTouchOcclusionInfoLocked(windowHandle, x, y);
                 if (!isTouchTrustedLocked(occlusionInfo)) {
                     if (DEBUG_TOUCH_OCCLUSION) {
-                        ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+                        ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x,
+                              y);
                         for (const auto& log : occlusionInfo.debugInfo) {
                             ALOGD("%s", log.c_str());
                         }
@@ -2195,12 +2208,32 @@
 
             // Update the temporary touch state.
             BitSet32 pointerIds;
-            if (isSplit) {
-                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-                pointerIds.markBit(pointerId);
-            }
-
+            pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
             tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
+
+            // If this is the 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.
+            // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
+            // engine only supports touch events.  We would need to add a mechanism similar
+            // to View.onGenericMotionEvent to enable wallpapers to handle these events.
+            if (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+                maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+                if ((targetFlags & InputTarget::FLAG_FOREGROUND) &&
+                    windowHandle->getInfo()->inputConfig.test(
+                            gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
+                    sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
+                    if (wallpaper != nullptr) {
+                        int32_t wallpaperFlags = InputTarget::FLAG_WINDOW_IS_OBSCURED |
+                                InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
+                                InputTarget::FLAG_DISPATCH_AS_IS;
+                        if (isSplit) {
+                            wallpaperFlags |= InputTarget::FLAG_SPLIT;
+                        }
+                        tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds);
+                    }
+                }
+            }
         }
     } else {
         /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -2221,8 +2254,8 @@
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             tempTouchState.isSlippery()) {
-            const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-            const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+            const float x = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+            const float y = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
 
             const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
             sp<WindowInfoHandle> oldTouchedWindowHandle =
@@ -2275,10 +2308,26 @@
                 }
 
                 BitSet32 pointerIds;
-                if (isSplit) {
-                    pointerIds.markBit(entry.pointerProperties[0].id);
-                }
+                pointerIds.markBit(entry.pointerProperties[0].id);
                 tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+
+                // Check if the wallpaper window should deliver the corresponding event.
+                slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
+                                   tempTouchState, pointerIds);
+            }
+        }
+
+        // Update the pointerIds for non-splittable when it received pointer down.
+        if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+            // If no split, we suppose all touched windows should receive pointer down.
+            const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+            for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+                TouchedWindow& touchedWindow = tempTouchState.windows[i];
+                // Ignore drag window for it should just track one pointer.
+                if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+                    continue;
+                }
+                touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
             }
         }
     }
@@ -2370,37 +2419,6 @@
         }
     }
 
-    // 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.
-    // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
-    // engine only supports touch events.  We would need to add a mechanism similar
-    // to View.onGenericMotionEvent to enable wallpapers to handle these events.
-    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-        sp<WindowInfoHandle> foregroundWindowHandle =
-                tempTouchState.getFirstForegroundWindowHandle();
-        if (foregroundWindowHandle &&
-            foregroundWindowHandle->getInfo()->inputConfig.test(
-                    WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
-            const std::vector<sp<WindowInfoHandle>>& windowHandles =
-                    getWindowHandlesLocked(displayId);
-            for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
-                const WindowInfo* info = windowHandle->getInfo();
-                if (info->displayId == displayId &&
-                    windowHandle->getInfo()->inputConfig.test(
-                            WindowInfo::InputConfig::IS_WALLPAPER)) {
-                    tempTouchState
-                            .addOrUpdateWindow(windowHandle,
-                                               InputTarget::FLAG_WINDOW_IS_OBSCURED |
-                                                       InputTarget::
-                                                               FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
-                                                       InputTarget::FLAG_DISPATCH_AS_IS,
-                                               BitSet32(0));
-                }
-            }
-        }
-    }
-
     // Success!  Output targets.
     injectionResult = InputEventInjectionResult::SUCCEEDED;
 
@@ -2453,21 +2471,17 @@
             }
         } 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;
+            int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+            uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
 
-                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;
+            for (size_t i = 0; i < tempTouchState.windows.size();) {
+                TouchedWindow& touchedWindow = tempTouchState.windows[i];
+                touchedWindow.pointerIds.clearBit(pointerId);
+                if (touchedWindow.pointerIds.isEmpty()) {
+                    tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+                    continue;
                 }
+                i += 1;
             }
         }
 
@@ -2534,8 +2548,8 @@
     }
 
     const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
-    const int32_t x = entry.pointerCoords[pointerIndex].getX();
-    const int32_t y = entry.pointerCoords[pointerIndex].getY();
+    const float x = entry.pointerCoords[pointerIndex].getX();
+    const float y = entry.pointerCoords[pointerIndex].getY();
 
     switch (maskedAction) {
         case AMOTION_EVENT_ACTION_MOVE: {
@@ -3683,7 +3697,7 @@
 }
 
 void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
-        const sp<Connection>& connection) {
+        const sp<Connection>& connection, int32_t targetFlags) {
     if (connection->status == Connection::Status::BROKEN) {
         return;
     }
@@ -3711,7 +3725,7 @@
         target.globalScaleFactor = windowInfo->globalScaleFactor;
     }
     target.inputChannel = connection->inputChannel;
-    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    target.flags = targetFlags;
 
     const bool wasEmpty = connection->outboundQueue.empty();
 
@@ -3746,6 +3760,16 @@
     }
 }
 
+void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
+        const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) {
+    if (windowHandle != nullptr) {
+        sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken());
+        if (wallpaperConnection != nullptr) {
+            synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options);
+        }
+    }
+}
+
 std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
         const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
     ALOG_ASSERT(pointerIds.value != 0);
@@ -4431,7 +4455,7 @@
     if (calculatedHmac == INVALID_HMAC) {
         return nullptr;
     }
-    if (calculatedHmac != event.getHmac()) {
+    if (0 != CRYPTO_memcmp(calculatedHmac.data(), event.getHmac().data(), calculatedHmac.size())) {
         return nullptr;
     }
     return result;
@@ -4479,6 +4503,14 @@
     if (it == mDisplayInfos.end()) return;
     const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
 
+    if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
+        entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+        const vec2 cursor =
+                MotionEvent::calculateTransformedXY(entry.source, transformToDisplay,
+                                                    {entry.xCursorPosition, entry.yCursorPosition});
+        entry.xCursorPosition = cursor.x;
+        entry.yCursorPosition = cursor.y;
+    }
     for (uint32_t i = 0; i < entry.pointerCount; i++) {
         entry.pointerCoords[i] =
                 MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
@@ -4567,6 +4599,12 @@
     return getWindowHandleLocked(focusedToken, displayId);
 }
 
+ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+    auto displayInfoIt = mDisplayInfos.find(displayId);
+    return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
+                                                : kIdentityTransform;
+}
+
 bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
     sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
     const bool noInputChannel =
@@ -4759,14 +4797,7 @@
                         touchedWindow.windowHandle->getInfo()->inputConfig.test(
                                 gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                         sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
-                        if (wallpaper != nullptr) {
-                            sp<Connection> wallpaperConnection =
-                                    getConnectionLocked(wallpaper->getToken());
-                            if (wallpaperConnection != nullptr) {
-                                synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
-                                                                               options);
-                            }
-                        }
+                        synthesizeCancelationEventsForWindowLocked(wallpaper, options);
                     }
                 }
                 state.windows.erase(state.windows.begin() + i);
@@ -5079,6 +5110,7 @@
         // Erase old window.
         int32_t oldTargetFlags = touchedWindow->targetFlags;
         BitSet32 pointerIds = touchedWindow->pointerIds;
+        sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
         state->removeWindowByToken(fromToken);
 
         // Add new window.
@@ -5091,14 +5123,13 @@
 
         // Store the dragging window.
         if (isDragDrop) {
-            if (pointerIds.count() > 1) {
-                ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the"
-                      " window.");
+            if (pointerIds.count() != 1) {
+                ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
+                      " pointer on the window.");
                 return false;
             }
-            // If the window didn't not support split or the source is mouse, the pointerIds count
-            // would be 0, so we have to track the pointer 0.
-            const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit();
+            // Track the pointer id for drag window and generate the drag state.
+            const int32_t id = pointerIds.firstMarkedBit();
             mDragState = std::make_unique<DragState>(toWindowHandle, id);
         }
 
@@ -5111,7 +5142,10 @@
                     options(CancelationOptions::CANCEL_POINTER_EVENTS,
                             "transferring touch focus from this window to another window");
             synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
-            synthesizePointerDownEventsForConnectionLocked(toConnection);
+            synthesizePointerDownEventsForConnectionLocked(toConnection, newTargetFlags);
+            // Check if the wallpaper window should deliver the corresponding event.
+            transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
+                                   *state, pointerIds);
         }
 
         if (DEBUG_FOCUS) {
@@ -5847,9 +5881,9 @@
             StringPrintf("%s does not have a focused window", application->getName().c_str());
     updateLastAnrStateLocked(*application, reason);
 
-    auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+    auto command = [this, app = std::move(application)]() REQUIRES(mLock) {
         scoped_unlock unlock(mLock);
-        mPolicy->notifyNoFocusedWindowAnr(application);
+        mPolicy->notifyNoFocusedWindowAnr(app);
     };
     postCommandLocked(std::move(command));
 }
@@ -5910,9 +5944,9 @@
 void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
                                                           std::optional<int32_t> pid,
                                                           std::string reason) {
-    auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+    auto command = [this, token, pid, r = std::move(reason)]() REQUIRES(mLock) {
         scoped_unlock unlock(mLock);
-        mPolicy->notifyWindowUnresponsive(token, pid, reason);
+        mPolicy->notifyWindowUnresponsive(token, pid, r);
     };
     postCommandLocked(std::move(command));
 }
@@ -6334,6 +6368,13 @@
 
     { // acquire lock
         std::scoped_lock _l(mLock);
+
+        // Ensure that we have an entry created for all existing displays so that if a displayId has
+        // no windows, we can tell that the windows were removed from the display.
+        for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+            handlesPerDisplay[displayId];
+        }
+
         mDisplayInfos.clear();
         for (const auto& displayInfo : displayInfos) {
             mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
@@ -6389,4 +6430,97 @@
     mMonitorDispatchingTimeout = timeout;
 }
 
+void InputDispatcher::slipWallpaperTouch(int32_t targetFlags,
+                                         const sp<WindowInfoHandle>& oldWindowHandle,
+                                         const sp<WindowInfoHandle>& newWindowHandle,
+                                         TouchState& state, const BitSet32& pointerIds) {
+    const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
+            gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+    const bool newHasWallpaper = (targetFlags & InputTarget::FLAG_FOREGROUND) &&
+            newWindowHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+    const sp<WindowInfoHandle> oldWallpaper =
+            oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+    const sp<WindowInfoHandle> newWallpaper =
+            newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr;
+    if (oldWallpaper == newWallpaper) {
+        return;
+    }
+
+    if (oldWallpaper != nullptr) {
+        state.addOrUpdateWindow(oldWallpaper, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+                                BitSet32(0));
+    }
+
+    if (newWallpaper != nullptr) {
+        state.addOrUpdateWindow(newWallpaper,
+                                InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER |
+                                        InputTarget::FLAG_WINDOW_IS_OBSCURED |
+                                        InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+                                pointerIds);
+    }
+}
+
+void InputDispatcher::transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags,
+                                             const sp<WindowInfoHandle> fromWindowHandle,
+                                             const sp<WindowInfoHandle> toWindowHandle,
+                                             TouchState& state, const BitSet32& pointerIds) {
+    const bool oldHasWallpaper = (oldTargetFlags & InputTarget::FLAG_FOREGROUND) &&
+            fromWindowHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+    const bool newHasWallpaper = (newTargetFlags & InputTarget::FLAG_FOREGROUND) &&
+            toWindowHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+
+    const sp<WindowInfoHandle> oldWallpaper =
+            oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+    const sp<WindowInfoHandle> newWallpaper =
+            newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr;
+    if (oldWallpaper == newWallpaper) {
+        return;
+    }
+
+    if (oldWallpaper != nullptr) {
+        CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+                                   "transferring touch focus to another window");
+        state.removeWindowByToken(oldWallpaper->getToken());
+        synthesizeCancelationEventsForWindowLocked(oldWallpaper, options);
+    }
+
+    if (newWallpaper != nullptr) {
+        int32_t wallpaperFlags =
+                oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+        wallpaperFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED |
+                InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+        state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds);
+        sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken());
+        if (wallpaperConnection != nullptr) {
+            sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken());
+            toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
+            synthesizePointerDownEventsForConnectionLocked(wallpaperConnection, wallpaperFlags);
+        }
+    }
+}
+
+sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow(
+        const sp<WindowInfoHandle>& windowHandle) const {
+    const std::vector<sp<WindowInfoHandle>>& windowHandles =
+            getWindowHandlesLocked(windowHandle->getInfo()->displayId);
+    bool foundWindow = false;
+    for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
+        if (!foundWindow && otherHandle != windowHandle) {
+            continue;
+        }
+        if (windowHandle == otherHandle) {
+            foundWindow = true;
+            continue;
+        }
+
+        if (otherHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::IS_WALLPAPER)) {
+            return otherHandle;
+        }
+    }
+    return nullptr;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ed89ed0..7769b9e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -240,11 +240,11 @@
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
     sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
-            int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+            int32_t displayId, float x, float y, TouchState* touchState, bool isStylus = false,
             bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
 
     std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
-            int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
+            int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
 
     sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
             REQUIRES(mLock);
@@ -370,6 +370,7 @@
             int32_t displayId) const REQUIRES(mLock);
     sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
             const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
+    ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
 
     // Same function as above, but faster. Since displayId is provided, this avoids the need
     // to loop through all displays.
@@ -621,8 +622,12 @@
                                                         const CancelationOptions& options)
             REQUIRES(mLock);
 
-    void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
-            REQUIRES(mLock);
+    void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection,
+                                                        int32_t targetFlags) REQUIRES(mLock);
+
+    void synthesizeCancelationEventsForWindowLocked(
+            const sp<android::gui::WindowInfoHandle>& windowHandle,
+            const CancelationOptions& options) REQUIRES(mLock);
 
     // Splitting motion events across windows.
     std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
@@ -684,6 +689,18 @@
     bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
 
     sp<InputReporterInterface> mReporter;
+
+    void slipWallpaperTouch(int32_t targetFlags,
+                            const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+                            const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+                            TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+    void transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags,
+                                const sp<android::gui::WindowInfoHandle> fromWindowHandle,
+                                const sp<android::gui::WindowInfoHandle> toWindowHandle,
+                                TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+
+    sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
+            const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index d39113b..2df97d9 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -43,8 +43,8 @@
 }
 
 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.
+    // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
+    // valid pointer property from the input event.
     if (newPointerIds.isEmpty()) {
         setDefaultPointerTransform(transform);
         return;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4c31ec3..6783022 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -29,7 +29,7 @@
 struct TouchedWindow {
     sp<gui::WindowInfoHandle> windowHandle;
     int32_t targetFlags;
-    BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
+    BitSet32 pointerIds;
 };
 
 } // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 575b3d7..fff1b03 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -26,11 +26,10 @@
 
 namespace android {
 
-
 /*
  * Input dispatcher policy interface.
  *
- * The input reader policy is used by the input reader to interact with the Window Manager
+ * The input dispatcher policy is used by the input dispatcher to interact with the Window Manager
  * and other system components.
  *
  * The actual implementation is partially supported by callbacks into the DVM
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 20baa42..39beed3 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -461,6 +461,11 @@
         }
     }
 
+    std::vector<int32_t> usageCodes = keyMap.keyLayoutMap->findUsageCodesForKey(keycode);
+    if (usageCodes.size() > 0 && mscBitmask.test(MSC_SCAN)) {
+        return true;
+    }
+
     return false;
 }
 
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0d73e07..3f26aaa 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -110,7 +110,8 @@
         dump += "<none>\n";
     }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
-    dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+    dump += StringPrintf(INDENT2 "Sources: %s\n",
+                         inputEventSourceToString(deviceInfo.getSources()).c_str());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
     dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
 
@@ -128,10 +129,10 @@
                 snprintf(name, sizeof(name), "%d", range.axis);
             }
             dump += StringPrintf(INDENT3
-                                 "%s: source=0x%08x, "
+                                 "%s: source=%s, "
                                  "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
-                                 name, range.source, range.min, range.max, range.flat, range.fuzz,
-                                 range.resolution);
+                                 name, inputEventSourceToString(range.source).c_str(), range.min,
+                                 range.max, range.flat, range.fuzz, range.resolution);
         }
     }
 
@@ -231,6 +232,10 @@
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    if (mController != nullptr && mController->getEventHubId() == eventHubId) {
+        // Delete mController, since the corresponding eventhub device is going away
+        mController = nullptr;
+    }
     mDevices.erase(eventHubId);
 }
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e9a82b4..905b348 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -216,9 +216,9 @@
               "(ignored non-input device)",
               device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
     } else {
-        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s",
               device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
-              device->getSources());
+              inputEventSourceToString(device->getSources()).c_str());
     }
 
     mDevices.emplace(eventHubId, device);
@@ -270,9 +270,10 @@
               device->getId(), eventHubId, device->getName().c_str(),
               device->getDescriptor().c_str());
     } else {
-        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x",
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=%s",
               device->getId(), eventHubId, device->getName().c_str(),
-              device->getDescriptor().c_str(), device->getSources());
+              device->getDescriptor().c_str(),
+              inputEventSourceToString(device->getSources()).c_str());
     }
 
     device->removeEventHubDevice(eventHubId);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 76a87bb..f4f3ae9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -25,6 +25,8 @@
 #include "PointerControllerInterface.h"
 #include "TouchCursorInputMapperCommon.h"
 
+#include "input/PrintTools.h"
+
 namespace android {
 
 // The default velocity control parameters that has no effect.
@@ -80,7 +82,7 @@
 void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    if (mParameters.mode == Parameters::MODE_POINTER) {
+    if (mParameters.mode == Parameters::Mode::POINTER) {
         float minX, minY, maxX, maxY;
         if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
             info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
@@ -117,6 +119,7 @@
                          toString(mCursorScrollAccumulator.haveRelativeHWheel()));
     dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
     dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+    dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
     dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
     dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
     dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -135,12 +138,12 @@
 
         // Configure device mode.
         switch (mParameters.mode) {
-            case Parameters::MODE_POINTER_RELATIVE:
+            case Parameters::Mode::POINTER_RELATIVE:
                 // Should not happen during first time configuration.
                 ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
-                mParameters.mode = Parameters::MODE_POINTER;
+                mParameters.mode = Parameters::Mode::POINTER;
                 [[fallthrough]];
-            case Parameters::MODE_POINTER:
+            case Parameters::Mode::POINTER:
                 mSource = AINPUT_SOURCE_MOUSE;
                 mXPrecision = 1.0f;
                 mYPrecision = 1.0f;
@@ -148,7 +151,7 @@
                 mYScale = 1.0f;
                 mPointerController = getContext()->getPointerController(getDeviceId());
                 break;
-            case Parameters::MODE_NAVIGATION:
+            case Parameters::Mode::NAVIGATION:
                 mSource = AINPUT_SOURCE_TRACKBALL;
                 mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
                 mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
@@ -161,12 +164,13 @@
         mHWheelScale = 1.0f;
     }
 
-    const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) ||
-            (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
+            ((!changes && config->pointerCaptureRequest.enable) ||
+             (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
     if (configurePointerCapture) {
         if (config->pointerCaptureRequest.enable) {
-            if (mParameters.mode == Parameters::MODE_POINTER) {
-                mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+            if (mParameters.mode == Parameters::Mode::POINTER) {
+                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);
@@ -174,8 +178,8 @@
                 ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
             }
         } else {
-            if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
-                mParameters.mode = Parameters::MODE_POINTER;
+            if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+                mParameters.mode = Parameters::Mode::POINTER;
                 mSource = AINPUT_SOURCE_MOUSE;
             } else {
                 ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
@@ -190,8 +194,8 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
         configurePointerCapture) {
-        if (config->pointerCaptureRequest.enable) {
-            // Disable any acceleration or scaling when Pointer Capture is enabled.
+        if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+            // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
             mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
             mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
             mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
@@ -202,21 +206,36 @@
         }
     }
 
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
+        configurePointerCapture) {
+        const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+        mDisplayId = ADISPLAY_ID_NONE;
+        if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+            // This InputDevice is associated with a viewport.
+            // Only generate events for the associated display.
+            const bool mismatchedPointerDisplay =
+                    isPointer && (viewport->displayId != mPointerController->getDisplayId());
+            mDisplayId = mismatchedPointerDisplay ? std::nullopt
+                                                  : std::make_optional(viewport->displayId);
+        } else if (isPointer) {
+            // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+            mDisplayId = mPointerController->getDisplayId();
+        }
+
         mOrientation = DISPLAY_ORIENTATION_0;
         const bool isOrientedDevice =
                 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-
         // InputReader works in the un-rotated display coordinate space, so we don't need to do
         // anything if the device is already orientation-aware. If the device is not
         // orientation-aware, then we need to apply the inverse rotation of the display so that
         // when the display rotation is applied later as a part of the per-window transform, we
-        // get the expected screen coordinates.
-        if (!isOrientedDevice) {
-            std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::INTERNAL);
-            if (internalViewport) {
-                mOrientation = getInverseRotation(internalViewport->orientation);
+        // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
+        // rotations and report values directly from the input device.
+        if (!isOrientedDevice && mDisplayId &&
+            mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+            if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+                mOrientation = getInverseRotation(viewport->orientation);
             }
         }
 
@@ -225,12 +244,12 @@
 }
 
 void CursorInputMapper::configureParameters() {
-    mParameters.mode = Parameters::MODE_POINTER;
+    mParameters.mode = Parameters::Mode::POINTER;
     String8 cursorModeString;
     if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
                                                              cursorModeString)) {
         if (cursorModeString == "navigation") {
-            mParameters.mode = Parameters::MODE_NAVIGATION;
+            mParameters.mode = Parameters::Mode::NAVIGATION;
         } else if (cursorModeString != "pointer" && cursorModeString != "default") {
             ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
         }
@@ -241,7 +260,7 @@
                                                          mParameters.orientationAware);
 
     mParameters.hasAssociatedDisplay = false;
-    if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+    if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
         mParameters.hasAssociatedDisplay = true;
     }
 }
@@ -250,21 +269,7 @@
     dump += INDENT3 "Parameters:\n";
     dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
                          toString(mParameters.hasAssociatedDisplay));
-
-    switch (mParameters.mode) {
-        case Parameters::MODE_POINTER:
-            dump += INDENT4 "Mode: pointer\n";
-            break;
-        case Parameters::MODE_POINTER_RELATIVE:
-            dump += INDENT4 "Mode: relative pointer\n";
-            break;
-        case Parameters::MODE_NAVIGATION:
-            dump += INDENT4 "Mode: navigation\n";
-            break;
-        default:
-            ALOG_ASSERT(false);
-    }
-
+    dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
 }
 
@@ -294,6 +299,11 @@
 }
 
 void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    if (!mDisplayId) {
+        // Ignore events when there is no target display configured.
+        return;
+    }
+
     int32_t lastButtonState = mButtonState;
     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
     mButtonState = currentButtonState;
@@ -339,7 +349,6 @@
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-    int32_t displayId = ADISPLAY_ID_NONE;
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -363,7 +372,6 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = mPointerController->getDisplayId();
     } else {
         // Pointer capture and navigation modes
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -385,7 +393,7 @@
 
     // Synthesize key down from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, displayId, policyFlags, lastButtonState, currentButtonState);
+                         mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -406,7 +414,7 @@
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
                 NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
-                                             getDeviceId(), mSource, displayId, policyFlags,
+                                             getDeviceId(), mSource, *mDisplayId, policyFlags,
                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                              metaState, buttonState, MotionClassification::NONE,
                                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -418,7 +426,7 @@
         }
 
         NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
+                              *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
                               currentButtonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                               mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
@@ -431,7 +439,7 @@
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
                 NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                           mSource, displayId, policyFlags,
+                                           mSource, *mDisplayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -447,7 +455,7 @@
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
             NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                       mSource, displayId, policyFlags,
+                                       mSource, *mDisplayId, policyFlags,
                                        AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                                        currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -462,7 +470,7 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
             NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                        mSource, displayId, policyFlags,
+                                        mSource, *mDisplayId, policyFlags,
                                         AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                                         currentButtonState, MotionClassification::NONE,
                                         AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -474,7 +482,7 @@
 
     // Synthesize key up from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         displayId, policyFlags, lastButtonState, currentButtonState);
+                         *mDisplayId, policyFlags, lastButtonState, currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
     mCursorScrollAccumulator.finishSync();
@@ -489,16 +497,7 @@
 }
 
 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
-    if (mParameters.hasAssociatedDisplay) {
-        if (mParameters.mode == Parameters::MODE_POINTER) {
-            return std::make_optional(mPointerController->getDisplayId());
-        } else {
-            // If the device is orientationAware and not a mouse,
-            // it expects to dispatch events to any display
-            return std::make_optional(ADISPLAY_ID_NONE);
-        }
-    }
-    return std::nullopt;
+    return mDisplayId;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index c84c6c4..60b3dd9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -74,10 +74,17 @@
 
     // Immutable configuration parameters.
     struct Parameters {
-        enum Mode {
-            MODE_POINTER,
-            MODE_POINTER_RELATIVE,
-            MODE_NAVIGATION,
+        enum class Mode {
+            // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen,
+            // reporting absolute screen locations using SOURCE_MOUSE.
+            POINTER,
+            // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer
+            // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE.
+            POINTER_RELATIVE,
+            // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL.
+            NAVIGATION,
+
+            ftl_last = NAVIGATION,
         };
 
         Mode mode;
@@ -104,6 +111,10 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // The display that events generated by this mapper should target. This can be set to
+    // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+    // std::nullopt), all events will be ignored.
+    std::optional<int32_t> mDisplayId;
     int32_t mOrientation;
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 41a8426..cc323ad 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -48,11 +48,8 @@
 
     delete[] mSlots;
     mSlots = new Slot[slotCount];
-}
 
-void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
-    // Unfortunately there is no way to read the initial contents of the slots.
-    // So when we reset the accumulator, we must assume they are all zeroes.
+    mCurrentSlot = -1;
     if (mUsingSlotsProtocol) {
         // Query the driver for the current slot index and use it as the initial slot
         // before we start reading events from the device.  It is possible that the
@@ -64,24 +61,22 @@
         // This can cause the touch point to "jump", but at least there will be
         // no stuck touches.
         int32_t initialSlot;
-        status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
-        if (status) {
-            ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
-            initialSlot = -1;
+        if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+            status == OK) {
+            mCurrentSlot = initialSlot;
+        } else {
+            ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
         }
-        clearSlots(initialSlot);
-    } else {
-        clearSlots(-1);
     }
 }
 
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+void MultiTouchMotionAccumulator::resetSlots() {
     if (mSlots) {
         for (size_t i = 0; i < mSlotCount; i++) {
             mSlots[i].clear();
         }
     }
-    mCurrentSlot = initialSlot;
+    mCurrentSlot = -1;
 }
 
 void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
@@ -169,7 +164,7 @@
 
 void MultiTouchMotionAccumulator::finishSync() {
     if (!mUsingSlotsProtocol) {
-        clearSlots(-1);
+        resetSlots();
     }
 }
 
@@ -230,10 +225,12 @@
 MultiTouchInputMapper::~MultiTouchInputMapper() {}
 
 void MultiTouchInputMapper::reset(nsecs_t when) {
-    mMultiTouchMotionAccumulator.reset(getDeviceContext());
-
-    mPointerIdBits.clear();
-
+    // The evdev multi-touch protocol does not allow userspace applications to query the initial or
+    // current state of the pointers at any time. This means if we clear our accumulated state when
+    // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
+    // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
+    // rebuilding the state from scratch, we work around this kernel API limitation by never
+    // fully clearing any state specific to the multi-touch protocol.
     TouchInputMapper::reset(when);
 }
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index b7c3457..e7d9350 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -71,7 +71,6 @@
     ~MultiTouchMotionAccumulator();
 
     void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
-    void reset(InputDeviceContext& deviceContext);
     void process(const RawEvent* rawEvent);
     void finishSync();
     bool hasStylus() const;
@@ -86,7 +85,7 @@
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
 
-    void clearSlots(int32_t initialSlot);
+    void resetSlots();
     void warnIfNotInUse(const RawEvent& event, const Slot& slot);
 };
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 00fc288..ed3b0ec 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -44,6 +44,8 @@
 
 // --- Static Definitions ---
 
+static const DisplayViewport kUninitializedViewport;
+
 template <typename T>
 inline static void swap(T& a, T& b) {
     T temp = a;
@@ -390,10 +392,10 @@
     }
 
     if (changes && resetNeeded) {
-        // If device was reset, cancel touch event and update touch spot state.
-        cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
-        mCurrentCookedState.clear();
-        updateTouchSpots();
+        // If the device needs to be reset, cancel any ongoing gestures and reset the state.
+        cancelTouch(when, when);
+        reset(when);
+
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
         NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
@@ -881,7 +883,7 @@
 }
 
 void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
-    DeviceMode oldDeviceMode = mDeviceMode;
+    const DeviceMode oldDeviceMode = mDeviceMode;
 
     resolveExternalStylusPresence();
 
@@ -910,43 +912,37 @@
         mDeviceMode = DeviceMode::UNSCALED;
     }
 
-    // Ensure we have valid X and Y axes.
+    const std::optional<DisplayViewport> newViewportOpt = findViewport();
+
+    // Ensure the device is valid and can be used.
     if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
         ALOGW("Touch device '%s' did not report support for X or Y axis!  "
               "The device will be inoperable.",
               getDeviceName().c_str());
         mDeviceMode = DeviceMode::DISABLED;
-        return;
-    }
-
-    // Get associated display dimensions.
-    std::optional<DisplayViewport> newViewport = findViewport();
-    if (!newViewport) {
+    } else if (!newViewportOpt) {
         ALOGI("Touch device '%s' could not query the properties of its associated "
               "display.  The device will be inoperable until the display size "
               "becomes available.",
               getDeviceName().c_str());
         mDeviceMode = DeviceMode::DISABLED;
-        return;
-    }
-
-    if (!newViewport->isActive) {
+    } else if (!newViewportOpt->isActive) {
         ALOGI("Disabling %s (device %i) because the associated viewport is not active",
               getDeviceName().c_str(), getDeviceId());
         mDeviceMode = DeviceMode::DISABLED;
-        return;
     }
 
     // Raw width and height in the natural orientation.
     const int32_t rawWidth = mRawPointerAxes.getRawWidth();
     const int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
-    const bool viewportChanged = mViewport != *newViewport;
+    const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
+    const bool viewportChanged = mViewport != newViewport;
     bool skipViewportUpdate = false;
     if (viewportChanged) {
-        const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
-        const bool viewportDisplayIdChanged = mViewport.displayId != newViewport->displayId;
-        mViewport = *newViewport;
+        const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
+        const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId;
+        mViewport = newViewport;
 
         if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
             // Convert rotated viewport to the natural orientation.
@@ -1105,10 +1101,6 @@
             // of the diagonal axis of the touch pad.  Touches that are wider than this are
             // translated into freeform gestures.
             mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
-
-            // Abort current pointer usages because the state has changed.
-            const nsecs_t readTime = when; // synthetic event
-            abortPointerUsage(when, readTime, 0 /*policyFlags*/);
         }
 
         // Inform the dispatcher about the changes.
@@ -1481,6 +1473,10 @@
 }
 
 void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    if (mDeviceMode == DeviceMode::DISABLED) {
+        // Only save the last pending state when the device is disabled.
+        mRawStatesPending.clear();
+    }
     // Push a new state.
     mRawStatesPending.emplace_back();
 
@@ -3530,6 +3526,8 @@
 
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
                                              bool down, bool hovering) {
+    LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER,
+                        "%s cannot be used when the device is not in POINTER mode.", __func__);
     int32_t metaState = getContext()->getGlobalMetaState();
 
     if (down || hovering) {
@@ -3652,6 +3650,10 @@
     if (down || hovering) {
         mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
         mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+        mPointerSimple.displayId = displayId;
+        mPointerSimple.source = mSource;
+        mPointerSimple.lastCursorX = xCursorPosition;
+        mPointerSimple.lastCursorY = yCursorPosition;
     } else {
         mPointerSimple.reset();
     }
@@ -3661,7 +3663,23 @@
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
-    dispatchPointerSimple(when, readTime, policyFlags, false, false);
+    if (mPointerSimple.down || mPointerSimple.hovering) {
+        int32_t metaState = getContext()->getGlobalMetaState();
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+                              mPointerSimple.source, mPointerSimple.displayId, policyFlags,
+                              AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+                              metaState, mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
+                              mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener().notifyMotion(&args);
+        if (mPointerController != nullptr) {
+            mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+        }
+    }
+    mPointerSimple.reset();
 }
 
 void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index c948f56..2937bf8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -327,6 +327,8 @@
         int32_t rawVScroll;
         int32_t rawHScroll;
 
+        explicit inline RawState() { clear(); }
+
         void copyFrom(const RawState& other) {
             when = other.when;
             readTime = other.readTime;
@@ -712,6 +714,12 @@
         // Time the pointer last went down.
         nsecs_t downTime;
 
+        // Values reported for the last pointer event.
+        uint32_t source;
+        int32_t displayId;
+        float lastCursorX;
+        float lastCursorY;
+
         void reset() {
             currentCoords.clear();
             currentProperties.clear();
@@ -720,6 +728,10 @@
             down = false;
             hovering = false;
             downTime = 0;
+            source = 0;
+            displayId = ADISPLAY_ID_NONE;
+            lastCursorX = 0.f;
+            lastCursorY = 0.f;
         }
     } mPointerSimple;
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 76a7c19..75cd9da 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -60,6 +60,7 @@
     },
     static_libs: [
         "libc++fs",
+        "libgmock",
     ],
     require_root: true,
     test_suites: ["device-tests"],
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bb8e566..b23b88a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -58,6 +58,8 @@
         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 static constexpr int32_t POINTER_2_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_0_UP =
+        AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 static constexpr int32_t POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
@@ -73,6 +75,9 @@
 
 static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
 
+static constexpr int expectedWallpaperFlags =
+        AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
 struct PointF {
     float x;
     float y;
@@ -128,10 +133,10 @@
             const auto& motionEvent = static_cast<const MotionEvent&>(event);
             EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
             EXPECT_EQ(motionEvent.getAction(), args.action);
-            EXPECT_EQ(motionEvent.getX(0), point.x);
-            EXPECT_EQ(motionEvent.getY(0), point.y);
-            EXPECT_EQ(motionEvent.getRawX(0), point.x);
-            EXPECT_EQ(motionEvent.getRawY(0), point.y);
+            EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+            EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+            EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+            EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
         });
     }
 
@@ -1670,8 +1675,6 @@
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
     wallpaperWindow->setIsWallpaper(true);
-    constexpr int expectedWallpaperFlags =
-            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1714,8 +1717,6 @@
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
     wallpaperWindow->setIsWallpaper(true);
-    constexpr int expectedWallpaperFlags =
-            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1745,24 +1746,27 @@
     foregroundWindow->consumeMotionCancel();
 }
 
+class ShouldSplitTouchFixture : public InputDispatcherTest,
+                                public ::testing::WithParamInterface<bool> {};
+INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture,
+                         ::testing::Values(true, false));
 /**
  * A single window that receives touch (on top), and a wallpaper window underneath it.
  * The top window gets a multitouch gesture.
  * Ensure that wallpaper gets the same gesture.
  */
-TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window =
-            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
-    window->setDupTouchToWallpaper(true);
+    sp<FakeWindowHandle> foregroundWindow =
+            new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+    foregroundWindow->setDupTouchToWallpaper(true);
+    foregroundWindow->setPreventSplitting(GetParam());
 
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
     wallpaperWindow->setIsWallpaper(true);
-    constexpr int expectedWallpaperFlags =
-            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
 
     // Touch down on top window
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1771,7 +1775,7 @@
             << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
     // Both top window and its wallpaper should receive the touch down
-    window->consumeMotionDown();
+    foregroundWindow->consumeMotionDown();
     wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 
     // Second finger down on the top window
@@ -1790,11 +1794,34 @@
                                 InputEventInjectionSync::WAIT_FOR_RESULT))
             << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
-    window->consumeMotionPointerDown(1 /* pointerIndex */);
+    foregroundWindow->consumeMotionPointerDown(1 /* pointerIndex */);
     wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
                                               expectedWallpaperFlags);
-    window->assertNoEvents();
-    wallpaperWindow->assertNoEvents();
+
+    const MotionEvent secondFingerUpEvent =
+            MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(100)
+                                     .y(100))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(150)
+                                     .y(150))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    foregroundWindow->consumeMotionPointerUp(0);
+    wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 }
 
 /**
@@ -1821,8 +1848,6 @@
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
     wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
     wallpaperWindow->setIsWallpaper(true);
-    constexpr int expectedWallpaperFlags =
-            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
     mDispatcher->setInputWindows(
             {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
@@ -1888,6 +1913,51 @@
 }
 
 /**
+ * Two windows: a window on the left with dup touch to wallpaper and window on the right without it.
+ * The touch slips to the right window. so left window and wallpaper should receive ACTION_CANCEL
+ * The right window should receive ACTION_DOWN.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> leftWindow =
+            new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    leftWindow->setFrame(Rect(0, 0, 200, 200));
+    leftWindow->setDupTouchToWallpaper(true);
+    leftWindow->setSlippery(true);
+
+    sp<FakeWindowHandle> rightWindow =
+            new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+    sp<FakeWindowHandle> wallpaperWindow =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setIsWallpaper(true);
+
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+    // Touch down on left window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both foreground window and its wallpaper should receive the touch down
+    leftWindow->consumeMotionDown();
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Move to right window, the left window should receive cancel.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {201, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    leftWindow->consumeMotionCancel();
+    rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
  * On the display, have a single window, and also an area where there's no window.
  * First pointer touches the "no window" area of the screen. Second pointer touches the window.
  * Make sure that the window receives the second pointer, and first pointer is simply ignored.
@@ -2346,6 +2416,72 @@
     thirdWindow->consumeMotionDown();
 }
 
+TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocusable(true);
+
+    mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+    setFocusedWindow(window);
+
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyDown);
+    mDispatcher->notifyKey(&keyUp);
+
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+    // All windows are removed from the display. Ensure that we can no longer dispatch to it.
+    mDispatcher->onWindowInfosChanged({}, {});
+
+    window->consumeFocusEvent(false);
+
+    mDispatcher->notifyKey(&keyDown);
+    mDispatcher->notifyKey(&keyUp);
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+                                                             "Fake Window", ADISPLAY_ID_DEFAULT);
+    // Ensure window is non-split and have some transform.
+    window->setPreventSplitting(true);
+    window->setWindowOffset(20, 40);
+    mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(-30)
+                                     .y(-50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    const MotionEvent* event = window->consumeMotion();
+    EXPECT_EQ(POINTER_1_DOWN, event->getAction());
+    EXPECT_EQ(70, event->getX(0));  // 50 + 20
+    EXPECT_EQ(90, event->getY(0));  // 50 + 40
+    EXPECT_EQ(-10, event->getX(1)); // -30 + 20
+    EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+}
+
 /**
  * Ensure the correct coordinate spaces are used by InputDispatcher.
  *
@@ -2357,8 +2493,7 @@
 public:
     void SetUp() override {
         InputDispatcherTest::SetUp();
-        mDisplayInfos.clear();
-        mWindowInfos.clear();
+        removeAllWindowsAndDisplays();
     }
 
     void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -2374,6 +2509,11 @@
         mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
     }
 
+    void removeAllWindowsAndDisplays() {
+        mDisplayInfos.clear();
+        mWindowInfos.clear();
+    }
+
     // Set up a test scenario where the display has a scaled projection and there are two windows
     // on the display.
     std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
@@ -2405,11 +2545,11 @@
     std::vector<gui::WindowInfo> mWindowInfos;
 };
 
-TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) {
     auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
     // Send down to the first window. The point is represented in the display space. The point is
-    // selected so that if the hit test was done with the transform applied to it, then it would
-    // end up in the incorrect window.
+    // selected so that if the hit test was performed with the point and the bounds being in
+    // different coordinate spaces, the event would end up in the incorrect window.
     NotifyMotionArgs downMotionArgs =
             generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
@@ -2484,6 +2624,78 @@
     EXPECT_EQ(80, event->getY(0));
 }
 
+/** Ensure consistent behavior of InputDispatcher in all orientations. */
+class InputDispatcherDisplayOrientationFixture
+      : public InputDispatcherDisplayProjectionTest,
+        public ::testing::WithParamInterface<ui::Rotation> {};
+
+// This test verifies the touchable region of a window for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) {
+    constexpr static int32_t displayWidth = 400;
+    constexpr static int32_t displayHeight = 800;
+
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    const auto rotation = GetParam();
+
+    // Set up the display with the specified rotation.
+    const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+    const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+    const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+    const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+                                         logicalDisplayWidth, logicalDisplayHeight);
+    addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+    // Create a window with its bounds determined in the logical display.
+    const Rect frameInLogicalDisplay(100, 100, 200, 300);
+    const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(frameInDisplay, displayTransform);
+    addWindow(window);
+
+    // The following points in logical display space should be inside the window.
+    static const std::array<vec2, 4> insidePoints{
+            {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+    for (const auto pointInsideWindow : insidePoints) {
+        const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
+        const PointF pointInDisplaySpace{p.x, p.y};
+        const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                             ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+        mDispatcher->notifyMotion(&down);
+        window->consumeMotionDown();
+
+        const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                           ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+        mDispatcher->notifyMotion(&up);
+        window->consumeMotionUp();
+    }
+
+    // The following points in logical display space should be outside the window.
+    static const std::array<vec2, 5> outsidePoints{
+            {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+    for (const auto pointOutsideWindow : outsidePoints) {
+        const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
+        const PointF pointInDisplaySpace{p.x, p.y};
+        const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                             ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+        mDispatcher->notifyMotion(&down);
+
+        const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                           ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+        mDispatcher->notifyMotion(&up);
+    }
+    window->assertNoEvents();
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
+                         InputDispatcherDisplayOrientationFixture,
+                         ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+                                           ui::ROTATION_270));
+
 using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
                                             sp<IBinder>, sp<IBinder>)>;
 
@@ -2496,20 +2708,26 @@
     // Create a couple of windows
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setDupTouchToWallpaper(true);
+
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
-
+    sp<FakeWindowHandle> wallpaper =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaper->setIsWallpaper(true);
     // Add the windows to the dispatcher
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}});
 
     // Send down to the first window
     NotifyMotionArgs downMotionArgs =
             generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                ADISPLAY_ID_DEFAULT);
     mDispatcher->notifyMotion(&downMotionArgs);
+
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
+    wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 
     // Transfer touch to the second window
     TransferFunction f = GetParam();
@@ -2518,6 +2736,7 @@
     // The first window gets cancel and the second gets down
     firstWindow->consumeMotionCancel();
     secondWindow->consumeMotionDown();
+    wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
 
     // Send up event to the second window
     NotifyMotionArgs upMotionArgs =
@@ -2527,6 +2746,7 @@
     // The first  window gets no events and the second gets up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionUp();
+    wallpaper->assertNoEvents();
 }
 
 /**
@@ -2648,6 +2868,65 @@
     secondWindow->consumeMotionUp();
 }
 
+TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    // Create a couple of windows
+    sp<FakeWindowHandle> firstWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
+                                       ADISPLAY_ID_DEFAULT);
+    firstWindow->setDupTouchToWallpaper(true);
+    sp<FakeWindowHandle> secondWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+                                       ADISPLAY_ID_DEFAULT);
+    secondWindow->setDupTouchToWallpaper(true);
+
+    sp<FakeWindowHandle> wallpaper1 =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1", ADISPLAY_ID_DEFAULT);
+    wallpaper1->setIsWallpaper(true);
+
+    sp<FakeWindowHandle> wallpaper2 =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT);
+    wallpaper2->setIsWallpaper(true);
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}});
+
+    // Send down to the first window
+    NotifyMotionArgs downMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&downMotionArgs);
+
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+    wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+    wallpaper2->assertNoEvents();
+
+    // Transfer touch focus to the second window
+    TransferFunction f = GetParam();
+    bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+    ASSERT_TRUE(success);
+
+    // The first window gets cancel and the second gets down
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionDown();
+    wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+    wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first  window gets no events and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+    wallpaper1->assertNoEvents();
+    wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
 // For the cases of single pointer touch and two pointers non-split touch, the api's
 // 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
 // for the case where there are multiple pointers split across several windows.
@@ -6149,6 +6428,8 @@
     sp<FakeWindowHandle> mWindow;
     sp<FakeWindowHandle> mSecondWindow;
     sp<FakeWindowHandle> mDragWindow;
+    // Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
+    static constexpr int32_t MOUSE_POINTER_ID = 1;
 
     void SetUp() override {
         InputDispatcherTest::SetUp();
@@ -6163,11 +6444,41 @@
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
     }
 
-    void injectDown() {
-        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                                   {50, 50}))
-                << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+        switch (fromSource) {
+            case AINPUT_SOURCE_TOUCHSCREEN:
+                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                          injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+                                           ADISPLAY_ID_DEFAULT, {50, 50}))
+                        << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+                break;
+            case AINPUT_SOURCE_STYLUS:
+                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                          injectMotionEvent(
+                                  mDispatcher,
+                                  MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+                                                     AINPUT_SOURCE_STYLUS)
+                                          .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+                                          .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+                                                           .x(50)
+                                                           .y(50))
+                                          .build()));
+                break;
+            case AINPUT_SOURCE_MOUSE:
+                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                          injectMotionEvent(
+                                  mDispatcher,
+                                  MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                          .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                          .pointer(PointerBuilder(MOUSE_POINTER_ID,
+                                                                  AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                           .x(50)
+                                                           .y(50))
+                                          .build()));
+                break;
+            default:
+                FAIL() << "Source " << fromSource << " doesn't support drag and drop";
+        }
 
         // Window should receive motion event.
         mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -6176,9 +6487,9 @@
     // Start performing drag, we will create a drag window and transfer touch to it.
     // @param sendDown : if true, send a motion down on first window before perform drag and drop.
     // Returns true on success.
-    bool performDrag(bool sendDown = true) {
+    bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
         if (sendDown) {
-            injectDown();
+            injectDown(fromSource);
         }
 
         // The drag window covers the entire display
@@ -6196,36 +6507,10 @@
         }
         return transferred;
     }
-
-    // Start performing drag, we will create a drag window and transfer touch to it.
-    void performStylusDrag() {
-        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-                  injectMotionEvent(mDispatcher,
-                                    MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
-                                                       AINPUT_SOURCE_STYLUS)
-                                            .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
-                                            .pointer(PointerBuilder(0,
-                                                                    AMOTION_EVENT_TOOL_TYPE_STYLUS)
-                                                             .x(50)
-                                                             .y(50))
-                                            .build()));
-        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-
-        // The drag window covers the entire display
-        mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
-        mDispatcher->setInputWindows(
-                {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
-
-        // Transfer touch focus to the drag window
-        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
-                                        true /* isDragDrop */);
-        mWindow->consumeMotionCancel();
-        mDragWindow->consumeMotionDown();
-    }
 };
 
 TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
-    performDrag();
+    startDrag();
 
     // Move on window.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6263,7 +6548,7 @@
 }
 
 TEST_F(InputDispatcherDragTests, DragAndDrop) {
-    performDrag();
+    startDrag();
 
     // Move on window.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6295,7 +6580,7 @@
 }
 
 TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
-    performStylusDrag();
+    startDrag(true, AINPUT_SOURCE_STYLUS);
 
     // Move on window and keep button pressed.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6342,7 +6627,7 @@
 }
 
 TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
-    performDrag();
+    startDrag();
 
     // Set second window invisible.
     mSecondWindow->setVisible(false);
@@ -6378,6 +6663,9 @@
 }
 
 TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
+    // Ensure window could track pointerIds if it didn't support split touch.
+    mWindow->setPreventSplitting(true);
+
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {50, 50}))
@@ -6398,7 +6686,7 @@
     mWindow->consumeMotionPointerDown(1 /* pointerIndex */);
 
     // Should not perform drag and drop when window has multi fingers.
-    ASSERT_FALSE(performDrag(false));
+    ASSERT_FALSE(startDrag(false));
 }
 
 TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
@@ -6426,7 +6714,7 @@
     mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 
     // Perform drag and drop from first window.
-    ASSERT_TRUE(performDrag(false));
+    ASSERT_TRUE(startDrag(false));
 
     // Move on window.
     const MotionEvent secondFingerMoveEvent =
@@ -6461,7 +6749,7 @@
 }
 
 TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
-    performDrag();
+    startDrag();
 
     // Update window of second display.
     sp<FakeWindowHandle> windowInSecondary =
@@ -6512,6 +6800,55 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
+    startDrag(true, AINPUT_SOURCE_MOUSE);
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
+                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(50)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
+                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(150)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+
+    // drop to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+                                        .buttonState(0)
+                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
+                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(150)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
 
 TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index db46699..03fbf07 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -56,7 +56,9 @@
 
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
+static const std::string DISPLAY_UNIQUE_ID = "local:1";
 static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
 static constexpr int32_t DISPLAY_WIDTH = 480;
 static constexpr int32_t DISPLAY_HEIGHT = 800;
 static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
@@ -91,6 +93,31 @@
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
 
+using ::testing::AllOf;
+
+MATCHER_P(WithAction, action, "InputEvent with specified action") {
+    return arg.action == action;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+    return arg.source == source;
+}
+
+MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
+    return arg.displayId == displayId;
+}
+
+MATCHER_P2(WithCoords, x, y, "MotionEvent with specified action") {
+    return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY();
+}
+
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+    const auto argToolType = arg.pointerProperties[0].toolType;
+    *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+                     << motionToolTypeToString(argToolType);
+    return argToolType == toolType;
+}
+
 template<typename T>
 static inline T min(T a, T b) {
     return a < b ? a : b;
@@ -2877,7 +2904,6 @@
 
     // Device should be disabled because it is associated with a specific display, but the
     // corresponding display is not found.
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                        InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2908,7 +2934,6 @@
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
@@ -2918,6 +2943,21 @@
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
+/**
+ * This test reproduces a crash caused by a dangling reference that remains after device is added
+ * and removed. The reference is accessed in InputDevice::dump(..);
+ */
+TEST_F(InputDeviceTest, DumpDoesNotCrash) {
+    constexpr int32_t TEST_EVENTHUB_ID = 10;
+    mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
+
+    InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/);
+    device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/);
+    device.removeEventHubDevice(TEST_EVENTHUB_ID);
+    std::string dumpStr, eventHubDevStr;
+    device.dump(dumpStr, eventHubDevStr);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -2943,6 +2983,8 @@
         mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
                                                             *mFakeListener);
         mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+        // Consume the device reset notification generated when adding a new device.
+        mFakeListener->assertNotifyDeviceResetWasCalled();
     }
 
     void SetUp() override {
@@ -2967,6 +3009,8 @@
             mReader->loopOnce();
         }
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        // Loop the reader to flush the input listener queue.
+        mReader->loopOnce();
     }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -2990,6 +3034,8 @@
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
         mapper.reset(ARBITRARY_TIME);
+        // Loop the reader to flush the input listener queue.
+        mReader->loopOnce();
         return mapper;
     }
 
@@ -3015,6 +3061,7 @@
         event.code = code;
         event.value = value;
         mapper.process(&event);
+        // Loop the reader to flush the input listener queue.
         mReader->loopOnce();
     }
 
@@ -4186,10 +4233,14 @@
                             int32_t rotatedX, int32_t rotatedY);
 
     void prepareDisplay(int32_t orientation) {
-        const std::string uniqueId = "local:0";
-        const ViewportType viewportType = ViewportType::INTERNAL;
-        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                orientation, uniqueId, NO_PORT, viewportType);
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
+                                     DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+    }
+
+    void prepareSecondaryDisplay() {
+        setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                     DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+                                     ViewportType::EXTERNAL);
     }
 
     static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y,
@@ -4466,6 +4517,7 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
     // need to be rotated.
@@ -4484,11 +4536,13 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
@@ -4499,6 +4553,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
@@ -4509,6 +4564,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
@@ -4519,6 +4575,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
@@ -4912,7 +4969,6 @@
     ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
 
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
@@ -4974,33 +5030,118 @@
     ASSERT_EQ(20, args.pointerCoords[0].getY());
 }
 
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
+    addConfigurationProperty("cursor.mode", "pointer");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    // Setup for second display.
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-    const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
-    mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
-                                    true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
-                                    ViewportType::EXTERNAL);
-    mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
 
-    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
-    mFakePointerController->setPosition(100, 200);
-    mFakePointerController->setButtonState(0);
+    // Ensure the display is rotated.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
 
     NotifyMotionArgs args;
+
+    // Verify that the coordinates are rotated.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
-    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X));
+    ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+
+    // Enable Pointer Capture.
+    mFakePolicy->setPointerCapture(true);
+    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    NotifyPointerCaptureChangedArgs captureArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+    ASSERT_TRUE(captureArgs.request.enable);
+
+    // Move and verify rotation is not applied.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_EQ(10, args.pointerCoords[0].getX());
+    ASSERT_EQ(20, args.pointerCoords[0].getY());
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown.
+    // The InputDevice is not associated with any display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    // Ensure input events are generated for the secondary display.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+                  WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
     ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
-    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown,
+    // and associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+                  WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display as the display on which the pointer should be shown.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+    // Associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // The mapper should not generate any events because it is associated with a display that is
+    // different from the pointer display.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 // --- TouchInputMapperTest ---
@@ -6653,6 +6794,82 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
+TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION | PRESSURE);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs motionArgs;
+
+    // Set the initial state for the touch pointer.
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 200);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MAX);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+
+    // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
+    // state by reading the current axis values.
+    mapper.reset(ARBITRARY_TIME);
+
+    // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+    // the recreated touch state to generate a down event.
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+    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(fakePointerController);
+
+    addConfigurationProperty("touch.deviceType", "pointer");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+    // Start a stylus gesture.
+    processKey(mapper, BTN_TOOL_PEN, 1);
+    processDown(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+    // TODO(b/257078296): Pointer mode generates extra event.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus
+    // gesture should be disabled.
+    auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+    viewport->isActive = false;
+    mFakePolicy->updateViewport(*viewport);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+                  WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+    // TODO(b/257078296): Pointer mode generates extra event.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+                  WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest,
        Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -6683,6 +6900,67 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest,
+       Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    NotifyMotionArgs motionArgs;
+
+    // Start a new gesture.
+    processDown(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+    // Make the viewport inactive. This will put the device in disabled mode.
+    auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+    viewport->isActive = false;
+    mFakePolicy->updateViewport(*viewport);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // We should receive a cancel event for the ongoing gesture.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+    // Then we should be notified that the device was reset.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+    // No events are generated while the viewport is inactive.
+    processMove(mapper, 101, 201);
+    processSync(mapper);
+    processUp(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Start a new gesture while the viewport is still inactive.
+    processDown(mapper, 300, 400);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 300);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 400);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+    processSync(mapper);
+
+    // Make the viewport active again. The device should resume processing events.
+    viewport->isActive = true;
+    mFakePolicy->updateViewport(*viewport);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // The device is reset because it changes back to direct mode, without generating any events.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // In the next sync, the touch state that was recreated when the device was reset is reported.
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+    // No more events.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+}
+
 // --- TouchDisplayProjectionTest ---
 
 class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -8540,27 +8818,27 @@
     ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
-    // Finger move
+    // The ongoing touch should be canceled immediately
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+    // Finger move is ignored
     x += 10, y += 10;
     processPosition(mapper, x, y);
     processSync(mapper);
-
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // Reactivate display viewport
     displayViewport.isActive = true;
     ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
-    // Finger move again
+    // Finger move again starts new gesture
     x += 10, y += 10;
     processPosition(mapper, x, y);
     processSync(mapper);
-
-    // Gesture is aborted, so events after display is activated won't be dispatched until there is
-    // no pointer on the touch device.
-    mFakeListener->assertNotifyMotionWasNotCalled();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
@@ -9208,6 +9486,80 @@
     ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
 }
 
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // First finger down.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, 100, 200);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, 300, 400);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(
+            mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+    // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
+    // preserved. Resetting should not generate any events.
+    mapper.reset(ARBITRARY_TIME);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+    // the existing touch state to generate a down event.
+    processPosition(mapper, 301, 302);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // First finger touches down and releases.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, 100, 200);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(
+            mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+
+    // Reset the mapper. When the mapper is reset, we expect it to restore the latest
+    // raw state where no pointers are down.
+    mapper.reset(ARBITRARY_TIME);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Send an empty sync frame. Since there are no pointers, no events are generated.
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 6a26c63..57b382c 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,6 +69,13 @@
                                            "Expected notifyMotion() to have been called."));
 }
 
+void TestInputListener::assertNotifyMotionWasCalled(
+        const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+    NotifyMotionArgs outEventArgs;
+    ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+    ASSERT_THAT(outEventArgs, matcher);
+}
+
 void TestInputListener::assertNotifyMotionWasNotCalled() {
     ASSERT_NO_FATAL_FAILURE(
             assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 626cdfc..0bdfc6b 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -18,6 +18,7 @@
 #define _UI_TEST_INPUT_LISTENER_H
 
 #include <android-base/thread_annotations.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
@@ -48,6 +49,8 @@
 
     void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
 
+    void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+
     void assertNotifyMotionWasNotCalled();
 
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 0062f42..29fa001 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -16,13 +16,17 @@
 
 #include "../UnwantedInteractionBlocker.h"
 #include <android-base/silent_death_test.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 #include <linux/input.h>
 #include <thread>
+#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 
 #include "TestInputListener.h"
 
+using ::testing::AllOf;
+
 namespace android {
 
 constexpr int32_t DEVICE_ID = 3;
@@ -30,6 +34,8 @@
 constexpr int32_t Y_RESOLUTION = 11;
 constexpr int32_t MAJOR_RESOLUTION = 1;
 
+const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds();
+
 constexpr int POINTER_0_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 constexpr int POINTER_1_DOWN =
@@ -47,6 +53,27 @@
 constexpr int UP = AMOTION_EVENT_ACTION_UP;
 constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
 
+constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
+
+MATCHER_P(WithAction, action, "MotionEvent with specified action") {
+    bool result = true;
+    if (action == CANCEL) {
+        result &= (arg.flags & FLAG_CANCELED) != 0;
+    }
+    result &= arg.action == action;
+    *result_listener << "expected to receive " << MotionEvent::actionToString(action)
+                     << " but received " << MotionEvent::actionToString(arg.action) << " instead.";
+    return result;
+}
+
+MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") {
+    return arg.flags == flags;
+}
+
+static nsecs_t toNs(std::chrono::nanoseconds duration) {
+    return duration.count();
+}
+
 struct PointerData {
     float x;
     float y;
@@ -252,7 +279,7 @@
                                      /*newSuppressedPointerIds*/ {1});
     ASSERT_EQ(2u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
     assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
 }
 
@@ -267,7 +294,7 @@
                                      /*newSuppressedPointerIds*/ {0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -282,7 +309,7 @@
                                      /*newSuppressedPointerIds*/ {1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -297,7 +324,7 @@
                                      /*newSuppressedPointerIds*/ {0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -312,7 +339,7 @@
                                      /*newSuppressedPointerIds*/ {0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -328,7 +355,7 @@
                                      /*newSuppressedPointerIds*/ {0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -384,7 +411,7 @@
     expected.reported_tool_type = ::ui::EventPointerType::kTouch;
     expected.stylus_button = false;
 
-    ASSERT_EQ(expected, touches[0]) << toString(touches[0]);
+    ASSERT_EQ(expected, touches[0]) << touches[0];
 }
 
 // --- UnwantedInteractionBlockerTest ---
@@ -569,6 +596,114 @@
     dumpThread.join();
 }
 
+/**
+ * Heuristic filter that's present in the palm rejection model blocks touches early if the size
+ * of the touch is large. This is an integration test that checks that this filter kicks in.
+ */
+TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
+    mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+    // Small touch down
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+    // Large touch oval on the next move
+    NotifyMotionArgs args2 =
+            generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+    // Lift up the touch to force the model to decide on whether it's a palm
+    NotifyMotionArgs args3 =
+            generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL));
+}
+
+/**
+ * Send a stylus event that would have triggered the heuristic palm detector if it were a touch
+ * event. However, since it's a stylus event, it should propagate without being canceled through
+ * the blocker.
+ * This is similar to `HeuristicFilterWorks` test, but for stylus tool.
+ */
+TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) {
+    InputDeviceInfo info = generateTestDeviceInfo();
+    info.addSource(AINPUT_SOURCE_STYLUS);
+    mBlocker->notifyInputDevicesChanged({info});
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+    // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
+    NotifyMotionArgs args2 =
+            generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+    args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+    // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
+    // it's a palm.
+    NotifyMotionArgs args3 =
+            generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
+/**
+ * Send a mixed touch and stylus event.
+ * The touch event goes first, and is a palm. The stylus event goes down after.
+ * Stylus event should continue to work even after touch is detected as a palm.
+ */
+TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) {
+    InputDeviceInfo info = generateTestDeviceInfo();
+    info.addSource(AINPUT_SOURCE_STYLUS);
+    mBlocker->notifyInputDevicesChanged({info});
+
+    // Touch down
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+    // Stylus pointer down
+    NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN,
+                                                {{1, 2, 3}, {10, 20, 30}});
+    args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN));
+
+    // Large touch oval on the next finger move
+    NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE,
+                                                {{1, 2, 300}, {11, 21, 30}});
+    args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+    // Lift up the finger pointer. It should be canceled due to the heuristic filter.
+    NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
+                                                {{1, 2, 300}, {11, 21, 30}});
+    args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args4);
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
+
+    NotifyMotionArgs args5 =
+            generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
+    args5.pointerProperties[0].id = args4.pointerProperties[1].id;
+    args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args5);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+    // Lift up the stylus pointer
+    NotifyMotionArgs args6 =
+            generateMotionArgs(0 /*downTime*/, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    args6.pointerProperties[0].id = args4.pointerProperties[1].id;
+    args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args6);
+    mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
 using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
 
 /**
@@ -630,138 +765,138 @@
  */
 TEST_F(PalmRejectorTest, TwoPointersAreCanceled) {
     std::vector<NotifyMotionArgs> argsList;
-    constexpr nsecs_t downTime = 255955749837000;
+    const nsecs_t downTime = toNs(0ms);
 
     mPalmRejector->processMotion(
             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}}));
+            generateMotionArgs(downTime, toNs(8ms), MOVE, {{1406.0, 650.0, 52.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}}));
+            generateMotionArgs(downTime, toNs(16ms), MOVE, {{1429.0, 672.0, 46.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}}));
+            generateMotionArgs(downTime, toNs(24ms), MOVE, {{1417.0, 685.0, 41.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN,
+            generateMotionArgs(downTime, toNs(32ms), POINTER_1_DOWN,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955783039000, MOVE,
+            generateMotionArgs(downTime, toNs(40ms), MOVE,
                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955792536000, MOVE,
+            generateMotionArgs(downTime, toNs(48ms), MOVE,
                                {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955799474000, MOVE,
+            generateMotionArgs(downTime, toNs(56ms), MOVE,
                                {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955809177000, MOVE,
+            generateMotionArgs(downTime, toNs(64ms), MOVE,
                                {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955816131000, MOVE,
+            generateMotionArgs(downTime, toNs(72ms), MOVE,
                                {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}}));
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955825907000, MOVE,
+            generateMotionArgs(downTime, toNs(80ms), MOVE,
                                {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}}));
     ASSERT_EQ(1u, argsList.size());
     ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags);
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955832736000, MOVE,
+            generateMotionArgs(downTime, toNs(88ms), MOVE,
                                {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}}));
     ASSERT_EQ(2u, argsList.size());
     ASSERT_EQ(POINTER_0_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
     ASSERT_EQ(MOVE, argsList[1].action);
     ASSERT_EQ(1u, argsList[1].pointerCount);
     ASSERT_EQ(0, argsList[1].flags);
 
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955842432000, MOVE,
+            generateMotionArgs(downTime, toNs(96ms), MOVE,
                                {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955849380000, MOVE,
+            generateMotionArgs(downTime, toNs(104ms), MOVE,
                                {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955859046000, MOVE,
+            generateMotionArgs(downTime, toNs(112ms), MOVE,
                                {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}}));
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955869823000, MOVE,
+            generateMotionArgs(downTime, toNs(120ms), MOVE,
                                {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}}));
     ASSERT_EQ(1u, argsList.size());
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action);
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955875641000, MOVE,
+            generateMotionArgs(downTime, toNs(128ms), MOVE,
                                {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955882693000, MOVE,
+            generateMotionArgs(downTime, toNs(136ms), MOVE,
                                {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955892324000, MOVE,
+            generateMotionArgs(downTime, toNs(144ms), MOVE,
                                {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955899425000, MOVE,
+            generateMotionArgs(downTime, toNs(152ms), MOVE,
                                {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955909400000, MOVE,
+            generateMotionArgs(downTime, toNs(160ms), MOVE,
                                {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955915885000, MOVE,
+            generateMotionArgs(downTime, toNs(168ms), MOVE,
                                {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955925607000, MOVE,
+            generateMotionArgs(downTime, toNs(176ms), MOVE,
                                {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955932580000, MOVE,
+            generateMotionArgs(downTime, toNs(184ms), MOVE,
                                {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955942231000, MOVE,
+            generateMotionArgs(downTime, toNs(192ms), MOVE,
                                {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955949204000, MOVE,
+            generateMotionArgs(downTime, toNs(200ms), MOVE,
                                {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955959103000, MOVE,
+            generateMotionArgs(downTime, toNs(208ms), MOVE,
                                {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955965884000, MOVE,
+            generateMotionArgs(downTime, toNs(216ms), MOVE,
                                {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955975649000, MOVE,
+            generateMotionArgs(downTime, toNs(224ms), MOVE,
                                {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955982537000, MOVE,
+            generateMotionArgs(downTime, toNs(232ms), MOVE,
                                {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955992284000, MOVE,
+            generateMotionArgs(downTime, toNs(240ms), MOVE,
                                {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}}));
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955999348000, POINTER_1_UP,
+            generateMotionArgs(downTime, toNs(248ms), POINTER_1_UP,
                                {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}}));
     ASSERT_TRUE(argsList.empty());
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}}));
+            generateMotionArgs(downTime, toNs(256ms), MOVE, {{1362.0, 716.0, 55.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}}));
+            generateMotionArgs(downTime, toNs(264ms), MOVE, {{1347.0, 707.0, 54.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}}));
+            generateMotionArgs(downTime, toNs(272ms), MOVE, {{1340.0, 698.0, 54.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}}));
+            generateMotionArgs(downTime, toNs(280ms), MOVE, {{1338.0, 694.0, 55.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}}));
+            generateMotionArgs(downTime, toNs(288ms), MOVE, {{1336.0, 690.0, 53.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}}));
+            generateMotionArgs(downTime, toNs(296ms), MOVE, {{1334.0, 685.0, 47.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}}));
+            generateMotionArgs(downTime, toNs(304ms), MOVE, {{1333.0, 679.0, 46.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}}));
+            generateMotionArgs(downTime, toNs(312ms), MOVE, {{1332.0, 672.0, 45.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}}));
+            generateMotionArgs(downTime, toNs(320ms), MOVE, {{1333.0, 666.0, 40.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}}));
+            generateMotionArgs(downTime, toNs(328ms), MOVE, {{1336.0, 661.0, 24.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}}));
+            generateMotionArgs(downTime, toNs(336ms), MOVE, {{1338.0, 656.0, 16.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}}));
+            generateMotionArgs(downTime, toNs(344ms), MOVE, {{1341.0, 649.0, 1.0}}));
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}}));
+            generateMotionArgs(downTime, toNs(352ms), UP, {{1341.0, 649.0, 1.0}}));
     ASSERT_TRUE(argsList.empty());
 }
 
@@ -845,7 +980,7 @@
     ASSERT_EQ(2u, argsList.size());
     // First event - cancel pointer 1
     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
     // Second event - send MOVE for the remaining pointer
     ASSERT_EQ(MOVE, argsList[1].action);
     ASSERT_EQ(0, argsList[1].flags);
@@ -886,7 +1021,7 @@
     // Cancel all
     ASSERT_EQ(CANCEL, argsList[0].action);
     ASSERT_EQ(2u, argsList[0].pointerCount);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     // Future move events are ignored
     argsList = mPalmRejector->processMotion(
@@ -932,7 +1067,7 @@
                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
     ASSERT_EQ(1u, argsList.size());
     ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     // Future move events should not go to the listener.
     argsList = mPalmRejector->processMotion(
@@ -966,7 +1101,7 @@
                                {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
     ASSERT_EQ(2u, argsList.size());
     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action);
     ASSERT_EQ(0, argsList[1].flags);
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 4676167..9e41a93 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -97,9 +97,14 @@
     return calling_pid == request_pid;
 }
 
-MemtrackProxy::MemtrackProxy()
-      : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
-        memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+MemtrackProxy::MemtrackProxy() {
+    memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance();
+
+    // Only check for a HIDL implementation if we failed to get the AIDL service
+    if (!memtrack_aidl_instance_) {
+        memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance();
+    }
+}
 
 ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
                                             std::vector<MemtrackRecord>* _aidl_return) {
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index e75eade..f5b360f 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -274,7 +274,7 @@
             .type = type,
             .format = format,
             .size = static_cast<int32_t>(memory->size),
-            .memoryHandle = makeToAidl(memory->handle),
+            .memoryHandle = dupToAidl(memory->handle),
     };
 
     return convertToStatus(mSensors->registerDirectChannel(mem, channelHandle));
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 948692b..e0a4f03 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1328,6 +1328,7 @@
             mSensors.getUserDebugSensors() : mSensors.getUserSensors();
     Vector<Sensor> accessibleSensorList;
 
+    resetTargetSdkVersionCache(opPackageName);
     bool isCapped = isRateCappedBasedOnPermission(opPackageName);
     for (size_t i = 0; i < initialSensorList.size(); i++) {
         Sensor sensor = initialSensorList[i];
@@ -1367,6 +1368,7 @@
     if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
         return nullptr;
     }
+    resetTargetSdkVersionCache(opPackageName);
 
     Mutex::Autolock _l(mLock);
     // To create a client in DATA_INJECTION mode to inject data, SensorService should already be
@@ -1402,6 +1404,7 @@
 sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
         const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
         const native_handle *resource) {
+    resetTargetSdkVersionCache(opPackageName);
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
 
     // No new direct connections are allowed when sensor privacy is enabled
@@ -1643,14 +1646,6 @@
         checkWakeLockStateLocked(&connLock);
     }
 
-    {
-        Mutex::Autolock packageLock(sPackageTargetVersionLock);
-        auto iter = sPackageTargetVersion.find(c->mOpPackageName);
-        if (iter != sPackageTargetVersion.end()) {
-            sPackageTargetVersion.erase(iter);
-        }
-    }
-
     SensorDevice& dev(SensorDevice::getInstance());
     dev.notifyConnectionDestroyed(c);
 }
@@ -2091,6 +2086,14 @@
     return targetSdkVersion;
 }
 
+void SensorService::resetTargetSdkVersionCache(const String16& opPackageName) {
+    Mutex::Autolock packageLock(sPackageTargetVersionLock);
+    auto iter = sPackageTargetVersion.find(opPackageName);
+    if (iter != sPackageTargetVersion.end()) {
+        sPackageTargetVersion.erase(iter);
+    }
+}
+
 void SensorService::checkWakeLockState() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     checkWakeLockStateLocked(&connLock);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 234dc9c..4ba3c51 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -377,6 +377,7 @@
             const String16& opPackageName);
     static bool hasPermissionForSensor(const Sensor& sensor);
     static int getTargetSdkVersion(const String16& opPackageName);
+    static void resetTargetSdkVersionCache(const String16& opPackageName);
     // SensorService acquires a partial wakelock for delivering events from wake up sensors. This
     // method checks whether all the events from these wake up sensors have been delivered to the
     // corresponding applications, if yes the wakelock is released.
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 6d8d574..9b03344 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -201,7 +201,7 @@
         // if thread not initialized, start thread
         mStopThread = false;
         std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
-            struct sched_param p = {0};
+            struct sched_param p = {};
             p.sched_priority = 10;
             if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
                 LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 0d6e476..cb586c6 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -11,6 +11,7 @@
     name: "libsensorserviceaidl_fuzzer",
     defaults: [
         "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
     ],
     host_supported: true,
     static_libs: [
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
index 1b63d76..ee8ceb3 100644
--- a/services/sensorservice/aidl/fuzzer/fuzzer.cpp
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -16,7 +16,7 @@
 #include <fuzzbinder/libbinder_ndk_driver.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
-#include <ServiceManager.h>
+#include <fakeservicemanager/FakeServiceManager.h>
 #include <android-base/logging.h>
 #include <android/binder_interface_utils.h>
 #include <fuzzbinder/random_binder.h>
@@ -29,7 +29,7 @@
 [[clang::no_destroy]] static std::once_flag gSmOnce;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+    static android::sp<android::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager();
     std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
     fakeServiceManager->clear();
 
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
index 26bcdc5..beb38b9 100644
--- a/services/sensorservice/aidl/utils.cpp
+++ b/services/sensorservice/aidl/utils.cpp
@@ -58,7 +58,7 @@
 ::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
     ::aidl::android::hardware::sensors::Event dst;
     ::android::hardware::sensors::implementation::
-            convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+            convertFromASensorEvent(src, &dst);
     return dst;
 }
 
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index 9380600..0a4e684 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -60,6 +60,9 @@
     if (mPollThread.joinable()) {
         mPollThread.join();
     }
+
+    ::android::SensorManager::removeInstanceForPackage(
+            String16(ISensorManager::descriptor));
 }
 
 // Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
 
 ::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
     ::android::hardware::sensors::V1_0::Event dst;
-    ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
-            reinterpret_cast<const sensors_event_t&>(src), &dst);
+    ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+            src, &dst);
     return dst;
 }
 
diff --git a/services/stats/.clang-format b/services/stats/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/services/stats/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+  - Regex:    '^"Log\.h"'
+    Priority:    -1
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index 7fea616..6b99627 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -13,11 +13,15 @@
         "StatsAidl.cpp",
         "StatsHal.cpp",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "android.frameworks.stats@1.0",
-        "android.frameworks.stats-V1-ndk",
+        "android.frameworks.stats-V2-ndk",
         "libbinder_ndk",
+        "libexpresslog",
         "libhidlbase",
         "liblog",
         "libstatslog",
@@ -29,10 +33,12 @@
     ],
     export_shared_lib_headers: [
         "android.frameworks.stats@1.0",
-        "android.frameworks.stats-V1-ndk",
+        "android.frameworks.stats-V2-ndk",
     ],
     local_include_dirs: [
         "include/stats",
     ],
-    vintf_fragments: ["android.frameworks.stats@1.0-service.xml"]
+    vintf_fragments: [
+        "android.frameworks.stats-service.xml",
+    ],
 }
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index a3b68f1..b22f903 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -14,63 +14,259 @@
  * limitations under the License.
  */
 
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false  // STOPSHIP if true
 #define LOG_TAG "StatsAidl"
 
-#include <log/log.h>
-#include <statslog.h>
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
 
 #include "StatsAidl.h"
 
+#include <Counter.h>
+#include <log/log.h>
+#include <stats_annotations.h>
+#include <stats_event.h>
+#include <statslog.h>
+
+#include <unordered_map>
+
+namespace {
+    static const char* g_AtomErrorMetricName =
+        "statsd_errors.value_report_vendor_atom_errors_count";
+}
+
 namespace aidl {
 namespace android {
 namespace frameworks {
 namespace stats {
 
-StatsHal::StatsHal() {}
+using ::android::expresslog::Counter;
+
+template <typename E>
+constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
+    return static_cast<typename std::underlying_type<E>::type>(e);
+}
+
+StatsHal::StatsHal() {
+}
+
+bool write_annotation(AStatsEvent* event, const Annotation& annotation) {
+    switch (annotation.value.getTag()) {
+        case AnnotationValue::boolValue: {
+            AStatsEvent_addBoolAnnotation(event, to_underlying(annotation.annotationId),
+                                          annotation.value.get<AnnotationValue::boolValue>());
+            break;
+        }
+        case AnnotationValue::intValue: {
+            AStatsEvent_addInt32Annotation(event, to_underlying(annotation.annotationId),
+                                           annotation.value.get<AnnotationValue::intValue>());
+            break;
+        }
+        default: {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool write_atom_annotations(AStatsEvent* event,
+                            const std::vector<std::optional<Annotation>>& annotations) {
+    for (const auto& atomAnnotation : annotations) {
+        if (!atomAnnotation) {
+            return false;
+        }
+        if (!write_annotation(event, *atomAnnotation)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool write_field_annotations(AStatsEvent* event, const std::vector<Annotation>& annotations) {
+    for (const auto& fieldAnnotation : annotations) {
+        if (!write_annotation(event, fieldAnnotation)) {
+            return false;
+        }
+    }
+    return true;
+}
 
 ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
-    std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
     if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
-        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId);
+        Counter::logIncrement(g_AtomErrorMetricName);
         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-            -1, "Not a valid vendor atom ID");
+                -1, "Not a valid vendor atom ID");
     }
-    if (reverseDomainName.length() > 50) {
-        ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+    if (vendorAtom.reverseDomainName.length() > 50) {
+        ALOGE("Vendor atom reverse domain name %s is too long.",
+              vendorAtom.reverseDomainName.c_str());
+        Counter::logIncrement(g_AtomErrorMetricName);
         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-            -1, "Vendor atom reverse domain name is too long");
+                -1, "Vendor atom reverse domain name is too long");
     }
     AStatsEvent* event = AStatsEvent_obtain();
     AStatsEvent_setAtomId(event, vendorAtom.atomId);
+
+    if (vendorAtom.atomAnnotations) {
+        if (!write_atom_annotations(event, *vendorAtom.atomAnnotations)) {
+            AStatsEvent_release(event);
+            ALOGE("Atom ID %ld has incompatible atom level annotation", (long)vendorAtom.atomId);
+            Counter::logIncrement(g_AtomErrorMetricName);
+            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    -1, "invalid atom annotation");
+        }
+    }
+
+    // populate map for quickier access for VendorAtomValue associated annotations by value index
+    std::unordered_map<int, int> fieldIndexToAnnotationSetMap;
+    if (vendorAtom.valuesAnnotations) {
+        const std::vector<std::optional<AnnotationSet>>& valuesAnnotations =
+                *vendorAtom.valuesAnnotations;
+        for (int i = 0; i < valuesAnnotations.size(); i++) {
+            if (valuesAnnotations[i]) {
+                fieldIndexToAnnotationSetMap[valuesAnnotations[i]->valueIndex] = i;
+            }
+        }
+    }
+
     AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
+    size_t atomValueIdx = 0;
     for (const auto& atomValue : vendorAtom.values) {
         switch (atomValue.getTag()) {
             case VendorAtomValue::intValue:
-                AStatsEvent_writeInt32(event,
-                    atomValue.get<VendorAtomValue::intValue>());
+                AStatsEvent_writeInt32(event, atomValue.get<VendorAtomValue::intValue>());
                 break;
             case VendorAtomValue::longValue:
-                AStatsEvent_writeInt64(event,
-                    atomValue.get<VendorAtomValue::longValue>());
+                AStatsEvent_writeInt64(event, atomValue.get<VendorAtomValue::longValue>());
                 break;
             case VendorAtomValue::floatValue:
-                AStatsEvent_writeFloat(event,
-                    atomValue.get<VendorAtomValue::floatValue>());
+                AStatsEvent_writeFloat(event, atomValue.get<VendorAtomValue::floatValue>());
                 break;
             case VendorAtomValue::stringValue:
                 AStatsEvent_writeString(event,
-                    atomValue.get<VendorAtomValue::stringValue>().c_str());
+                                        atomValue.get<VendorAtomValue::stringValue>().c_str());
                 break;
+            case VendorAtomValue::boolValue:
+                AStatsEvent_writeBool(event, atomValue.get<VendorAtomValue::boolValue>());
+                break;
+            case VendorAtomValue::repeatedIntValue: {
+                const std::optional<std::vector<int>>& repeatedIntValue =
+                        atomValue.get<VendorAtomValue::repeatedIntValue>();
+                if (!repeatedIntValue) {
+                    AStatsEvent_writeInt32Array(event, {}, 0);
+                    break;
+                }
+                AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
+                                            repeatedIntValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedLongValue: {
+                const std::optional<std::vector<int64_t>>& repeatedLongValue =
+                        atomValue.get<VendorAtomValue::repeatedLongValue>();
+                if (!repeatedLongValue) {
+                    AStatsEvent_writeInt64Array(event, {}, 0);
+                    break;
+                }
+                AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
+                                            repeatedLongValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedFloatValue: {
+                const std::optional<std::vector<float>>& repeatedFloatValue =
+                        atomValue.get<VendorAtomValue::repeatedFloatValue>();
+                if (!repeatedFloatValue) {
+                    AStatsEvent_writeFloatArray(event, {}, 0);
+                    break;
+                }
+                AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
+                                            repeatedFloatValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedStringValue: {
+                const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
+                        atomValue.get<VendorAtomValue::repeatedStringValue>();
+                if (!repeatedStringValue) {
+                    AStatsEvent_writeStringArray(event, {}, 0);
+                    break;
+                }
+                const std::vector<std::optional<std::string>>& repeatedStringVector =
+                        *repeatedStringValue;
+                const char* cStringArray[repeatedStringVector.size()];
+
+                for (int i = 0; i < repeatedStringVector.size(); ++i) {
+                    cStringArray[i] = repeatedStringVector[i].has_value()
+                                              ? repeatedStringVector[i]->c_str()
+                                              : "";
+                }
+
+                AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
+                break;
+            }
+            case VendorAtomValue::repeatedBoolValue: {
+                const std::optional<std::vector<bool>>& repeatedBoolValue =
+                        atomValue.get<VendorAtomValue::repeatedBoolValue>();
+                if (!repeatedBoolValue) {
+                    AStatsEvent_writeBoolArray(event, {}, 0);
+                    break;
+                }
+                const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
+                bool boolArray[repeatedBoolValue->size()];
+
+                for (int i = 0; i < repeatedBoolVector.size(); ++i) {
+                    boolArray[i] = repeatedBoolVector[i];
+                }
+
+                AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size());
+                break;
+            }
+            case VendorAtomValue::byteArrayValue: {
+                const std::optional<std::vector<uint8_t>>& byteArrayValue =
+                        atomValue.get<VendorAtomValue::byteArrayValue>();
+                if (!byteArrayValue) {
+                    AStatsEvent_writeByteArray(event, {}, 0);
+                    break;
+                }
+                AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
+                break;
+            }
+            default: {
+                AStatsEvent_release(event);
+                ALOGE("Atom ID %ld has invalid atomValue.getTag", (long)vendorAtom.atomId);
+                Counter::logIncrement(g_AtomErrorMetricName);
+                return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                        -1, "invalid atomValue.getTag");
+                break;
+            }
         }
+
+        const auto& valueAnnotationIndex = fieldIndexToAnnotationSetMap.find(atomValueIdx);
+        if (valueAnnotationIndex != fieldIndexToAnnotationSetMap.end()) {
+            const std::vector<Annotation>& fieldAnnotations =
+                    (*vendorAtom.valuesAnnotations)[valueAnnotationIndex->second]->annotations;
+            VLOG("Atom ID %ld has %ld annotations for field #%ld", (long)vendorAtom.atomId,
+                 (long)fieldAnnotations.size(), (long)atomValueIdx + 2);
+            if (!write_field_annotations(event, fieldAnnotations)) {
+                AStatsEvent_release(event);
+                ALOGE("Atom ID %ld has incompatible field level annotation for field #%ld",
+                      (long)vendorAtom.atomId, (long)atomValueIdx + 2);
+                Counter::logIncrement(g_AtomErrorMetricName);
+                return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                        -1, "invalid atom field annotation");
+            }
+        }
+        atomValueIdx++;
     }
     AStatsEvent_build(event);
     const int ret = AStatsEvent_write(event);
     AStatsEvent_release(event);
-
-    return ret <= 0 ?
-            ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") :
-            ndk::ScopedAStatus::ok();
+    if (ret <= 0) {
+        ALOGE("Error writing Atom ID %ld. Result: %d", (long)vendorAtom.atomId, ret);
+        Counter::logIncrement(g_AtomErrorMetricName);
+    }
+    return ret <= 0 ? ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret,
+                                                                              "report atom failed")
+                    : ndk::ScopedAStatus::ok();
 }
 
 }  // namespace stats
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index ae0a984..19176d9 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -14,42 +14,42 @@
  * limitations under the License.
  */
 
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false  // STOPSHIP if true
 #define LOG_TAG "StatsHal"
 
+#include "StatsHal.h"
+
 #include <log/log.h>
 #include <statslog.h>
 
-#include "StatsHal.h"
-
 namespace android {
 namespace frameworks {
 namespace stats {
 namespace V1_0 {
 namespace implementation {
 
-StatsHal::StatsHal() {}
+StatsHal::StatsHal() {
+}
 
-hardware::Return<void> StatsHal::reportSpeakerImpedance(
-        const SpeakerImpedance& speakerImpedance) {
+hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) {
     android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
-            speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+                               speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
     android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
-            hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+                               hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportPhysicalDropDetected(
         const PhysicalDropDetected& physicalDropDetected) {
-    android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
-            int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
-            physicalDropDetected.freefallDuration);
+    android::util::stats_write(
+            android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg),
+            physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration);
 
     return hardware::Void();
 }
@@ -58,20 +58,21 @@
     std::vector<int32_t> buckets = chargeCycles.cycleBucket;
     int initialSize = buckets.size();
     for (int i = 0; i < 10 - initialSize; i++) {
-        buckets.push_back(0); // Push 0 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],
-            buckets[9]);
+                               buckets[2], buckets[3], buckets[4], buckets[5], buckets[6],
+                               buckets[7], buckets[8], buckets[9]);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
         const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
-    android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
-            int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
-            batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
+    android::util::stats_write(
+            android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type),
+            batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV,
+            batteryHealthSnapshotArgs.currentMicroA,
             batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
             batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
 
@@ -87,14 +88,15 @@
 hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
         const BatteryCausedShutdown& batteryCausedShutdown) {
     android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
-            batteryCausedShutdown.voltageMicroV);
+                               batteryCausedShutdown.voltageMicroV);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
         const UsbPortOverheatEvent& usbPortOverheatEvent) {
-    android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+    android::util::stats_write(
+            android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
             usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
             usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
             usbPortOverheatEvent.timeToInactive);
@@ -102,23 +104,22 @@
     return hardware::Void();
 }
 
-hardware::Return<void> StatsHal::reportSpeechDspStat(
-        const SpeechDspStat& speechDspStat) {
+hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) {
     android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
-            speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
-            speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+                               speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
+                               speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
 
     return hardware::Void();
 }
 
 hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
-    std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
     if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
-        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId);
         return hardware::Void();
     }
-    if (reverseDomainName.length() > 50) {
-        ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+    if (vendorAtom.reverseDomainName.size() > 50) {
+        ALOGE("Vendor atom reverse domain name %s is too long.",
+              vendorAtom.reverseDomainName.c_str());
         return hardware::Void();
     }
     AStatsEvent* event = AStatsEvent_obtain();
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats-service.xml
similarity index 93%
rename from services/stats/android.frameworks.stats@1.0-service.xml
rename to services/stats/android.frameworks.stats-service.xml
index c564b7b..7e2635e 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats-service.xml
@@ -11,7 +11,7 @@
 
     <hal format="aidl">
         <name>android.frameworks.stats</name>
-        <version>1</version>
+        <version>2</version>
         <fqname>IStats/default</fqname>
     </hal>
 </manifest>
diff --git a/services/stats/include/stats/StatsAidl.h b/services/stats/include/stats/StatsAidl.h
index 219e71e..340b539 100644
--- a/services/stats/include/stats/StatsAidl.h
+++ b/services/stats/include/stats/StatsAidl.h
@@ -28,8 +28,7 @@
     /**
      * Binder call to get vendor atom.
      */
-    virtual ndk::ScopedAStatus reportVendorAtom(
-        const VendorAtom& in_vendorAtom) override;
+    virtual ndk::ScopedAStatus reportVendorAtom(const VendorAtom& in_vendorAtom) override;
 };
 
 }  // namespace stats
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
index 071e54f..864ad14 100644
--- a/services/stats/include/stats/StatsHal.h
+++ b/services/stats/include/stats/StatsHal.h
@@ -16,7 +16,6 @@
 
 #include <android/frameworks/stats/1.0/IStats.h>
 #include <android/frameworks/stats/1.0/types.h>
-
 #include <stats_event.h>
 
 using namespace android::frameworks::stats::V1_0;
@@ -30,8 +29,8 @@
 using android::hardware::Return;
 
 /**
-* Implements the Stats HAL
-*/
+ * Implements the Stats HAL
+ */
 class StatsHal : public IStats {
 public:
     StatsHal();
@@ -50,12 +49,12 @@
      * Binder call to get PhysicalDropDetected atom.
      */
     virtual Return<void> reportPhysicalDropDetected(
-             const PhysicalDropDetected& physicalDropDetected) override;
+            const PhysicalDropDetected& physicalDropDetected) override;
 
     /**
      * Binder call to get ChargeCyclesReported atom.
      */
-     virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+    virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
 
     /**
      * Binder call to get BatteryHealthSnapshot atom.
@@ -83,8 +82,7 @@
     /**
      * Binder call to get Speech DSP state atom.
      */
-    virtual Return<void> reportSpeechDspStat(
-            const SpeechDspStat& speechDspStat) override;
+    virtual Return<void> reportSpeechDspStat(const SpeechDspStat& speechDspStat) override;
 
     /**
      * Binder call to get vendor atom.
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 3875f15..e06f3c4 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -459,6 +459,7 @@
 }
 
 bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
     mDrawingState.surfaceDamageRegion = surfaceDamage;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 16cb41b..5e84be1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@
     // similar requests if needed.
     virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
+    // Sends the brightness setting to HWC
+    virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+
 protected:
     ~Display() = default;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index db2fd1b..2203639 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -27,6 +27,7 @@
 #include <compositionengine/LayerFE.h>
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
+#include <ui/FenceTime.h>
 #include <ui/GraphicTypes.h>
 #include <ui/LayerStack.h>
 #include <ui/Region.h>
@@ -311,6 +312,8 @@
             const Region& flashRegion,
             std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
     virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+    virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0;
+    virtual bool isPowerHintSessionEnabled() = 0;
     virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
     virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 61a0e6a..33a10a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -72,6 +72,7 @@
             const compositionengine::DisplayColorProfileCreationArgs&) override;
     void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
     void createClientCompositionCache(uint32_t cacheSize) override;
+    void applyDisplayBrightness(const bool applyImmediately) override;
 
     // Internal helpers used by chooseCompositionStrategy()
     using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
@@ -89,6 +90,8 @@
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
 private:
+    bool isPowerHintSessionEnabled() override;
+    void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
     DisplayId mId;
     bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 31c51e6..428c19f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -140,6 +140,8 @@
           std::vector<LayerFE*> &outLayerFEs) override;
     void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
     void setExpensiveRenderingExpected(bool enabled) override;
+    void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
+    bool isPowerHintSessionEnabled() override;
     void dumpBase(std::string&) const;
 
     // Implemented by the final implementation for the final state it uses.
@@ -150,6 +152,8 @@
     virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
     virtual void dumpState(std::string& out) const = 0;
 
+    bool mustRecompose() const;
+
 private:
     void dirtyEntireOutput();
     compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
@@ -168,6 +172,9 @@
     std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
     std::unique_ptr<planner::Planner> mPlanner;
     std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+
+    // Whether the content must be recomposed this frame.
+    bool mMustRecompose = false;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 2bfd3cf..24a7744 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -115,7 +115,7 @@
 
     // Renders the cached set with the supplied output composition state.
     void render(renderengine::RenderEngine& re, TexturePool& texturePool,
-                const OutputCompositionState& outputState);
+                const OutputCompositionState& outputState, bool deviceHandlesColorTransform);
 
     void dump(std::string& result) const;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 92cc484..f934cb2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -106,7 +106,8 @@
 
     // Renders the newest cached sets with the supplied output composition state
     void renderCachedSets(const OutputCompositionState& outputState,
-                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                          bool deviceHandlesColorTransform);
 
     void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index b7ebca6..c968df7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -65,7 +65,8 @@
     // Rendering a pending cached set is optional: if the renderDeadline is not far enough in the
     // future then the planner may opt to skip rendering the cached set.
     void renderCachedSets(const OutputCompositionState& outputState,
-                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                          bool deviceHandlesColorTransform);
 
     void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 72e6f3b..7e99ec2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@
     MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
     MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
     MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+    MOCK_METHOD1(applyDisplayBrightness, void(const bool));
     MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index cb9fbad..2a04949 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -133,6 +133,8 @@
     MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
     MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
     MOCK_METHOD1(setTreat170mAsSrgb, void(bool));
+    MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence));
+    MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index b79b46b..163d9a3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -203,6 +203,24 @@
     setReleasedLayers(std::move(releasedLayers));
 }
 
+void Display::applyDisplayBrightness(const bool applyImmediately) {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId());
+    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
+        physicalDisplayId && getState().displayBrightness) {
+        const status_t result =
+                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+                                         getState().displayBrightnessNits,
+                                         Hwc2::Composer::DisplayBrightnessOptions{
+                                                 .applyImmediately = applyImmediately})
+                        .get();
+        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+                 getName().c_str(), result, strerror(-result));
+    }
+    // Clear out the display brightness now that it's been communicated to composer.
+    editState().displayBrightness.reset();
+}
+
 void Display::beginFrame() {
     Output::beginFrame();
 
@@ -212,20 +230,7 @@
         return;
     }
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
-        physicalDisplayId && getState().displayBrightness) {
-        const status_t result =
-                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
-                                         getState().displayBrightnessNits,
-                                         Hwc2::Composer::DisplayBrightnessOptions{
-                                                 .applyImmediately = false})
-                        .get();
-        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
-                 getName().c_str(), result, strerror(-result));
-    }
-    // Clear out the display brightness now that it's been communicated to composer.
-    editState().displayBrightness.reset();
+    applyDisplayBrightness(false);
 }
 
 bool Display::chooseCompositionStrategy(
@@ -243,11 +248,14 @@
         return false;
     }
 
+    const nsecs_t startTime = systemTime();
+
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
+    const bool requiresClientComposition = anyLayersRequireClientComposition();
     if (status_t result =
-                hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+                hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
                                                 getState().earliestPresentTime,
                                                 getState().previousPresentFence,
                                                 getState().expectedPresentTime, outChanges);
@@ -257,6 +265,11 @@
         return false;
     }
 
+    if (isPowerHintSessionEnabled()) {
+        mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime());
+        mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+    }
+
     return true;
 }
 
@@ -356,9 +369,24 @@
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
+
+    const nsecs_t startTime = systemTime();
+
+    if (isPowerHintSessionEnabled()) {
+        if (!getCompositionEngine().getHwComposer().getComposer()->isSupported(
+                    Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+            getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+            mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime);
+        }
+    }
+
     hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime,
                                    getState().previousPresentFence);
 
+    if (isPowerHintSessionEnabled()) {
+        mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime());
+    }
+
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
     // TODO(b/121291683): Change HWComposer call to return entire map
@@ -384,18 +412,33 @@
     }
 }
 
+bool Display::isPowerHintSessionEnabled() {
+    return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession();
+}
+
+void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
+    mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+}
+
 void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
                           GpuCompositionResult&& result) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
+    if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
         ALOGV("Skipping display composition");
         return;
     }
 
     impl::Output::finishFrame(refreshArgs, std::move(result));
+
+    if (isPowerHintSessionEnabled()) {
+        auto& hwc = getCompositionEngine().getHwComposer();
+        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+        }
+    }
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index c3385a8..d0c5803 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -586,8 +586,29 @@
     // Remove the transparent area from the visible region
     if (!layerFEState->isOpaque) {
         if (tr.preserveRects()) {
-            // transform the transparent region
-            transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+            // Clip the transparent region to geomLayerBounds first
+            // The transparent region may be influenced by applications, for
+            // instance, by overriding ViewGroup#gatherTransparentRegion with a
+            // custom view. Once the layer stack -> display mapping is known, we
+            // must guard against very wrong inputs to prevent underflow or
+            // overflow errors. We do this here by constraining the transparent
+            // region to be within the pre-transform layer bounds, since the
+            // layer bounds are expected to play nicely with the full
+            // transform.
+            const Region clippedTransparentRegionHint =
+                    layerFEState->transparentRegionHint.intersect(
+                            Rect(layerFEState->geomLayerBounds));
+
+            if (clippedTransparentRegionHint.isEmpty()) {
+                if (!layerFEState->transparentRegionHint.isEmpty()) {
+                    ALOGD("Layer: %s had an out of bounds transparent region",
+                          layerFE->getDebugName());
+                    layerFEState->transparentRegionHint.dump("transparentRegionHint");
+                }
+                transparentRegion.clear();
+            } else {
+                transparentRegion = tr.transform(clippedTransparentRegionHint);
+            }
         } else {
             // transformation too complex, can't do the
             // transparent region optimization.
@@ -956,17 +977,17 @@
     //   frame, then nothing more until we get new layers.
     // - When a display is created with a private layer stack, we won't
     //   emit any black frames until a layer is added to the layer stack.
-    const bool mustRecompose = dirty && !(empty && wasEmpty);
+    mMustRecompose = dirty && !(empty && wasEmpty);
 
     const char flagPrefix[] = {'-', '+'};
     static_cast<void>(flagPrefix);
-    ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
-             mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
-             flagPrefix[empty], flagPrefix[wasEmpty]);
+    ALOGV("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __func__,
+          mMustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+          flagPrefix[empty], flagPrefix[wasEmpty]);
 
-    mRenderSurface->beginFrame(mustRecompose);
+    mRenderSurface->beginFrame(mMustRecompose);
 
-    if (mustRecompose) {
+    if (mMustRecompose) {
         outputState.lastCompositionHadVisibleLayers = !empty;
     }
 }
@@ -1099,6 +1120,10 @@
         return;
     }
 
+    if (isPowerHintSessionEnabled()) {
+        // get fence end time to know when gpu is complete in display
+        setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get()))));
+    }
     // swap buffers (presentation)
     mRenderSurface->queueBuffer(std::move(*optReadyFence));
 }
@@ -1218,7 +1243,8 @@
             ATRACE_NAME("ClientCompositionCacheHit");
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
-            return base::unique_fd();
+            // b/239944175 pass the fence associated with the buffer.
+            return base::unique_fd(std::move(fd));
         }
         ATRACE_NAME("ClientCompositionCacheMiss");
         mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
@@ -1403,6 +1429,14 @@
     // The base class does nothing with this call.
 }
 
+void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) {
+    // The base class does nothing with this call.
+}
+
+bool Output::isPowerHintSessionEnabled() {
+    return false;
+}
+
 void Output::postFramebuffer() {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
@@ -1460,7 +1494,8 @@
 
 void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
+        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
+                                   getState().usesDeviceComposition || getSkipColorTransform());
     }
 }
 
@@ -1555,5 +1590,9 @@
     mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
 }
 
+bool Output::mustRecompose() const {
+    return mMustRecompose;
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 641b806..d6f02ee 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,7 +159,8 @@
 }
 
 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
-                       const OutputCompositionState& outputState) {
+                       const OutputCompositionState& outputState,
+                       bool deviceHandlesColorTransform) {
     ATRACE_CALL();
     const Rect& viewport = outputState.layerStackSpace.getContent();
     const ui::Dataspace& outputDataspace = outputState.dataspace;
@@ -170,6 +171,8 @@
             .physicalDisplay = outputState.framebufferSpace.getContent(),
             .clip = viewport,
             .outputDataspace = outputDataspace,
+            .colorTransform = outputState.colorTransformMatrix,
+            .deviceHandlesColorTransform = deviceHandlesColorTransform,
             .orientation = orientation,
             .targetLuminanceNits = outputState.displayBrightnessNits,
     };
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 1062b70..9175dd0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -99,7 +99,8 @@
 
 void Flattener::renderCachedSets(
         const OutputCompositionState& outputState,
-        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+        std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+        bool deviceHandlesColorTransform) {
     ATRACE_CALL();
 
     if (!mNewCachedSet) {
@@ -136,7 +137,7 @@
         }
     }
 
-    mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
+    mNewCachedSet->render(mRenderEngine, mTexturePool, outputState, deviceHandlesColorTransform);
 }
 
 void Flattener::dumpLayers(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index c8413eb..54133d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -201,11 +201,11 @@
                             finalPlan);
 }
 
-void Planner::renderCachedSets(
-        const OutputCompositionState& outputState,
-        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+void Planner::renderCachedSets(const OutputCompositionState& outputState,
+                               std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                               bool deviceHandlesColorTransform) {
     ATRACE_CALL();
-    mFlattener.renderCachedSets(outputState, renderDeadline);
+    mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 0e5a7b6..5369642 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -169,6 +169,7 @@
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
         EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false));
     }
 
     DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
@@ -970,16 +971,40 @@
 
     // We expect no calls to queueBuffer if composition was skipped.
     EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+    EXPECT_CALL(*renderSurface, beginFrame(false));
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
 
+    gpuDisplay->beginFrame();
     gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
 }
 
-TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
+    auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+    std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+    EXPECT_CALL(*renderSurface, beginFrame(false));
+
+    gpuDisplay->editState().isEnabled = true;
+    gpuDisplay->editState().usesClientComposition = false;
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
+
+    gpuDisplay->beginFrame();
+    gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
     auto args = getDisplayCreationArgsForGpuVirtualDisplay();
     std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
 
@@ -988,11 +1013,15 @@
 
     // We expect a single call to queueBuffer when composition is not skipped.
     EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+    EXPECT_CALL(*renderSurface, beginFrame(true));
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+
+    gpuDisplay->beginFrame();
     gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9b12b08..d7704a8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -137,6 +137,7 @@
     MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
     MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
                 (const, override));
+    MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 50adcfb..c8bd5e4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,11 +38,33 @@
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
-    MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
-                (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+    MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
     MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+    MOCK_METHOD(void, setGpuFenceTime,
+                (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+    MOCK_METHOD(void, setHwcValidateTiming,
+                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (override));
+    MOCK_METHOD(void, setHwcPresentTiming,
+                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (override));
+    MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+    MOCK_METHOD(void, setRequiresClientComposition,
+                (DisplayId displayId, bool requiresClientComposition), (override));
+    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+                (override));
+    MOCK_METHOD(void, setHwcPresentDelayedTime,
+                (DisplayId displayId,
+                 std::chrono::steady_clock::time_point earliestFrameStartTime));
+    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+    MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 063726b..cf12890 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1505,6 +1505,8 @@
     static const Region kTransparentRegionHint;
     static const Region kTransparentRegionHintTwo;
     static const Region kTransparentRegionHintTwo90Rotation;
+    static const Region kTransparentRegionHintNegative;
+    static const Region kTransparentRegionHintNegativeIntersectsBounds;
 
     StrictMock<OutputPartialMock> mOutput;
     LayerFESet mGeomSnapshots;
@@ -1528,6 +1530,10 @@
         Region(Rect(25, 20, 50, 75));
 const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
         Region(Rect(125, 25, 180, 50));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative =
+        Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds =
+        Region(Rect(INT32_MIN, INT32_MIN, 100, 100));
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1997,6 +2003,41 @@
                 RegionEq(kTransparentRegionHintTwo90Rotation));
 }
 
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+    mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation;
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+    mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative;
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+    ensureOutputLayerIfVisible();
+
+    // Check that the blocking region clips an out-of-bounds transparent region.
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+                RegionEq(kTransparentRegionHint));
+}
+
 /*
  * Output::present()
  */
@@ -3319,6 +3360,9 @@
         MOCK_METHOD2(appendRegionFlashRequests,
                      void(const Region&, std::vector<LayerFE::LayerSettings>&));
         MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+        MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence),
+                    (override));
+        MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
     };
 
     OutputComposeSurfacesTest() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8a99e4e..0e9db36 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -376,7 +376,7 @@
             .WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = false;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -429,7 +429,7 @@
             .WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -477,7 +477,58 @@
             .WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
+    expectReadyBuffer(cachedSet);
+
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+    EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, renderWhitePointNoColorTransform) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    // This is a duplicate of the "renderWhitePoint" test, but setting "deviceHandlesColorTransform"
+    // to false, in the render call.
+
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+
+    mOutputState.displayBrightnessNits = 400.f;
+
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<renderengine::LayerSettings>&,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+        EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+    };
+
+    EXPECT_CALL(*layerFE1,
+                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    mOutputState.isSecure = true;
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -527,7 +578,7 @@
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -767,7 +818,7 @@
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 TEST_F(CachedSetTest, addHolePunch_noBuffer) {
@@ -829,7 +880,7 @@
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 TEST_F(CachedSetTest, append_removesHolePunch) {
@@ -969,7 +1020,7 @@
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 50e3a28..96021ec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -159,13 +159,13 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 }
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -177,7 +177,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -187,7 +187,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -222,7 +222,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 }
 
 TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
@@ -284,7 +284,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -389,7 +389,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -428,7 +428,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -437,7 +437,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -452,7 +452,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -461,7 +461,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -505,7 +505,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -521,7 +521,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -534,7 +534,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -550,7 +550,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -562,7 +562,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -603,7 +603,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -616,7 +616,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -669,7 +669,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -682,7 +682,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -743,7 +743,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer0);
@@ -753,7 +753,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer0); // got overridden
     EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -815,7 +815,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer0);
@@ -825,7 +825,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer0); // got overridden
     EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -871,7 +871,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -881,7 +881,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
     EXPECT_EQ(nullptr, overrideBuffer3);
@@ -917,7 +917,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -928,7 +928,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
     }
@@ -971,7 +971,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -981,7 +981,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
     EXPECT_NE(nullptr, overrideBuffer3);
@@ -1020,7 +1020,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
     ASSERT_NE(std::nullopt, cachedSet);
@@ -1034,7 +1034,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
@@ -1063,7 +1063,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1071,12 +1071,12 @@
     // Simulate attempting to render prior to merging the new cached set with the layer stack.
     // Here we should not try to re-render.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We provide the override buffer now that it's rendered
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
@@ -1120,7 +1120,8 @@
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
         mFlattener->renderCachedSets(mOutputState,
                                      std::chrono::steady_clock::now() -
-                                             (kCachedSetRenderDuration + 10ms));
+                                             (kCachedSetRenderDuration + 10ms),
+                                     true);
     }
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
@@ -1128,7 +1129,8 @@
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState,
                                  std::chrono::steady_clock::now() -
-                                         (kCachedSetRenderDuration + 10ms));
+                                         (kCachedSetRenderDuration + 10ms),
+                                 true);
 }
 
 TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
@@ -1162,7 +1164,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1175,7 +1177,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1213,7 +1215,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1226,7 +1228,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1264,7 +1266,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1277,7 +1279,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1318,7 +1320,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1332,7 +1334,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1371,7 +1373,7 @@
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
             .WillOnce(Return(ByMove(
                     futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1384,7 +1386,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a915b61..b49c95d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -104,7 +104,7 @@
 
     mCompositionDisplay->getRenderSurface()->initialize();
 
-    setPowerMode(args.initialPowerMode);
+    if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
 
     // initialize the display orientation transform.
     setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -173,20 +173,31 @@
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+    if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+        if (mStagedBrightness && mBrightness != mStagedBrightness) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+            mBrightness = *mStagedBrightness;
+        }
+        mStagedBrightness = std::nullopt;
+        getCompositionDisplay()->applyDisplayBrightness(true);
+    }
+
     mPowerMode = mode;
-    getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
+
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
+                                                   *mPowerMode != hal::PowerMode::OFF);
 }
 
 void DisplayDevice::enableLayerCaching(bool enable) {
     getCompositionDisplay()->setLayerCachingEnabled(enable);
 }
 
-hal::PowerMode DisplayDevice::getPowerMode() const {
+std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const {
     return mPowerMode;
 }
 
 bool DisplayDevice::isPoweredOn() const {
-    return mPowerMode != hal::PowerMode::OFF;
+    return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
 void DisplayDevice::setActiveMode(DisplayModeId id) {
@@ -211,6 +222,7 @@
               to_string(getId()).c_str());
         return BAD_VALUE;
     }
+    mNumModeSwitchesInPolicy++;
     mUpcomingActiveMode = info;
     ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
     return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
@@ -324,8 +336,10 @@
 }
 
 void DisplayDevice::persistBrightness(bool needsComposite) {
-    if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
-        getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+    if (mStagedBrightness && mBrightness != mStagedBrightness) {
+        if (needsComposite) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+        }
         mBrightness = *mStagedBrightness;
     }
     mStagedBrightness = std::nullopt;
@@ -372,7 +386,7 @@
     }
 
     result += "\n   powerMode="s;
-    result += to_string(mPowerMode);
+    result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
     result += '\n';
 
     if (mRefreshRateConfigs) {
@@ -497,7 +511,7 @@
     }
 }
 
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
     ATRACE_CALL();
 
     LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
@@ -515,7 +529,7 @@
     }
 
     // Check if we are already at the desired mode
-    if (getActiveMode()->getId() == info.mode->getId()) {
+    if (!force && getActiveMode()->getId() == info.mode->getId()) {
         return false;
     }
 
@@ -537,6 +551,27 @@
     mDesiredActiveModeChanged = false;
 }
 
+status_t DisplayDevice::setRefreshRatePolicy(
+        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+    const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
+    const status_t setPolicyResult = overridePolicy
+            ? mRefreshRateConfigs->setOverridePolicy(policy)
+            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+
+    if (setPolicyResult == OK) {
+        const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
+
+        ALOGI("Display %s policy changed\n"
+              "Previous: {%s}\n"
+              "Current:  {%s}\n"
+              "%d mode changes were performed under the previous policy",
+              to_string(getId()).c_str(), oldPolicy.toString().c_str(),
+              policy ? policy->toString().c_str() : "null", numModeChanges);
+    }
+
+    return setPolicyResult;
+}
+
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d5d87b4..b91dece 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -181,7 +181,7 @@
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
-    hardware::graphics::composer::hal::PowerMode getPowerMode() const;
+    std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
 
@@ -204,7 +204,7 @@
         }
     };
 
-    bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+    bool setDesiredActiveMode(const ActiveModeInfo&, bool force = false) EXCLUDES(mActiveModeLock);
     std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
     void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
     ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
@@ -249,6 +249,10 @@
     nsecs_t getVsyncPeriodFromHWC() const;
     nsecs_t getRefreshTimestamp() const;
 
+    status_t setRefreshRatePolicy(
+            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
+            bool overridePolicy);
+
     // release HWC resources (if any) for removable displays
     void disconnect();
 
@@ -277,11 +281,11 @@
 
     static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
-    hardware::graphics::composer::hal::PowerMode mPowerMode =
-            hardware::graphics::composer::hal::PowerMode::OFF;
+     // allow initial power mode as null.
+    std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
     DisplayModePtr mActiveMode;
-    std::optional<float> mStagedBrightness = std::nullopt;
-    float mBrightness = -1.f;
+    std::optional<float> mStagedBrightness;
+    std::optional<float> mBrightness;
     const DisplayModes mSupportedModes;
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
@@ -303,6 +307,8 @@
     TracedOrdinal<bool> mDesiredActiveModeChanged
             GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
     ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+
+    std::atomic_int mNumModeSwitchesInPolicy = 0;
 };
 
 struct DisplayDeviceState {
@@ -360,8 +366,7 @@
     HdrCapabilities hdrCapabilities;
     int32_t supportedPerFrameMetadata{0};
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
-    hardware::graphics::composer::hal::PowerMode initialPowerMode{
-            hardware::graphics::composer::hal::PowerMode::ON};
+    std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
     bool isPrimary{false};
     DisplayModes supportedModes;
     DisplayModeId activeModeId;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index c52e96d..8364ed9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -826,25 +826,24 @@
                                    mHdrMetadata.cta8613.maxFrameAverageLightLevel}});
     }
 
-    Error error = static_cast<Error>(
-            mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas));
+    const Error error = static_cast<Error>(
+        mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas));
+    if (error != Error::NONE) {
+        return error;
+    }
 
+    std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
     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});
-        Error setMetadataBlobsError =
-                static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId,
-                                                                           perFrameMetadataBlobs));
-        if (error == Error::NONE) {
-            return setMetadataBlobsError;
-        }
     }
-    return error;
+
+    return static_cast<Error>(
+            mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, perFrameMetadataBlobs));
 }
 
 Error Layer::setDisplayFrame(const Rect& frame)
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0da8ece..a6aee1f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -738,6 +738,13 @@
             });
 }
 
+bool HWComposer::getValidateSkipped(HalDisplayId displayId) const {
+    if (mDisplayData.count(displayId) == 0) {
+        return false;
+    }
+    return mDisplayData.at(displayId).validateWasSkipped;
+}
+
 status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId,
                                         hal::HWConfigId displayModeId) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 4c0ecd8..92a8f30 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -199,6 +199,9 @@
             PhysicalDisplayId, float brightness, float brightnessNits,
             const Hwc2::Composer::DisplayBrightnessOptions&) = 0;
 
+    // Get whether the display skipped validation on the latest present
+    virtual bool getValidateSkipped(HalDisplayId displayId) const = 0;
+
     // Events handling ---------------------------------------------------------
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
@@ -397,6 +400,8 @@
 
     status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
 
+    bool getValidateSkipped(HalDisplayId displayId) const override;
+
     // Composer 2.4
     ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
     bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index b5678b4..a0350b7 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -187,7 +187,7 @@
     if (!mSupportsPowerHint.has_value()) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
-        mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+        mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession();
     }
     return *mSupportsPowerHint;
 }
@@ -196,7 +196,7 @@
     return mPowerHintSessionRunning;
 }
 
-void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) {
     if (!usePowerHintSession()) {
         ALOGV("Power hint session target duration cannot be set, skipping");
         return;
@@ -205,26 +205,45 @@
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper != nullptr) {
-            halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count());
+            halWrapper->setTargetWorkDuration(targetDuration);
         }
     }
 }
 
-void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+void PowerAdvisor::sendActualWorkDuration() {
     if (!mBootFinished || !usePowerHintSession()) {
         ALOGV("Actual work duration power hint cannot be sent, skipping");
         return;
     }
-    {
+    const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false);
+    if (actualDuration.has_value()) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper != nullptr) {
-            halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+            halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(),
+                                               systemTime());
         }
     }
 }
 
-// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
+void PowerAdvisor::sendPredictedWorkDuration() {
+    if (!mBootFinished || !usePowerHintSession()) {
+        ALOGV("Actual work duration power hint cannot be sent, skipping");
+        return;
+    }
+
+    const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true);
+
+    if (predictedDuration.has_value()) {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
+                                               systemTime());
+        }
+    }
+}
+
 void PowerAdvisor::enablePowerHint(bool enabled) {
     mPowerHintEnabled = enabled;
 }
@@ -244,6 +263,302 @@
     return mPowerHintSessionRunning;
 }
 
+void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
+    DisplayTimingData& displayData = mDisplayTimingData[displayId];
+    if (displayData.gpuEndFenceTime) {
+        nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime();
+        if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) {
+            for (auto&& [_, otherDisplayData] : mDisplayTimingData) {
+                // If the previous display started before us but ended after we should have
+                // started, then it likely delayed our start time and we must compensate for that.
+                // Displays finishing earlier should have already made their way through this call
+                // and swapped their timing into "lastValid" from "latest", so we check that here.
+                if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue;
+                if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) &&
+                    (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) {
+                    displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime;
+                    break;
+                }
+            }
+            displayData.lastValidGpuStartTime = displayData.gpuStartTime;
+            displayData.lastValidGpuEndTime = signalTime;
+        }
+    }
+    displayData.gpuEndFenceTime = std::move(fenceTime);
+    displayData.gpuStartTime = systemTime();
+}
+
+void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+                                        nsecs_t validateEndTime) {
+    DisplayTimingData& displayData = mDisplayTimingData[displayId];
+    displayData.hwcValidateStartTime = validateStartTime;
+    displayData.hwcValidateEndTime = validateEndTime;
+}
+
+void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+                                       nsecs_t presentEndTime) {
+    DisplayTimingData& displayData = mDisplayTimingData[displayId];
+    displayData.hwcPresentStartTime = presentStartTime;
+    displayData.hwcPresentEndTime = presentEndTime;
+}
+
+void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) {
+    mDisplayTimingData[displayId].skippedValidate = skipped;
+}
+
+void PowerAdvisor::setRequiresClientComposition(DisplayId displayId,
+                                                bool requiresClientComposition) {
+    mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
+}
+
+void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
+    mExpectedPresentTimes.append(expectedPresentTime);
+}
+
+void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+    mLastSfPresentEndTime = presentEndTime;
+    mLastPresentFenceTime = presentFenceTime;
+}
+
+void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
+    mFrameDelayDuration = frameDelayDuration;
+}
+
+void PowerAdvisor::setHwcPresentDelayedTime(
+        DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) {
+    mDisplayTimingData[displayId].hwcPresentDelayedTime =
+            (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime();
+}
+
+void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
+    mCommitStartTimes.append(commitStartTime);
+}
+
+void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
+    mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
+}
+
+void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
+    mDisplayIds = displayIds;
+}
+
+void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) {
+    mTotalFrameTargetDuration = targetDuration;
+}
+
+std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
+        std::optional<nsecs_t> DisplayTimingData::*sortBy) {
+    std::vector<DisplayId> sortedDisplays;
+    std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays),
+                 [&](DisplayId id) {
+                     return mDisplayTimingData.count(id) &&
+                             (mDisplayTimingData[id].*sortBy).has_value();
+                 });
+    std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) {
+        return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy);
+    });
+    return sortedDisplays;
+}
+
+std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
+    if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) {
+        return std::nullopt;
+    }
+
+    // Tracks when we finish presenting to hwc
+    nsecs_t estimatedEndTime = mCommitStartTimes[0];
+
+    // How long we spent this frame not doing anything, waiting for fences or vsync
+    nsecs_t idleDuration = 0;
+
+    // Most recent previous gpu end time in the current frame, probably from a prior display, used
+    // as the start time for the next gpu operation if it ran over time since it probably blocked
+    std::optional<nsecs_t> previousValidGpuEndTime;
+
+    // The currently estimated gpu end time for the frame,
+    // used to accumulate gpu time as we iterate over the active displays
+    std::optional<nsecs_t> estimatedGpuEndTime;
+
+    // If we're predicting at the start of the frame, we use last frame as our reference point
+    // If we're predicting at the end of the frame, we use the current frame as a reference point
+    nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
+
+    // When the prior frame should be presenting to the display
+    // If we're predicting at the start of the frame, we use last frame's expected present time
+    // If we're predicting at the end of the frame, the present fence time is already known
+    nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+
+    // The timing info for the previously calculated display, if there was one
+    std::optional<DisplayTimeline> previousDisplayReferenceTiming;
+    std::vector<DisplayId>&& displayIds =
+            getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
+    DisplayTimeline referenceTiming, estimatedTiming;
+
+    // Iterate over the displays that use hwc in the same order they are presented
+    for (DisplayId displayId : displayIds) {
+        if (mDisplayTimingData.count(displayId) == 0) {
+            continue;
+        }
+
+        auto& displayData = mDisplayTimingData.at(displayId);
+
+        // mLastPresentFenceTime should always be the time of the reference frame, since it will be
+        // the previous frame's present fence if called at the start, and current frame's if called
+        // at the end
+        referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
+
+        // If this is the first display, include the duration before hwc present starts
+        if (!previousDisplayReferenceTiming.has_value()) {
+            estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime;
+        } else { // Otherwise add the time since last display's hwc present finished
+            estimatedEndTime += referenceTiming.hwcPresentStartTime -
+                    previousDisplayReferenceTiming->hwcPresentEndTime;
+        }
+
+        // Late hint can re-use reference timing here since it's estimating its own reference frame
+        estimatedTiming = earlyHint
+                ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime,
+                                                                estimatedEndTime)
+                : referenceTiming;
+
+        // Update predicted present finish time with this display's present time
+        estimatedEndTime = estimatedTiming.hwcPresentEndTime;
+
+        // Track how long we spent waiting for the fence, can be excluded from the timing estimate
+        idleDuration += estimatedTiming.probablyWaitsForPresentFence
+                ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
+                : 0;
+
+        // Track how long we spent waiting to present, can be excluded from the timing estimate
+        idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration;
+
+        // Estimate the reference frame's gpu timing
+        auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
+        if (gpuTiming.has_value()) {
+            previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
+
+            // Estimate the prediction frame's gpu end time from the reference frame
+            estimatedGpuEndTime =
+                    std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) +
+                    gpuTiming->duration;
+        }
+        previousDisplayReferenceTiming = referenceTiming;
+    }
+    ATRACE_INT64("Idle duration", idleDuration);
+
+    nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
+
+    // Don't count time spent idly waiting in the estimate as we could do more work in that time
+    estimatedEndTime -= idleDuration;
+    estimatedFlingerEndTime -= idleDuration;
+
+    // We finish the frame when both present and the gpu are done, so wait for the later of the two
+    // Also add the frame delay duration since the target did not move while we were delayed
+    nsecs_t totalDuration = mFrameDelayDuration +
+            std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
+
+    // We finish SurfaceFlinger when post-composition finishes, so add that in here
+    nsecs_t flingerDuration =
+            estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+
+    // Combine the two timings into a single normalized one
+    nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+
+    return std::make_optional(combinedDuration);
+}
+
+nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) {
+    nsecs_t targetDuration;
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        targetDuration = *getPowerHal()->getTargetWorkDuration();
+    }
+    if (!mTotalFrameTargetDuration.has_value()) return flingerDuration;
+
+    // Normalize total to the flinger target (vsync period) since that's how often we actually send
+    // hints
+    nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration;
+    return std::max(flingerDuration, normalizedTotalDuration);
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference(
+        nsecs_t fenceTime, nsecs_t displayStartTime) {
+    DisplayTimeline estimated;
+    estimated.hwcPresentStartTime = displayStartTime;
+
+    // We don't predict waiting for vsync alignment yet
+    estimated.hwcPresentDelayDuration = 0;
+
+    // How long we expect to run before we start waiting for the fence
+    // For now just re-use last frame's post-present duration and assume it will not change much
+    // Excludes time spent waiting for vsync since that's not going to be consistent
+    estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime +
+            (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
+    estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime;
+    estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration +
+            (estimated.probablyWaitsForPresentFence ? fenceTime
+                                                    : estimated.presentFenceWaitStartTime);
+    return estimated;
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline(
+        nsecs_t fenceTime) {
+    DisplayTimeline timeline;
+    // How long between calling hwc present and trying to wait on the fence
+    const nsecs_t fenceWaitStartDelay =
+            (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated)
+                    .count();
+
+    // Did our reference frame wait for an appropriate vsync before calling into hwc
+    const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() &&
+            *hwcPresentDelayedTime > *hwcPresentStartTime &&
+            *hwcPresentDelayedTime < *hwcPresentEndTime;
+
+    // Use validate start here if we skipped it because we did validate + present together
+    timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime;
+
+    // Use validate end here if we skipped it because we did validate + present together
+    timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime;
+
+    // How long hwc present was delayed waiting for the next appropriate vsync
+    timeline.hwcPresentDelayDuration =
+            (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
+    // When we started waiting for the present fence after calling into hwc present
+    timeline.presentFenceWaitStartTime =
+            timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
+    timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime &&
+            fenceTime < timeline.hwcPresentEndTime;
+
+    // How long we ran after we finished waiting for the fence but before hwc present finished
+    timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime -
+            (timeline.probablyWaitsForPresentFence ? fenceTime
+                                                   : timeline.presentFenceWaitStartTime);
+    return timeline;
+}
+
+std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
+        std::optional<nsecs_t> previousEnd) {
+    if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
+        return std::nullopt;
+    }
+    const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime);
+    const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime();
+    nsecs_t gpuDuration = 0;
+    if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID &&
+        latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) {
+        // If we know how long the most recent gpu duration was, use that
+        gpuDuration = latestGpuEndTime - latestGpuStartTime;
+    } else if (lastValidGpuEndTime.has_value()) {
+        // If we don't have the fence data, use the most recent information we do have
+        gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime;
+        if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) {
+            // If pending but went over the previous duration, use current time as the end
+            gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime);
+        }
+    }
+    return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime};
+}
+
 class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
 public:
     HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -325,6 +640,9 @@
     }
 
     mSupportsPowerHint = checkPowerHintSessionSupported();
+
+    // Currently set to 0 to disable rate limiter by default
+    mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
 }
 
 AidlPowerHalWrapper::~AidlPowerHalWrapper() {
@@ -332,7 +650,7 @@
         mPowerHintSession->close();
         mPowerHintSession = nullptr;
     }
-};
+}
 
 std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
     // This only waits if the service is actually declared
@@ -370,7 +688,7 @@
     return ret.isOk();
 }
 
-// only version 2+ of the aidl supports power hint sessions, hidl has no support
+// Only version 2+ of the aidl supports power hint sessions, hidl has no support
 bool AidlPowerHalWrapper::supportsPowerHintSession() {
     return mSupportsPowerHint;
 }
@@ -424,30 +742,14 @@
     return isPowerHintSessionRunning();
 }
 
-bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) {
-    if (targetDurationNanos <= 0) {
-        return false;
-    }
-    // report if the change in target from our last submission to now exceeds the threshold
-    return abs(1.0 -
-               static_cast<double>(mLastTargetDurationSent) /
-                       static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent;
-}
-
-void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
+void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) {
     ATRACE_CALL();
-    mTargetDuration = targetDurationNanos;
-    if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
-    if (!sNormalizeTarget && isPowerHintSessionRunning() &&
-        shouldSetTargetDuration(targetDurationNanos)) {
-        if (mLastActualDurationSent.has_value()) {
-            // update the error term here since we are actually sending an update to powerhal
-            if (sTraceHintSessionData)
-                ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent);
-        }
-        ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos);
-        mLastTargetDurationSent = targetDurationNanos;
-        auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+    mTargetDuration = targetDuration;
+    if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration);
+    if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+        ALOGV("Sending target time: %" PRId64 "ns", targetDuration);
+        mLastTargetDurationSent = targetDuration;
+        auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration);
         if (!ret.isOk()) {
             ALOGW("Failed to set power hint target work duration with error: %s",
                   ret.exceptionMessage().c_str());
@@ -456,8 +758,8 @@
     }
 }
 
-bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
-    // report if we have never reported before or are approaching a stale session
+bool AidlPowerHalWrapper::shouldReportActualDurations() {
+    // Report if we have never reported before or are approaching a stale session
     if (!mLastActualDurationSent.has_value() ||
         (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
         return true;
@@ -466,65 +768,42 @@
     if (!mActualDuration.has_value()) {
         return false;
     }
-
-    // duration of most recent timing
-    const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
-    // duration of the last timing actually reported to the powerhal
-    const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
-
-    // report if the change in duration from then to now exceeds the threshold
-    return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
-            kAllowedActualDeviationPercent;
+    // Report if the change in actual duration exceeds the threshold
+    return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation;
 }
 
-void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
-                                                 nsecs_t timeStampNanos) {
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) {
     ATRACE_CALL();
 
-    if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+    if (actualDuration < 0 || !isPowerHintSessionRunning()) {
         ALOGV("Failed to send actual work duration, skipping");
         return;
     }
-    nsecs_t reportedDuration = actualDurationNanos;
+    const nsecs_t reportedDuration = actualDuration;
 
-    // normalize the sent values to a pre-set target
-    if (sNormalizeTarget) {
-        reportedDuration += mLastTargetDurationSent - mTargetDuration;
-    } else {
-        // when target duration change is within deviation and not updated, adjust the actual
-        // duration proportionally based on the difference, e.g. if new target is 5ms longer than
-        // last reported but actual duration is the same as last target, we want to report a smaller
-        // actual work duration now to indicate that we are overshooting
-        if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) {
-            reportedDuration =
-                    static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) /
-                                         mTargetDuration * actualDurationNanos);
-            mActualDuration = reportedDuration;
-        }
-    }
     mActualDuration = reportedDuration;
     WorkDuration duration;
     duration.durationNanos = reportedDuration;
-    duration.timeStampNanos = timeStampNanos;
+    duration.timeStampNanos = timestamp;
     mPowerHintQueue.push_back(duration);
 
     if (sTraceHintSessionData) {
-        ATRACE_INT64("Measured duration", actualDurationNanos);
-        ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos);
+        ATRACE_INT64("Measured duration", actualDuration);
+        ATRACE_INT64("Target error term", actualDuration - mTargetDuration);
 
         ATRACE_INT64("Reported duration", reportedDuration);
         ATRACE_INT64("Reported target", mLastTargetDurationSent);
-        ATRACE_INT64("Reported target error term", mLastTargetDurationSent - reportedDuration);
+        ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent);
     }
 
     ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
           " with error: %" PRId64,
-          reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration);
+          reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent);
 
     // This rate limiter queues similar duration reports to the powerhal into
     // batches to avoid excessive binder calls. The criteria to send a given batch
     // are outlined in shouldReportActualDurationsNow()
-    if (shouldReportActualDurationsNow()) {
+    if (shouldReportActualDurations()) {
         ALOGV("Sending hint update batch");
         mLastActualReportTimestamp = systemTime();
         auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
@@ -534,8 +813,8 @@
             mShouldReconnectHal = true;
         }
         mPowerHintQueue.clear();
-        // we save the non-normalized value here to detect % changes
-        mLastActualDurationSent = reportedDuration;
+        // We save the actual duration here for rate limiting
+        mLastActualDurationSent = actualDuration;
     }
 }
 
@@ -551,76 +830,73 @@
     return mTargetDuration;
 }
 
+void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) {
+    mAllowedActualDeviation = allowedDeviation;
+}
+
 const bool AidlPowerHalWrapper::sTraceHintSessionData =
         base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
 
-const bool AidlPowerHalWrapper::sNormalizeTarget =
-        base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false);
-
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
-    static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
-    static bool sHasHal = true;
-
-    if (!sHasHal) {
+    if (!mHasHal) {
         return nullptr;
     }
 
-    // grab old hint session values before we destroy any existing wrapper
+    // Grab old hint session values before we destroy any existing wrapper
     std::vector<int32_t> oldPowerHintSessionThreadIds;
     std::optional<int64_t> oldTargetWorkDuration;
 
-    if (sHalWrapper != nullptr) {
-        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
-        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+    if (mHalWrapper != nullptr) {
+        oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
+        oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
     }
 
     // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
-        sHalWrapper = nullptr;
+        mHalWrapper = nullptr;
         mReconnectPowerHal = false;
     }
 
-    if (sHalWrapper != nullptr) {
-        auto wrapper = sHalWrapper.get();
-        // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+    if (mHalWrapper != nullptr) {
+        auto wrapper = mHalWrapper.get();
+        // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
         if (!wrapper->shouldReconnectHAL()) {
             return wrapper;
         }
         ALOGD("Reconnecting Power HAL");
-        sHalWrapper = nullptr;
+        mHalWrapper = nullptr;
     }
 
-    // at this point, we know for sure there is no running session
+    // At this point, we know for sure there is no running session
     mPowerHintSessionRunning = false;
 
     // First attempt to connect to the AIDL Power HAL
-    sHalWrapper = AidlPowerHalWrapper::connect();
+    mHalWrapper = AidlPowerHalWrapper::connect();
 
     // If that didn't succeed, attempt to connect to the HIDL Power HAL
-    if (sHalWrapper == nullptr) {
-        sHalWrapper = HidlPowerHalWrapper::connect();
+    if (mHalWrapper == nullptr) {
+        mHalWrapper = HidlPowerHalWrapper::connect();
     } else {
         ALOGD("Successfully connecting AIDL Power HAL");
-        // if AIDL, pass on any existing hint session values
-        // thread ids always safe to set
-        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
-        // only set duration and start if duration is defined
+        // If AIDL, pass on any existing hint session values
+        mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+        // Only set duration and start if duration is defined
         if (oldTargetWorkDuration.has_value()) {
-            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
-            // only start if possible to run and both threadids and duration are defined
+            mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+            // Only start if possible to run and both threadids and duration are defined
             if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
-                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+                mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
             }
         }
     }
 
     // If we make it to this point and still don't have a HAL, it's unlikely we
     // will, so stop trying
-    if (sHalWrapper == nullptr) {
-        sHasHal = false;
+    if (mHalWrapper == nullptr) {
+        mHasHal = false;
     }
 
-    return sHalWrapper.get();
+    return mHalWrapper.get();
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 7c10e19..6e25f78 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -18,11 +18,15 @@
 
 #include <atomic>
 #include <chrono>
+#include <unordered_map>
 #include <unordered_set>
 
+#include <ui/DisplayId.h>
+#include <ui/FenceTime.h>
 #include <utils/Mutex.h>
 
 #include <android/hardware/power/IPower.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
 
@@ -44,13 +48,50 @@
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual bool isUsingExpensiveRendering() = 0;
     virtual void notifyDisplayUpdateImminent() = 0;
+    // Checks both if it supports and if it's enabled
     virtual bool usePowerHintSession() = 0;
     virtual bool supportsPowerHintSession() = 0;
     virtual bool isPowerHintSessionRunning() = 0;
-    virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
-    virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
+    // Sends a power hint that updates to the target work duration for the frame
+    virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+    // Sends a power hint for the actual known work duration at the end of the frame
+    virtual void sendActualWorkDuration() = 0;
+    // Sends a power hint for the upcoming frame predicted from previous frame timing
+    virtual void sendPredictedWorkDuration() = 0;
+    // Sets whether the power hint session is enabled
     virtual void enablePowerHint(bool enabled) = 0;
+    // Initializes the power hint session
     virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
+    // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
+    virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
+    // Reports the start and end times of a hwc validate call this frame for a given display
+    virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+                                      nsecs_t validateEndTime) = 0;
+    // Reports the start and end times of a hwc present call this frame for a given display
+    virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+                                     nsecs_t presentEndTime) = 0;
+    // Reports the expected time that the current frame will present to the display
+    virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+    // Reports the most recent present fence time and end time once known
+    virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0;
+    // Reports whether a display used client composition this frame
+    virtual void setRequiresClientComposition(DisplayId displayId,
+                                              bool requiresClientComposition) = 0;
+    // Reports whether a given display skipped validation this frame
+    virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
+    // Reports when a hwc present is delayed, and the time that it will resume
+    virtual void setHwcPresentDelayedTime(
+            DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0;
+    // Reports the start delay for SurfaceFlinger this frame
+    virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0;
+    // Reports the SurfaceFlinger commit start time this frame
+    virtual void setCommitStart(nsecs_t commitStartTime) = 0;
+    // Reports the SurfaceFlinger composite end time this frame
+    virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0;
+    // Reports the list of the currently active displays
+    virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
+    // Sets the target duration for the entire pipeline including the gpu
+    virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0;
 };
 
 namespace impl {
@@ -70,12 +111,11 @@
         virtual void restartPowerHintSession() = 0;
         virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
         virtual bool startPowerHintSession() = 0;
-        virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
-        virtual void sendActualWorkDuration(int64_t actualDurationNanos,
-                                            nsecs_t timeStampNanos) = 0;
+        virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+        virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0;
         virtual bool shouldReconnectHAL() = 0;
         virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
-        virtual std::optional<int64_t> getTargetWorkDuration() = 0;
+        virtual std::optional<nsecs_t> getTargetWorkDuration() = 0;
     };
 
     PowerAdvisor(SurfaceFlinger& flinger);
@@ -89,26 +129,43 @@
     bool usePowerHintSession() override;
     bool supportsPowerHintSession() override;
     bool isPowerHintSessionRunning() override;
-    void setTargetWorkDuration(int64_t targetDurationNanos) override;
-    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
+    void setTargetWorkDuration(nsecs_t targetDuration) override;
+    void sendActualWorkDuration() override;
+    void sendPredictedWorkDuration() override;
     void enablePowerHint(bool enabled) override;
     bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
+    void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
+    void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime,
+                              nsecs_t validateEndTime) override;
+    void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+                             nsecs_t presentEndTime) override;
+    void setSkippedValidate(DisplayId displayId, bool skipped) override;
+    void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
+    void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
+    void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
+    void setHwcPresentDelayedTime(
+            DisplayId displayId,
+            std::chrono::steady_clock::time_point earliestFrameStartTime) override;
+
+    void setFrameDelay(nsecs_t frameDelayDuration) override;
+    void setCommitStart(nsecs_t commitStartTime) override;
+    void setCompositeEnd(nsecs_t compositeEndTime) override;
+    void setDisplays(std::vector<DisplayId>& displayIds) override;
+    void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
 
 private:
+    friend class PowerAdvisorTest;
+
+    // Tracks if powerhal exists
+    bool mHasHal = true;
+    // Holds the hal wrapper for getPowerHal
+    std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
+
     HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
     bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
     std::mutex mPowerHalMutex;
 
     std::atomic_bool mBootFinished = false;
-    std::optional<bool> mPowerHintEnabled;
-    std::optional<bool> mSupportsPowerHint;
-    bool mPowerHintSessionRunning = false;
-
-    // An adjustable safety margin which moves the "target" earlier to allow flinger to
-    // go a bit over without dropping a frame, especially since we can't measure
-    // the exact time HWC finishes composition so "actual" durations are measured
-    // from the end of present() instead, which is a bit later.
-    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
@@ -117,6 +174,110 @@
     std::atomic_bool mSendUpdateImminent = true;
     std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
     std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
+
+    // Higher-level timing data used for estimation
+    struct DisplayTimeline {
+        // The start of hwc present, or the start of validate if it happened there instead
+        nsecs_t hwcPresentStartTime = -1;
+        // The end of hwc present or validate, whichever one actually presented
+        nsecs_t hwcPresentEndTime = -1;
+        // How long the actual hwc present was delayed after hwcPresentStartTime
+        nsecs_t hwcPresentDelayDuration = 0;
+        // When we think we started waiting for the present fence after calling into hwc present and
+        // after potentially waiting for the earliest present time
+        nsecs_t presentFenceWaitStartTime = -1;
+        // How long we ran after we finished waiting for the fence but before hwc present finished
+        nsecs_t postPresentFenceHwcPresentDuration = 0;
+        // Are we likely to have waited for the present fence during composition
+        bool probablyWaitsForPresentFence = false;
+        // Estimate one frame's timeline from that of a previous frame
+        DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
+    };
+
+    struct GpuTimeline {
+        nsecs_t duration = 0;
+        nsecs_t startTime = -1;
+    };
+
+    // Power hint session data recorded from the pipeline
+    struct DisplayTimingData {
+        std::unique_ptr<FenceTime> gpuEndFenceTime;
+        std::optional<nsecs_t> gpuStartTime;
+        std::optional<nsecs_t> lastValidGpuEndTime;
+        std::optional<nsecs_t> lastValidGpuStartTime;
+        std::optional<nsecs_t> hwcPresentStartTime;
+        std::optional<nsecs_t> hwcPresentEndTime;
+        std::optional<nsecs_t> hwcValidateStartTime;
+        std::optional<nsecs_t> hwcValidateEndTime;
+        std::optional<nsecs_t> hwcPresentDelayedTime;
+        bool usedClientComposition = false;
+        bool skippedValidate = false;
+        // Calculate high-level timing milestones from more granular display timing data
+        DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime);
+        // Estimate the gpu duration for a given display from previous gpu timing data
+        std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd);
+    };
+
+    template <class T, size_t N>
+    class RingBuffer {
+        std::array<T, N> elements = {};
+        size_t mIndex = 0;
+        size_t numElements = 0;
+
+    public:
+        void append(T item) {
+            mIndex = (mIndex + 1) % N;
+            numElements = std::min(N, numElements + 1);
+            elements[mIndex] = item;
+        }
+        bool isFull() const { return numElements == N; }
+        // Allows access like [0] == current, [-1] = previous, etc..
+        T& operator[](int offset) {
+            size_t positiveOffset =
+                    static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
+            return elements[(mIndex + positiveOffset) % N];
+        }
+    };
+
+    // Filter and sort the display ids by a given property
+    std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy);
+    // Estimates a frame's total work duration including gpu time.
+    // Runs either at the beginning or end of a frame, using the most recent data available
+    std::optional<nsecs_t> estimateWorkDuration(bool earlyHint);
+    // There are two different targets and actual work durations we care about,
+    // this normalizes them together and takes the max of the two
+    nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration);
+
+    std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
+
+    // Current frame's delay
+    nsecs_t mFrameDelayDuration = 0;
+    // Last frame's post-composition duration
+    nsecs_t mLastPostcompDuration = 0;
+    // Buffer of recent commit start times
+    RingBuffer<nsecs_t, 2> mCommitStartTimes;
+    // Buffer of recent expected present times
+    RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
+    // Most recent present fence time, set at the end of the frame once known
+    nsecs_t mLastPresentFenceTime = -1;
+    // Most recent present fence time, set at the end of the frame once known
+    nsecs_t mLastSfPresentEndTime = -1;
+    // Target for the entire pipeline including gpu
+    std::optional<nsecs_t> mTotalFrameTargetDuration;
+    // Updated list of display IDs
+    std::vector<DisplayId> mDisplayIds;
+
+    std::optional<bool> mPowerHintEnabled;
+    std::optional<bool> mSupportsPowerHint;
+    bool mPowerHintSessionRunning = false;
+
+    // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
+    // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
+    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
+
+    // How long we expect hwc to run after the present call until it waits for the fence
+    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us;
+    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us;
 };
 
 class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
@@ -133,50 +294,50 @@
     void restartPowerHintSession() override;
     void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
     bool startPowerHintSession() override;
-    void setTargetWorkDuration(int64_t targetDurationNanos) override;
-    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override;
+    void setTargetWorkDuration(nsecs_t targetDuration) override;
+    void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override;
     bool shouldReconnectHAL() override;
     std::vector<int32_t> getPowerHintSessionThreadIds() override;
-    std::optional<int64_t> getTargetWorkDuration() override;
+    std::optional<nsecs_t> getTargetWorkDuration() override;
 
 private:
+    friend class AidlPowerHalWrapperTest;
+
     bool checkPowerHintSessionSupported();
     void closePowerHintSession();
-    bool shouldReportActualDurationsNow();
-    bool shouldSetTargetDuration(int64_t targetDurationNanos);
+    bool shouldReportActualDurations();
+
+    // Used for testing
+    void setAllowedActualDeviation(nsecs_t);
 
     const sp<hardware::power::IPower> mPowerHal = nullptr;
     bool mHasExpensiveRendering = false;
     bool mHasDisplayUpdateImminent = false;
     // Used to indicate an error state and need for reconstruction
     bool mShouldReconnectHal = false;
-    // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+
+    // Power hint session data
+
+    // Concurrent access for this is protected by mPowerHalMutex
     sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
     // Queue of actual durations saved to report
     std::vector<hardware::power::WorkDuration> mPowerHintQueue;
-    // The latest un-normalized values we have received for target and actual
-    int64_t mTargetDuration = kDefaultTarget.count();
-    std::optional<int64_t> mActualDuration;
+    // The latest values we have received for target and actual
+    nsecs_t mTargetDuration = kDefaultTarget.count();
+    std::optional<nsecs_t> mActualDuration;
     // The list of thread ids, stored so we can restart the session from this class if needed
     std::vector<int32_t> mPowerHintThreadIds;
-    bool mSupportsPowerHint;
+    bool mSupportsPowerHint = false;
     // Keep track of the last messages sent for rate limiter change detection
-    std::optional<int64_t> mLastActualDurationSent;
-    // timestamp of the last report we sent, used to avoid stale sessions
-    int64_t mLastActualReportTimestamp = 0;
-    int64_t mLastTargetDurationSent = kDefaultTarget.count();
-    // Whether to normalize all the actual values as error terms relative to a constant target
-    // This saves a binder call by not setting the target, and should not affect the pid values
-    static const bool sNormalizeTarget;
+    std::optional<nsecs_t> mLastActualDurationSent;
+    // Timestamp of the last report we sent, used to avoid stale sessions
+    nsecs_t mLastActualReportTimestamp = 0;
+    nsecs_t mLastTargetDurationSent = kDefaultTarget.count();
+    // Max amount the error term can vary without causing an actual value report
+    nsecs_t mAllowedActualDeviation = -1;
     // Whether we should emit ATRACE_INT data for hint sessions
     static const bool sTraceHintSessionData;
-
-    // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
-    static constexpr double kAllowedActualDeviationPercent = 0.1;
-    // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%)
-    static constexpr double kAllowedTargetDeviationPercent = 0.1;
-    // Target used for init and normalization, the actual value does not really matter
-    static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+    static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
     // Amount of time after the last message was sent before the session goes stale
     // actually 100ms but we use 80 here to ideally avoid going stale
     static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a2305af..3611dea 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -314,12 +314,12 @@
     virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
 
     // Sets the max number of display frames that can be stored. Called by SF backdoor.
-    virtual void setMaxDisplayFrames(uint32_t size);
+    virtual void setMaxDisplayFrames(uint32_t size) = 0;
 
     // Computes the historical fps for the provided set of layer IDs
     // The fps is compted from the linear timeline of present timestamps for DisplayFrames
     // containing at least one layer ID.
-    virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+    virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0;
 
     // Restores the max number of display frames to default. Called by SF backdoor.
     virtual void reset() = 0;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 38662bd..a31cdf0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -72,6 +72,7 @@
 #include "LayerProtoHelper.h"
 #include "LayerRejecter.h"
 #include "MonitoredProducer.h"
+#include "MutexUtils.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
@@ -81,6 +82,7 @@
 namespace android {
 namespace {
 constexpr int kDumpTableRowLength = 159;
+const ui::Transform kIdentityTransform;
 } // namespace
 
 using namespace ftl::flag_operators;
@@ -253,10 +255,12 @@
     auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
     std::sort(layersInTree.begin(), layersInTree.end());
 
-    traverse(LayerVector::StateSet::Current, [&](Layer* layer) {
-        layer->removeFromCurrentState();
-        layer->removeRelativeZ(layersInTree);
-    });
+    REQUIRE_MUTEX(mFlinger->mStateLock);
+    traverse(LayerVector::StateSet::Current,
+             [&](Layer* layer) REQUIRES(layer->mFlinger->mStateLock) {
+                 layer->removeFromCurrentState();
+                 layer->removeRelativeZ(layersInTree);
+             });
 }
 
 void Layer::addToCurrentState() {
@@ -405,7 +409,7 @@
     const auto& drawingState{getDrawingState()};
     const auto alpha = static_cast<float>(getAlpha());
     const bool opaque = isOpaque(drawingState);
-    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+    const bool usesRoundedCorners = hasRoundedCorners();
 
     auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
     if (!opaque || alpha != 1.0f) {
@@ -481,7 +485,7 @@
     compositionState->hasProtectedContent = isProtected();
     compositionState->dimmingEnabled = isDimmingEnabled();
 
-    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+    const bool usesRoundedCorners = hasRoundedCorners();
 
     compositionState->isOpaque =
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -935,10 +939,12 @@
         mFlinger->mLayersAdded = true;
         // set up SF to handle added color layer
         if (isRemovedFromCurrentState()) {
+            MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
             mDrawingState.bgColorLayer->onRemovedFromCurrentState();
         }
         mFlinger->setTransactionFlags(eTransactionNeeded);
     } else if (mDrawingState.bgColorLayer && alpha == 0) {
+        MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
         mDrawingState.bgColorLayer->reparent(nullptr);
         mDrawingState.bgColorLayer = nullptr;
         return true;
@@ -1916,27 +1922,22 @@
     const auto& parent = mDrawingParent.promote();
     if (parent != nullptr) {
         parentSettings = parent->getRoundedCornerState();
-        if (parentSettings.radius > 0) {
+        if (parentSettings.hasRoundedCorners()) {
             ui::Transform t = getActiveTransform(getDrawingState());
             t = t.inverse();
             parentSettings.cropRect = t.transform(parentSettings.cropRect);
-            // The rounded corners shader only accepts 1 corner radius for performance reasons,
-            // 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.
-            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]);
-            parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+            parentSettings.radius.x *= t.getScaleX();
+            parentSettings.radius.y *= t.getScaleY();
         }
     }
 
     // Get layer settings
     Rect layerCropRect = getCroppedBufferSize(getDrawingState());
-    const float radius = getDrawingState().cornerRadius;
+    const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
     RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
-    const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+    const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
 
-    if (layerSettingsValid && parentSettings.radius > 0) {
+    if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
         // If the parent and the layer have rounded corner settings, use the parent settings if the
         // parent crop is entirely inside the layer crop.
         // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -1950,7 +1951,7 @@
         }
     } else if (layerSettingsValid) {
         return layerSettings;
-    } else if (parentSettings.radius > 0) {
+    } else if (parentSettings.hasRoundedCorners()) {
         return parentSettings;
     }
     return {};
@@ -2071,7 +2072,8 @@
     layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
     layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
-    layerInfo->set_corner_radius(getRoundedCornerState().radius);
+    layerInfo->set_corner_radius(
+            (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
     layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
     layerInfo->set_is_trusted_overlay(isTrustedOverlay());
     LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
@@ -2164,7 +2166,8 @@
     if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
         WindowInfo info;
         if (useDrawing) {
-            info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
+            info = fillInputInfo(
+                    InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
         } else {
             info = state.inputInfo;
         }
@@ -2371,7 +2374,7 @@
     }
 }
 
-WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
+WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
     if (!hasInputInfo()) {
         mDrawingState.inputInfo.name = getName();
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
@@ -2380,22 +2383,22 @@
         mDrawingState.inputInfo.displayId = getLayerStack().id;
     }
 
+    const ui::Transform& displayTransform =
+            displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
+
     WindowInfo info = mDrawingState.inputInfo;
     info.id = sequence;
     info.displayId = getLayerStack().id;
 
     fillInputFrameInfo(info, displayTransform);
 
-    // 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.
-    const bool visible = hasInputInfo() ? canReceiveInput() : isVisible();
-    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    if (displayArgs.transform == nullptr) {
+        // Do not let the window receive touches if it is not associated with a valid display
+        // transform. We still allow the window to receive keys and prevent ANRs.
+        info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
+    }
+
+    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());
 
     info.alpha = getAlpha();
     fillTouchOcclusionMode(info);
@@ -2403,7 +2406,7 @@
 
     // If the window will be blacked out on a display because the display does not have the secure
     // flag and the layer has the secure flag set, then drop input.
-    if (!displayIsSecure && isSecure()) {
+    if (!displayArgs.isSecure && isSecure()) {
         info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
     }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 24abad9..5ffcabf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -134,13 +134,14 @@
 
     struct RoundedCornerState {
         RoundedCornerState() = default;
-        RoundedCornerState(FloatRect cropRect, float radius)
+        RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
               : cropRect(cropRect), radius(radius) {}
 
         // Rounded rectangle in local layer coordinate space.
         FloatRect cropRect = FloatRect();
         // Radius of the rounded rectangle.
-        float radius = 0.0f;
+        vec2 radius;
+        bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
     };
 
     using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -408,7 +409,7 @@
     virtual ui::LayerStack getLayerStack() const;
     virtual bool setMetadata(const LayerMetadata& data);
     virtual void setChildrenDrawingParent(const sp<Layer>&);
-    virtual bool reparent(const sp<IBinder>& newParentHandle);
+    virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock);
     virtual bool setColorTransform(const mat4& matrix);
     virtual mat4 getColorTransform() const;
     virtual bool hasColorTransform() const;
@@ -432,7 +433,8 @@
     virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
     virtual bool setTransactionCompletedListeners(
             const std::vector<sp<CallbackHandle>>& /*handles*/);
-    virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
+    virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
+            REQUIRES(mFlinger->mStateLock);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
     virtual bool setDimmingEnabled(const bool dimmingEnabled);
     virtual bool setFrameRateSelectionPriority(int32_t priority);
@@ -470,6 +472,21 @@
     virtual bool canReceiveInput() const;
 
     /*
+     * Whether or not the layer should be considered visible for input calculations.
+     */
+    virtual bool isVisibleForInput() const {
+        // 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.
+        return hasInputInfo() ? canReceiveInput() : isVisible();
+    }
+
+    /*
      * isProtected - true if the layer may contain protected contents in the
      * GRALLOC_USAGE_PROTECTED sense.
      */
@@ -596,7 +613,7 @@
     // corner crop does not intersect with its own rounded corner crop.
     virtual RoundedCornerState getRoundedCornerState() const;
 
-    bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+    bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
 
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     /**
@@ -714,13 +731,13 @@
     /*
      * Remove from current state and mark for removal.
      */
-    void removeFromCurrentState();
+    void removeFromCurrentState() REQUIRES(mFlinger->mStateLock);
 
     /*
      * 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();
+    void onRemovedFromCurrentState() REQUIRES(mFlinger->mStateLock);
 
     /*
      * Called when the layer is added back to the current state list.
@@ -844,7 +861,11 @@
     bool getPremultipledAlpha() const;
     void setInputInfo(const gui::WindowInfo& info);
 
-    gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+    struct InputDisplayArgs {
+        const ui::Transform* transform = nullptr;
+        bool isSecure = false;
+    };
+    gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
 
     /**
      * Returns whether this layer has an explicitly set input-info.
@@ -894,6 +915,9 @@
 
     virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; }
 
+    // Exposed so SurfaceFlinger can assert that it's held
+    const sp<SurfaceFlinger> mFlinger;
+
 protected:
     friend class impl::SurfaceInterceptor;
 
@@ -969,9 +993,6 @@
      */
     virtual Rect getInputBounds() const;
 
-    // constant
-    sp<SurfaceFlinger> mFlinger;
-
     bool mPremultipliedAlpha{true};
     const std::string mName;
     const std::string mTransactionName{"TX - " + mName};
diff --git a/services/surfaceflinger/MutexUtils.h b/services/surfaceflinger/MutexUtils.h
index f8be6f3..58f7cb4 100644
--- a/services/surfaceflinger/MutexUtils.h
+++ b/services/surfaceflinger/MutexUtils.h
@@ -50,4 +50,14 @@
     const status_t status;
 };
 
+// Require, under penalty of compilation failure, that the compiler thinks that a mutex is held.
+#define REQUIRE_MUTEX(expr) ([]() REQUIRES(expr) {})()
+
+// Tell the compiler that we know that a mutex is held.
+#define ASSERT_MUTEX(expr) ([]() ASSERT_CAPABILITY(expr) {})()
+
+// Specify that one mutex is an alias for another.
+// (e.g. SurfaceFlinger::mStateLock and Layer::mFlinger->mStateLock)
+#define MUTEX_ALIAS(held, alias) (REQUIRE_MUTEX(held), ASSERT_MUTEX(alias))
+
 } // namespace android
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 6011d0d..4e7da82 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -4,5 +4,7 @@
 lpy@google.com
 pdwilliams@google.com
 racarr@google.com
+ramindani@google.com
+rnlee@google.com
 scroggo@google.com
 vishnun@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2..a9180d4 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
 
 #include <algorithm>
 
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
 #include "Client.h"
 #include "Layer.h"
+#include "RefreshRateOverlay.h"
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@
 
 } // namespace
 
+SurfaceControlHolder::~SurfaceControlHolder() {
+    // Hand the sp<SurfaceControl> to the helper thread to release the last
+    // reference. This makes sure that the SurfaceControl is destructed without
+    // SurfaceFlinger::mStateLock held.
+    BackgroundExecutor::getInstance().sendCallbacks(
+            {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
 void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
                                                          SkCanvas& canvas) {
     const SkRect rect = [&]() {
@@ -210,21 +219,27 @@
     return buffers;
 }
 
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+    sp<SurfaceControl> surfaceControl =
+            SurfaceComposerClient::getDefault()
+                    ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+                                    PIXEL_FORMAT_RGBA_8888,
+                                    ISurfaceComposerClient::eFXSurfaceBufferState);
+    return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
 RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
       : mFpsRange(fpsRange),
         mShowSpinner(showSpinner),
-        mSurfaceControl(SurfaceComposerClient::getDefault()
-                                ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
-                                                kBufferHeight, PIXEL_FORMAT_RGBA_8888,
-                                                ISurfaceComposerClient::eFXSurfaceBufferState)) {
+        mSurfaceControl(createSurfaceControlHolder()) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
     }
 
-    createTransaction(mSurfaceControl)
-            .setLayer(mSurfaceControl, INT32_MAX - 2)
-            .setTrustedOverlay(mSurfaceControl, true)
+    createTransaction(mSurfaceControl->get())
+            .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+            .setTrustedOverlay(mSurfaceControl->get(), true)
             .apply();
 }
 
@@ -233,7 +248,7 @@
     if (!mSurfaceControl) return kNoBuffers;
 
     const auto transformHint =
-            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
 
     // Tell SurfaceFlinger about the pre-rotation on the buffer.
     const auto transform = [&] {
@@ -247,7 +262,9 @@
         }
     }();
 
-    createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+    createTransaction(mSurfaceControl->get())
+            .setTransform(mSurfaceControl->get(), transform)
+            .apply();
 
     BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@
     Rect frame((3 * width) >> 4, height >> 5);
     frame.offsetBy(width >> 5, height >> 4);
 
-    createTransaction(mSurfaceControl)
-            .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
-                       frame.getHeight() / static_cast<float>(kBufferHeight))
-            .setPosition(mSurfaceControl, frame.left, frame.top)
+    createTransaction(mSurfaceControl->get())
+            .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+                       0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+            .setPosition(mSurfaceControl->get(), frame.left, frame.top)
             .apply();
 }
 
 void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
-    createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+    createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
 void RefreshRateOverlay::changeRefreshRate(Fps fps) {
     mCurrentFps = fps;
     const auto buffer = getOrCreateBuffers(fps)[mFrame];
-    createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@
     const auto& buffers = getOrCreateBuffers(*mCurrentFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
-    createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36..a2966e6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@
 
 class GraphicBuffer;
 class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+    explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+    ~SurfaceControlHolder();
+
+    const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+    sp<SurfaceControl> mSurfaceControl;
+};
 
 class RefreshRateOverlay {
 public:
@@ -75,7 +89,7 @@
     const FpsRange mFpsRange; // For color interpolation.
     const bool mShowSpinner;
 
-    const sp<SurfaceControl> mSurfaceControl;
+    const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 2487dbd..e126931 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -203,25 +203,14 @@
         return 0.0f;
     }
 
-    // (b/133849373) ROT_90 screencap images produced upside down
-    auto area = sample_area;
-    if (orientation & ui::Transform::ROT_90) {
-        area.top = height - area.top;
-        area.bottom = height - area.bottom;
-        std::swap(area.top, area.bottom);
-
-        area.left = width - area.left;
-        area.right = width - area.right;
-        std::swap(area.left, area.right);
-    }
-
-    const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+    const uint32_t pixelCount =
+            (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left);
     uint32_t accumulatedLuma = 0;
 
     // Calculates luma with approximation of Rec. 709 primaries
-    for (int32_t row = area.top; row < area.bottom; ++row) {
+    for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) {
         const uint32_t* rowBase = data + row * stride;
-        for (int32_t column = area.left; column < area.right; ++column) {
+        for (int32_t column = sample_area.left; column < sample_area.right; ++column) {
             uint32_t pixel = rowBase[column];
             const uint32_t r = pixel & 0xFF;
             const uint32_t g = (pixel >> 8) & 0xFF;
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 5de796d..8df9ba5 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -57,7 +57,4 @@
         "libgtest",
         "libscheduler",
     ],
-    sanitize: {
-        address: true,
-    },
 }
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 747032b..4af1f5c 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -188,10 +188,10 @@
 
 VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const {
     std::lock_guard lock(mVsyncMutex);
-    nsecs_t expectedPresentTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
+    nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
             systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
-    nsecs_t deadline = expectedPresentTime - mWorkDuration.get().count() - mReadyDuration.count();
-    return {expectedPresentTime, deadline};
+    nsecs_t deadline = expectedPresentationTime - mReadyDuration.count();
+    return {expectedPresentationTime, deadline};
 }
 
 void DispSyncSource::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index ca83496..a48c921 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -40,22 +40,26 @@
 
 struct RefreshRateScore {
     DisplayModeIterator modeIt;
-    float score;
+    float overallScore;
+    struct {
+        float modeBelowThreshold;
+        float modeAboveThreshold;
+    } fixedRateBelowThresholdLayersScore;
 };
 
 template <typename Iterator>
 const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
     const auto it =
             std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
-                const auto& [modeIt, score] = current;
+                const auto& [modeIt, overallScore, _] = current;
 
                 std::string name = to_string(modeIt->second->getFps());
-                ALOGV("%s scores %.2f", name.c_str(), score);
+                ALOGV("%s scores %.2f", name.c_str(), overallScore);
 
-                ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100)));
+                ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
 
                 constexpr float kEpsilon = 0.0001f;
-                return score > max.score * (1 + kEpsilon);
+                return overallScore > max.overallScore * (1 + kEpsilon);
             });
 
     return it->modeIt->second;
@@ -151,31 +155,6 @@
     return {quotient, remainder};
 }
 
-bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const {
-    using namespace fps_approx_ops;
-
-    switch (layer.vote) {
-        case LayerVoteType::ExplicitExactOrMultiple:
-        case LayerVoteType::Heuristic:
-            if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
-                layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
-                // Don't vote high refresh rates past the threshold for layers with a low desired
-                // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
-                // 120 Hz, but desired 60 fps should have a vote.
-                return false;
-            }
-            break;
-        case LayerVoteType::ExplicitDefault:
-        case LayerVoteType::ExplicitExact:
-        case LayerVoteType::Max:
-        case LayerVoteType::Min:
-        case LayerVoteType::NoVote:
-            break;
-    }
-    return true;
-}
-
 float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
                                                                     Fps refreshRate) const {
     constexpr float kScoreForFractionalPairs = .8f;
@@ -240,10 +219,6 @@
 
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
                                                     bool isSeamlessSwitch) const {
-    if (!isVoteAllowed(layer, refreshRate)) {
-        return 0;
-    }
-
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -300,6 +275,7 @@
 auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
                                                   GlobalSignals signals) const
         -> std::pair<DisplayModePtr, GlobalSignals> {
+    using namespace fps_approx_ops;
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
@@ -409,7 +385,7 @@
 
         const auto weight = layer.weight;
 
-        for (auto& [modeIt, score] : scores) {
+        for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
             const auto& [id, mode] = *modeIt;
             const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
 
@@ -451,18 +427,92 @@
                 continue;
             }
 
-            const auto layerScore =
+            const float layerScore =
                     calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
-            ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                  to_string(mode->getFps()).c_str(), layerScore);
+            const float weightedLayerScore = weight * layerScore;
 
-            score += weight * layerScore;
+            // Layer with fixed source has a special consideration which depends on the
+            // mConfig.frameRateMultipleThreshold. We don't want these layers to score
+            // refresh rates above the threshold, but we also don't want to favor the lower
+            // ones by having a greater number of layers scoring them. Instead, we calculate
+            // the score independently for these layers and later decide which
+            // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
+            // score 120 Hz, but desired 60 fps should contribute to the score.
+            const bool fixedSourceLayer = [](LayerVoteType vote) {
+                switch (vote) {
+                    case LayerVoteType::ExplicitExactOrMultiple:
+                    case LayerVoteType::Heuristic:
+                        return true;
+                    case LayerVoteType::NoVote:
+                    case LayerVoteType::Min:
+                    case LayerVoteType::Max:
+                    case LayerVoteType::ExplicitDefault:
+                    case LayerVoteType::ExplicitExact:
+                        return false;
+                }
+            }(layer.vote);
+            const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
+                    layer.desiredRefreshRate <
+                            Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
+            if (fixedSourceLayer && layerBelowThreshold) {
+                const bool modeAboveThreshold =
+                        mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+                if (modeAboveThreshold) {
+                    ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+                          layerScore);
+                    fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
+                } else {
+                    ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+                          layerScore);
+                    fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
+                }
+            } else {
+                ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
+                      to_string(mode->getFps()).c_str(), layerScore);
+                overallScore += weightedLayerScore;
+            }
         }
     }
 
-    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
-    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
-    // or the lower otherwise.
+    // We want to find the best refresh rate without the fixed source layers,
+    // so we could know whether we should add the modeAboveThreshold scores or not.
+    // If the best refresh rate is already above the threshold, it means that
+    // some non-fixed source layers already scored it, so we can just add the score
+    // for all fixed source layers, even the ones that are above the threshold.
+    const bool maxScoreAboveThreshold = [&] {
+        if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
+            return false;
+        }
+
+        const auto maxScoreIt =
+                std::max_element(scores.begin(), scores.end(),
+                                 [](RefreshRateScore max, RefreshRateScore current) {
+                                     const auto& [modeIt, overallScore, _] = current;
+                                     return overallScore > max.overallScore;
+                                 });
+        ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+              "refresh rate multiples",
+              to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+              maxScoreAboveThreshold ? "above" : "below");
+        return maxScoreIt->modeIt->second->getFps() >=
+                Fps::fromValue(mConfig.frameRateMultipleThreshold);
+    }();
+
+    // Now we can add the fixed rate layers score
+    for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+        overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
+        if (maxScoreAboveThreshold) {
+            overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
+        }
+        ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
+              overallScore);
+    }
+
+    // Now that we scored all the refresh rates we need to pick the one that got the highest
+    // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers
+    // wanted Max, or the lower otherwise.
     const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
             ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
             : getMaxScoreRefreshRate(scores.begin(), scores.end());
@@ -471,7 +521,7 @@
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.score == 0; })) {
+                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
             const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
             ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
             return {max, kNoSignals};
@@ -575,7 +625,7 @@
             continue;
         }
 
-        for (auto& [_, score] : scores) {
+        for (auto& [_, score, _1] : scores) {
             score = 0;
         }
 
@@ -587,7 +637,7 @@
             LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
                                 layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
                                 layer->vote != LayerVoteType::ExplicitExact);
-            for (auto& [modeIt, score] : scores) {
+            for (auto& [modeIt, score, _] : scores) {
                 constexpr bool isSeamlessSwitch = true;
                 const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
                                                                   isSeamlessSwitch);
@@ -605,7 +655,7 @@
 
         // If we never scored any layers, we don't have a preferred frame rate
         if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.score == 0; })) {
+                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
             continue;
         }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 05a8692..a79002e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -353,9 +353,6 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
-    // Returns whether the layer is allowed to vote for the given refresh rate.
-    bool isVoteAllowed(const LayerRequirement&, Fps) const;
-
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 37f0fec..8b1a5d9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
+#include <ftl/fake_guard.h>
 #include <gui/WindowInfo.h>
 #include <system/window.h>
 #include <ui/DisplayStatInfo.h>
@@ -94,9 +95,13 @@
 }
 
 void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+    // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
     {
-        // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
-        std::scoped_lock lock(mRefreshRateConfigsLock);
+        // mRefreshRateConfigsLock is not locked here to avoid the deadlock
+        // as the callback can attempt to acquire the lock before stopIdleTimer can finish
+        // the execution. It's safe to FakeGuard as main thread is the only thread that
+        // writes to the mRefreshRateConfigs.
+        ftl::FakeGuard guard(mRefreshRateConfigsLock);
         if (mRefreshRateConfigs) {
             mRefreshRateConfigs->stopIdleTimer();
             mRefreshRateConfigs->clearIdleTimerCallbacks();
@@ -554,11 +559,12 @@
     }
 }
 
-void Scheduler::setDisplayPowerState(bool normal) {
+void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        mPolicy.isDisplayPowerStateNormal = normal;
+        mPolicy.displayPowerMode = powerMode;
     }
+    mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
 
     if (mDisplayPowerTimer) {
         mDisplayPowerTimer->reset();
@@ -706,7 +712,8 @@
     // If Display Power is not in normal operation we want to be in performance mode. When coming
     // back to normal mode, a grace period is given with DisplayPowerTimer.
     if (mDisplayPowerTimer &&
-        (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
+        (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+         mPolicy.displayPowerTimer == TimerState::Reset)) {
         constexpr GlobalSignals kNoSignals;
         return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
     }
@@ -720,9 +727,7 @@
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure the stored mode is up to date.
-    if (mPolicy.mode) {
-        mPolicy.mode = chooseDisplayMode().first;
-    }
+    mPolicy.mode = chooseDisplayMode().first;
     return mPolicy.mode;
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0c72124..a8043bf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -186,7 +186,7 @@
     // Indicates that touch interaction is taking place.
     void onTouchHint();
 
-    void setDisplayPowerState(bool normal);
+    void setDisplayPowerMode(hal::PowerMode powerMode);
 
     VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
 
@@ -325,7 +325,7 @@
         TimerState idleTimer = TimerState::Reset;
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
-        bool isDisplayPowerStateNormal = true;
+        hal::PowerMode displayPowerMode = hal::PowerMode::ON;
 
         // Chosen display mode.
         DisplayModePtr mode;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 665a7ee..e23945d 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -147,6 +147,11 @@
         return false;
     }
 
+    if (mDisplayPowerMode == hal::PowerMode::DOZE ||
+        mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) {
+        return true;
+    }
+
     if (!mLastHwVsync && !HwcVsyncPeriod) {
         return false;
     }
@@ -207,6 +212,11 @@
     return mMoreSamplesNeeded;
 }
 
+void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) {
+    std::scoped_lock lock(mMutex);
+    mDisplayPowerMode = powerMode;
+}
+
 void VSyncReactor::dump(std::string& result) const {
     std::lock_guard lock(mMutex);
     StringAppendF(&result, "VsyncReactor in use\n");
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 6a1950a..4501487 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,6 +49,8 @@
     bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                              bool* periodFlushed) final;
 
+    void setDisplayPowerMode(hal::PowerMode powerMode) final;
+
     void dump(std::string& result) const final;
 
 private:
@@ -73,6 +75,8 @@
     std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
     std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
 
+    hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
+
     const bool mSupportKernelIdleTimer = false;
 };
 
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 59f6537..726a420 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -18,7 +18,10 @@
 
 #include <cstddef>
 #include <memory>
+#include <mutex>
 
+#include <DisplayHardware/HWComposer.h>
+#include <DisplayHardware/Hal.h>
 #include <ui/FenceTime.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
@@ -70,6 +73,13 @@
      */
     virtual void setIgnorePresentFences(bool ignore) = 0;
 
+    /*
+     * Sets the primary display power mode to the controller.
+     *
+     * \param [in] powerMode
+     */
+    virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0;
+
     virtual void dump(std::string& result) const = 0;
 
 protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2fa40a5..8c46515 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -445,6 +445,11 @@
     }
 
     mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
+
+    // Power hint session mode, representing which hint(s) to send: early, late, or both)
+    mPowerHintSessionMode =
+            {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
+             .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1066,7 +1071,7 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
     ATRACE_CALL();
 
     if (!info.mode) {
@@ -1079,7 +1084,7 @@
         return;
     }
 
-    if (display->setDesiredActiveMode(info)) {
+    if (display->setDesiredActiveMode(info, force)) {
         scheduleComposite(FrameHint::kNone);
 
         // Start receiving vsync samples now, so that we can detect a period
@@ -2000,12 +2005,6 @@
 
 bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    // we set this once at the beginning of commit to ensure consistency throughout the whole frame
-    mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession();
-    if (mPowerHintSessionData.sessionEnabled) {
-        mPowerHintSessionData.commitStart = systemTime();
-    }
-
     // calculate the expected present time once and use the cached
     // value throughout this frame to make sure all layers are
     // seeing this same value.
@@ -2019,10 +2018,6 @@
     const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
     mScheduledPresentTime = expectedVsyncTime;
 
-    if (mPowerHintSessionData.sessionEnabled) {
-        mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime -
-                                             mPowerHintSessionData.commitStart);
-    }
     const auto vsyncIn = [&] {
         if (!ATRACE_ENABLED()) return 0.f;
         return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2098,6 +2093,32 @@
         }
     }
 
+    // Save this once per commit + composite to ensure consistency
+    // TODO (b/240619471): consider removing active display check once AOD is fixed
+    const auto activeDisplay =
+            FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+    mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
+            activeDisplay->getPowerMode() == hal::PowerMode::ON;
+    if (mPowerHintSessionEnabled) {
+        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+        // get stable vsync period from display mode
+        const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod();
+        mPowerAdvisor->setCommitStart(frameTime);
+        mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+        const nsecs_t idealSfWorkDuration =
+                mVsyncModulator->getVsyncConfig().sfWorkDuration.count();
+        // Frame delay is how long we should have minus how long we actually have
+        mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime));
+        mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+        mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+
+        // Send early hint here to make sure there's not another frame pending
+        if (mPowerHintSessionMode.early) {
+            // Send a rough prediction for this frame based on last frame's timing info
+            mPowerAdvisor->sendPredictedWorkDuration();
+        }
+    }
+
     if (mTracingEnabledChanged) {
         mLayerTracingEnabled = mLayerTracing.isEnabled();
         mTracingEnabledChanged = false;
@@ -2117,8 +2138,16 @@
 
         bool needsTraversal = false;
         if (clearTransactionFlags(eTransactionFlushNeeded)) {
+            // Locking:
+            // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+            // we must keep a copy of the transactions (specifically the composer
+            // states) around outside the scope of the lock
+            // 2. Transactions and created layers do not share a lock. To prevent applying
+            // transactions with layers still in the createdLayer queue, flush the transactions
+            // before committing the created layers.
+            std::vector<TransactionState> transactions = flushTransactions();
             needsTraversal |= commitCreatedLayers();
-            needsTraversal |= flushTransactionQueues(vsyncId);
+            needsTraversal |= applyTransactions(transactions, vsyncId);
         }
 
         const bool shouldCommit =
@@ -2174,16 +2203,15 @@
         FTL_FAKE_GUARD(kMainThreadContext) {
     ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
 
-    if (mPowerHintSessionData.sessionEnabled) {
-        mPowerHintSessionData.compositeStart = systemTime();
-    }
-
     compositionengine::CompositionRefreshArgs refreshArgs;
     const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
     refreshArgs.outputs.reserve(displays.size());
+    std::vector<DisplayId> displayIds;
     for (const auto& [_, display] : displays) {
         refreshArgs.outputs.push_back(display->getCompositionDisplay());
+        displayIds.push_back(display->getId());
     }
+    mPowerAdvisor->setDisplays(displayIds);
     mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
         if (auto layerFE = layer->getCompositionEngineLayerFE())
             refreshArgs.layers.push_back(layerFE);
@@ -2231,12 +2259,17 @@
 
     mCompositionEngine->present(refreshArgs);
 
-    if (mPowerHintSessionData.sessionEnabled) {
-        mPowerHintSessionData.presentEnd = systemTime();
-    }
-
     mTimeStats->recordFrameDuration(frameTime, systemTime());
 
+    // Send a power hint hint after presentation is finished
+    if (mPowerHintSessionEnabled) {
+        mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
+                                          systemTime());
+        if (mPowerHintSessionMode.late) {
+            mPowerAdvisor->sendActualWorkDuration();
+        }
+    }
+
     if (mScheduler->onPostComposition(presentTime)) {
         scheduleComposite(FrameHint::kNone);
     }
@@ -2281,11 +2314,8 @@
         scheduleCommit(FrameHint::kNone);
     }
 
-    // calculate total render time for performance hinting if adpf cpu hint is enabled,
-    if (mPowerHintSessionData.sessionEnabled) {
-        const nsecs_t flingerDuration =
-                (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
-        mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
+    if (mPowerHintSessionEnabled) {
+        mPowerAdvisor->setCompositeEnd(systemTime());
     }
 }
 
@@ -2879,7 +2909,8 @@
     ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
     // virtual displays are always considered enabled
-    creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    creationArgs.initialPowerMode =
+            state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
 
     sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
 
@@ -3238,16 +3269,34 @@
     if (!updateWindowInfo && mInputWindowCommands.empty()) {
         return;
     }
+
+    std::unordered_set<Layer*> visibleLayers;
+    mDrawingState.traverse([&visibleLayers](Layer* layer) {
+        if (layer->isVisibleForInput()) {
+            visibleLayers.insert(layer);
+        }
+    });
+    bool visibleLayersChanged = false;
+    if (visibleLayers != mVisibleLayers) {
+        visibleLayersChanged = true;
+        mVisibleLayers = std::move(visibleLayers);
+    }
+
     BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
                                                       windowInfos = std::move(windowInfos),
                                                       displayInfos = std::move(displayInfos),
                                                       inputWindowCommands =
                                                               std::move(mInputWindowCommands),
-                                                      inputFlinger = mInputFlinger, this]() {
+                                                      inputFlinger = mInputFlinger, this,
+                                                      visibleLayersChanged]() {
         ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
         if (updateWindowInfo) {
-            mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
-                                                            inputWindowCommands.syncInputWindows);
+            mWindowInfosListenerInvoker
+                    ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
+                                         /* shouldSync= */ inputWindowCommands.syncInputWindows,
+                                         /* forceImmediateCall= */
+                                         visibleLayersChanged ||
+                                                 !inputWindowCommands.focusRequests.empty());
         } else if (inputWindowCommands.syncInputWindows) {
             // If the caller requested to sync input windows, but there are no
             // changes to input windows, notify immediately.
@@ -3322,11 +3371,11 @@
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
 
-        // Do not create WindowInfos for windows on displays that cannot receive input.
-        if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {
-            const auto& info = opt->get();
-            outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));
-        }
+        const auto opt = displayInputInfos.get(layer->getLayerStack(),
+                                               [](const auto& info) -> Layer::InputDisplayArgs {
+                                                   return {&info.transform, info.isSecure};
+                                               });
+        outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
     });
 
     sNumWindowInfos = outWindowInfos.size();
@@ -3361,8 +3410,14 @@
     }
     ATRACE_CALL();
 
+    if (display->isInternal() && !isDisplayActiveLocked(display)) {
+        ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
+        return;
+    }
+
     if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
-        ALOGV("Skipping disallowed mode %d", mode->getId().value());
+        ALOGV("%s(%s): Disallowed mode %d", __func__, to_string(display->getId()).c_str(),
+              mode->getId().value());
         return;
     }
 
@@ -3775,7 +3830,7 @@
     return transactionsPendingBarrier;
 }
 
-bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
+std::vector<TransactionState> SurfaceFlinger::flushTransactions() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
@@ -3869,14 +3924,25 @@
                 flushUnsignaledPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                                                         applyTokensWithUnsignaledTransactions);
             }
-
-            return applyTransactions(transactions, vsyncId);
         }
     }
+    return transactions;
+}
+
+// for test only
+bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
+    std::vector<TransactionState> transactions = flushTransactions();
+    return applyTransactions(transactions, vsyncId);
 }
 
 bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
                                        int64_t vsyncId) {
+    Mutex::Autolock _l(mStateLock);
+    return applyTransactionsLocked(transactions, vsyncId);
+}
+
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions,
+                                             int64_t vsyncId) {
     bool needsTraversal = false;
     // Now apply all transactions.
     for (auto& transaction : transactions) {
@@ -4179,7 +4245,7 @@
 
 bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                            Vector<ComposerState>& states,
-                                           const Vector<DisplayState>& displays, uint32_t flags,
+                                           Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
                                            const client_cache_t& uncacheBuffer,
@@ -4188,7 +4254,8 @@
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
-    for (const DisplayState& display : displays) {
+    for (DisplayState& display : displays) {
+        display.sanitize(permissions);
         transactionFlags |= setDisplayStateLocked(display);
     }
 
@@ -4367,6 +4434,7 @@
         }
         return 0;
     }
+    MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
 
     // Only set by BLAST adapter layers
     if (what & layer_state_t::eProducerDisconnect) {
@@ -4860,8 +4928,8 @@
     const auto displayId = display->getPhysicalId();
     ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
-    const hal::PowerMode currentMode = display->getPowerMode();
-    if (mode == currentMode) {
+    std::optional<hal::PowerMode> currentMode = display->getPowerMode();
+    if (currentMode.has_value() && mode == *currentMode) {
         return;
     }
 
@@ -4877,7 +4945,7 @@
         mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
     const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
-    if (currentMode == hal::PowerMode::OFF) {
+    if (!currentMode || *currentMode == hal::PowerMode::OFF) {
         // Turn on the display
         if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
             onActiveDisplayChangedLocked(display);
@@ -4908,7 +4976,7 @@
         if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
             ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
         }
-        if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4922,7 +4990,7 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
+        if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, refreshRate);
         }
@@ -4941,7 +5009,7 @@
     if (isDisplayActiveLocked(display)) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
-        mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
+        mScheduler->setDisplayPowerMode(mode);
     }
 
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
@@ -4994,6 +5062,25 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
+        // Traversal of drawing state must happen on the main thread.
+        // Otherwise, SortedVector may have shared ownership during concurrent
+        // traversals, which can result in use-after-frees.
+        std::string compositionLayers;
+        mScheduler
+                ->schedule([&] {
+                    StringAppendF(&compositionLayers, "Composition layers\n");
+                    mDrawingState.traverseInZOrder([&](Layer* layer) {
+                        auto* compositionState = layer->getCompositionState();
+                        if (!compositionState || !compositionState->isVisible) return;
+
+                        android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
+                                                     layer->getDebugName() ? layer->getDebugName()
+                                                                           : "<unknown>");
+                        compositionState->dump(compositionLayers);
+                    });
+                })
+                .get();
+
         bool dumpLayers = true;
         {
             TimedLock lock(mStateLock, s2ns(1), __func__);
@@ -5006,7 +5093,7 @@
                 (it->second)(args, asProto, result);
                 dumpLayers = false;
             } else if (!asProto) {
-                dumpAllLocked(args, result);
+                dumpAllLocked(args, compositionLayers, result);
             }
         }
 
@@ -5305,7 +5392,8 @@
     result.append(future.get());
 }
 
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                                   std::string& result) const {
     const bool colorize = !args.empty() && args[0] == String16("--color");
     Colorizer colorizer(colorize);
 
@@ -5356,18 +5444,7 @@
     StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
     colorizer.reset(result);
 
-    {
-        StringAppendF(&result, "Composition layers\n");
-        mDrawingState.traverseInZOrder([&](Layer* layer) {
-            auto* compositionState = layer->getCompositionState();
-            if (!compositionState || !compositionState->isVisible) return;
-
-            android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
-                                         layer->getDebugName() ? layer->getDebugName()
-                                                               : "<unknown>");
-            compositionState->dump(result);
-        });
-    }
+    result.append(compositionLayers);
 
     colorizer.bold(result);
     StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
@@ -6632,8 +6709,13 @@
                                              1 /* layerCount */, usage, "screenshot");
 
     const status_t bufferStatus = buffer->initCheck();
-    LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
-                        bufferStatus);
+    if (bufferStatus != OK) {
+        // Animations may end up being really janky, but don't crash here.
+        // Otherwise an irreponsible process may cause an SF crash by allocating
+        // too much.
+        ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+        return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share();
+    }
     const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
             renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
                                                  renderengine::impl::ExternalTexture::Usage::
@@ -6656,8 +6738,10 @@
         std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
         if (!renderArea) {
             ALOGW("Skipping screen capture because of invalid render area.");
-            captureResults.result = NO_MEMORY;
-            captureListener->onScreenCaptureCompleted(captureResults);
+            if (captureListener) {
+                captureResults.result = NO_MEMORY;
+                captureListener->onScreenCaptureCompleted(captureResults);
+            }
             return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
         }
 
@@ -6907,9 +6991,7 @@
         return NO_ERROR;
     }
 
-    status_t setPolicyResult = overridePolicy
-            ? display->refreshRateConfigs().setOverridePolicy(policy)
-            : display->refreshRateConfigs().setDisplayManagerPolicy(*policy);
+    const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
     if (setPolicyResult < 0) {
         return BAD_VALUE;
     }
@@ -6917,9 +6999,19 @@
         return NO_ERROR;
     }
 
-    scheduler::RefreshRateConfigs::Policy currentPolicy =
-            display->refreshRateConfigs().getCurrentPolicy();
+    if (display->isInternal() && !isDisplayActiveLocked(display)) {
+        // The policy will be be applied when the display becomes active.
+        ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
+        return NO_ERROR;
+    }
 
+    return applyRefreshRateConfigsPolicy(display);
+}
+
+status_t SurfaceFlinger::applyRefreshRateConfigsPolicy(const sp<DisplayDevice>& display,
+                                                       bool force) {
+    const scheduler::RefreshRateConfigs::Policy currentPolicy =
+            display->refreshRateConfigs().getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
@@ -6947,7 +7039,7 @@
     if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
         ALOGV("switching to Scheduler preferred display mode %d",
               preferredDisplayMode->getId().value());
-        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
+        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed}, force);
     } else {
         LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
                          preferredDisplayMode->getId().value());
@@ -7232,6 +7324,7 @@
         ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get());
         return;
     }
+    MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
 
     sp<Layer> parent;
     bool addToRoot = state.addToRoot;
@@ -7276,14 +7369,23 @@
 void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
     ATRACE_CALL();
 
+    // During boot, SF powers on the primary display, which is the first display to be active. In
+    // that case, there is no need to force setDesiredActiveMode, because DM is about to send its
+    // policy via setDesiredDisplayModeSpecs.
+    bool forceApplyPolicy = false;
+
     if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
         display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+        forceApplyPolicy = true;
     }
 
     if (!activeDisplay) {
         ALOGE("%s: activeDisplay is null", __func__);
         return;
     }
+
+    ALOGI("Active display is %s", to_string(activeDisplay->getPhysicalId()).c_str());
+
     mActiveDisplayToken = activeDisplay->getDisplayToken();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
     updateInternalDisplayVsyncLocked(activeDisplay);
@@ -7292,9 +7394,11 @@
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay->getTransformHint();
 
-    // Update the kernel timer for the current active display, since the policy
-    // for this display might have changed when it was not the active display.
-    toggleKernelIdleTimer();
+    // The policy of the new active/leader display may have changed while it was inactive. In that
+    // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
+    // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
+    // and the kernel idle timer of the newly active display must be toggled.
+    applyRefreshRateConfigsPolicy(activeDisplay, forceApplyPolicy);
 }
 
 status_t SurfaceFlinger::addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d91aa11..d9add5c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -712,7 +712,7 @@
     // Called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
-    void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+    void setDesiredActiveMode(const ActiveModeInfo& info, bool force = false) REQUIRES(mStateLock);
     status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
     // Sets the active mode and a new refresh rate in SF.
     void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
@@ -735,6 +735,9 @@
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
             EXCLUDES(mStateLock);
 
+    status_t applyRefreshRateConfigsPolicy(const sp<DisplayDevice>&, bool force = false)
+            REQUIRES(mStateLock);
+
     void commitTransactions() EXCLUDES(mStateLock);
     void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
     void doCommitTransactions() REQUIRES(mStateLock);
@@ -760,7 +763,7 @@
      * Transactions
      */
     bool applyTransactionState(const FrameTimelineInfo& info, Vector<ComposerState>& state,
-                               const Vector<DisplayState>& displays, uint32_t flags,
+                               Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime, bool isAutoTimestamp,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -770,6 +773,9 @@
             REQUIRES(mStateLock);
     // flush pending transaction that was presented after desiredPresentTime.
     bool flushTransactionQueues(int64_t vsyncId);
+
+    std::vector<TransactionState> flushTransactions();
+
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
 
@@ -818,7 +824,8 @@
                                size_t totalTXapplied) const;
     bool stopTransactionProcessing(const std::unordered_set<sp<IBinder>, SpHash<IBinder>>&
                                            applyTokensWithUnsignaledTransactions) const;
-    bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
+    bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+    bool applyTransactionsLocked(std::vector<TransactionState>& transactions, int64_t vsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -852,7 +859,7 @@
     // this layer meaning it is entirely safe to destroy all
     // resources associated to this layer.
     void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
-    void markLayerPendingRemovalLocked(const sp<Layer>& layer);
+    void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock);
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
@@ -1085,7 +1092,8 @@
     /*
      * Debugging & dumpsys
      */
-    void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+    void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                       std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
     void listLayersLocked(std::string& result) const;
@@ -1436,16 +1444,21 @@
         return mScheduler->getLayerFramerate(now, id);
     }
 
+    bool mPowerHintSessionEnabled;
+
     struct {
-        bool sessionEnabled = false;
-        nsecs_t commitStart;
-        nsecs_t compositeStart;
-        nsecs_t presentEnd;
-    } mPowerHintSessionData GUARDED_BY(kMainThreadContext);
+        bool late = false;
+        bool early = false;
+    } mPowerHintSessionMode;
 
     nsecs_t mAnimationTransactionTimeout = s2ns(5);
 
     friend class SurfaceComposerAIDL;
+
+    // Layers visible during the last commit. This set should only be used for testing set equality
+    // and membership. The pointers should not be dereferenced as it's possible the set contains
+    // pointers to freed layers.
+    std::unordered_set<Layer*> mVisibleLayers;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index cab33ae..57752b7 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -5,6 +5,14 @@
     },
     {
       "name": "libcompositionengine_test"
+    },
+    {
+      "name": "libscheduler_test"
+    }
+  ],
+  "hwasan-presubmit": [
+    {
+      "name": "libscheduler_test"
     }
   ]
 }
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 30b9d8f..023402f 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -28,19 +28,26 @@
 
 struct WindowInfosListenerInvoker::WindowInfosReportedListener
       : gui::BnWindowInfosReportedListener {
-    explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker) : mInvoker(invoker) {}
+    explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker, size_t callbackCount,
+                                         bool shouldSync)
+          : mInvoker(invoker), mCallbacksPending(callbackCount), mShouldSync(shouldSync) {}
 
     binder::Status onWindowInfosReported() override {
-        mInvoker.windowInfosReported();
+        mCallbacksPending--;
+        if (mCallbacksPending == 0) {
+            mInvoker.windowInfosReported(mShouldSync);
+        }
         return binder::Status::ok();
     }
 
+private:
     WindowInfosListenerInvoker& mInvoker;
+    std::atomic<size_t> mCallbacksPending;
+    bool mShouldSync;
 };
 
 WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger)
-      : mFlinger(flinger),
-        mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {}
+      : mFlinger(flinger) {}
 
 void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -64,30 +71,76 @@
     mWindowInfosListeners.erase(who);
 }
 
-void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
-                                                    const std::vector<DisplayInfo>& displayInfos,
-                                                    bool shouldSync) {
-    ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mWindowInfosListeners) {
-            windowInfosListeners.push_back(listener);
+void WindowInfosListenerInvoker::windowInfosChanged(std::vector<WindowInfo> windowInfos,
+                                                    std::vector<DisplayInfo> displayInfos,
+                                                    bool shouldSync, bool forceImmediateCall) {
+    auto callListeners = [this, windowInfos = std::move(windowInfos),
+                          displayInfos = std::move(displayInfos)](bool shouldSync) mutable {
+        ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+        {
+            std::scoped_lock lock(mListenersMutex);
+            for (const auto& [_, listener] : mWindowInfosListeners) {
+                windowInfosListeners.push_back(listener);
+            }
         }
-    }
 
-    mCallbacksPending = windowInfosListeners.size();
+        auto reportedListener =
+                sp<WindowInfosReportedListener>::make(*this, windowInfosListeners.size(),
+                                                      shouldSync);
 
-    for (const auto& listener : windowInfosListeners) {
-        listener->onWindowInfosChanged(windowInfos, displayInfos,
-                                       shouldSync ? mWindowInfosReportedListener : nullptr);
+        for (const auto& listener : windowInfosListeners) {
+            auto status =
+                    listener->onWindowInfosChanged(windowInfos, displayInfos, reportedListener);
+            if (!status.isOk()) {
+                reportedListener->onWindowInfosReported();
+            }
+        }
+    };
+
+    {
+        std::scoped_lock lock(mMessagesMutex);
+        // If there are unacked messages and this isn't a forced call, then return immediately.
+        // If a forced window infos change doesn't happen first, the update will be sent after
+        // the WindowInfosReportedListeners are called. If a forced window infos change happens or
+        // if there are subsequent delayed messages before this update is sent, then this message
+        // will be dropped and the listeners will only be called with the latest info. This is done
+        // to reduce the amount of binder memory used.
+        if (mActiveMessageCount > 0 && !forceImmediateCall) {
+            mWindowInfosChangedDelayed = std::move(callListeners);
+            mShouldSyncDelayed |= shouldSync;
+            return;
+        }
+
+        mWindowInfosChangedDelayed = nullptr;
+        shouldSync |= mShouldSyncDelayed;
+        mShouldSyncDelayed = false;
+        mActiveMessageCount++;
     }
+    callListeners(shouldSync);
 }
 
-void WindowInfosListenerInvoker::windowInfosReported() {
-    mCallbacksPending--;
-    if (mCallbacksPending == 0) {
+void WindowInfosListenerInvoker::windowInfosReported(bool shouldSync) {
+    if (shouldSync) {
         mFlinger.windowInfosReported();
     }
+
+    std::function<void(bool)> callListeners;
+    bool shouldSyncDelayed;
+    {
+        std::scoped_lock lock{mMessagesMutex};
+        mActiveMessageCount--;
+        if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
+            return;
+        }
+
+        mActiveMessageCount++;
+        callListeners = std::move(mWindowInfosChangedDelayed);
+        mWindowInfosChangedDelayed = nullptr;
+        shouldSyncDelayed = mShouldSyncDelayed;
+        mShouldSyncDelayed = false;
+    }
+
+    callListeners(shouldSyncDelayed);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index d8d8d0f..701f11e 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -34,15 +34,15 @@
     void addWindowInfosListener(sp<gui::IWindowInfosListener>);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
-    void windowInfosChanged(const std::vector<gui::WindowInfo>&,
-                            const std::vector<gui::DisplayInfo>&, bool shouldSync);
+    void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
+                            bool shouldSync, bool forceImmediateCall);
 
 protected:
     void binderDied(const wp<IBinder>& who) override;
 
 private:
     struct WindowInfosReportedListener;
-    void windowInfosReported();
+    void windowInfosReported(bool shouldSync);
 
     SurfaceFlinger& mFlinger;
     std::mutex mListenersMutex;
@@ -51,8 +51,10 @@
     ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
 
-    sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
-    std::atomic<size_t> mCallbacksPending{0};
+    std::mutex mMessagesMutex;
+    uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
+    std::function<void(bool)> mWindowInfosChangedDelayed GUARDED_BY(mMessagesMutex);
+    bool mShouldSyncDelayed;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index ec18054..883766b 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -139,11 +139,6 @@
 
     set_sched_policy(0, SP_FOREGROUND);
 
-    // Put most SurfaceFlinger threads in the system-background cpuset
-    // Keeps us from unnecessarily using big cores
-    // Do this after the binder thread pool init
-    if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM);
-
     // initialize before clients can connect
     flinger->init();
 
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index ceddf27..6ecf78b 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -63,7 +63,7 @@
         "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.common@1.2",
         "libandroid",
         "libbase",
@@ -122,6 +122,9 @@
     ],
     cpp_std: "experimental",
     gnu_extensions: false,
+    data: [
+        ":SurfaceFlinger_test",
+    ],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 2dc96b8..c58fe48 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -149,4 +149,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
index 9ab35d7..53de4a6 100644
--- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -52,6 +52,8 @@
     void verifyAndClearExpectations();
     void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
                                      std::chrono::nanoseconds sleepBeforeLastSend);
+    std::chrono::nanoseconds mAllowedDeviation;
+    std::chrono::nanoseconds mStaleTimeout;
 };
 
 void AidlPowerHalWrapperTest::SetUp() {
@@ -59,6 +61,9 @@
     mMockSession = new NiceMock<MockIPowerHintSession>();
     ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
     mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+    mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count());
+    mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation};
+    mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout;
 }
 
 void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
@@ -76,6 +81,7 @@
         mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
     }
 }
+
 WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
     WorkDuration duration;
     duration.durationNanos = durationNanos.count();
@@ -83,6 +89,10 @@
     return duration;
 }
 
+WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) {
+    return toWorkDuration(timePair.first, timePair.second);
+}
+
 std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) {
     std::ostringstream os;
     for (auto duration : durations) {
@@ -112,7 +122,7 @@
     EXPECT_FALSE(mWrapper->startPowerHintSession());
 }
 
-TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) {
     ASSERT_TRUE(mWrapper->supportsPowerHintSession());
 
     std::vector<int32_t> threadIds = {1, 2};
@@ -149,12 +159,8 @@
 
     std::chrono::nanoseconds base = 100ms;
     // test cases with target work duration and whether it should update hint against baseline 100ms
-    const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
-                                                                              {-1ms, false},
-                                                                              {200ms, true},
-                                                                              {2ms, true},
-                                                                              {91ms, false},
-                                                                              {109ms, false}};
+    const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases =
+            {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}};
 
     for (const auto& test : testCases) {
         // reset to 100ms baseline
@@ -200,21 +206,21 @@
     // 100ms
     const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
             testCases = {{{{-1ms, 100}}, false},
-                         {{{91ms, 100}}, false},
-                         {{{109ms, 100}}, false},
+                         {{{100ms - (mAllowedDeviation / 2), 100}}, false},
+                         {{{100ms + (mAllowedDeviation / 2), 100}}, false},
+                         {{{100ms + (mAllowedDeviation + 1ms), 100}}, true},
+                         {{{100ms - (mAllowedDeviation + 1ms), 100}}, true},
                          {{{100ms, 100}, {200ms, 200}}, true},
                          {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
 
     for (const auto& test : testCases) {
         // reset actual duration
-        sendActualWorkDurationGroup({base}, 80ms);
+        sendActualWorkDurationGroup({base}, mStaleTimeout);
 
         auto raw = test.first;
         std::vector<WorkDuration> durations(raw.size());
         std::transform(raw.begin(), raw.end(), durations.begin(),
-                       [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
-                           return toWorkDuration(d.first, d.second);
-                       });
+                       [](auto d) { return toWorkDuration(d); });
         EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
                 .Times(test.second ? 1 : 0);
         sendActualWorkDurationGroup(durations, 0ms);
@@ -222,40 +228,6 @@
     }
 }
 
-TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    std::chrono::nanoseconds lastTarget = 100ms;
-    EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1);
-    mWrapper->setTargetWorkDuration(lastTarget.count());
-    std::chrono::nanoseconds newTarget = 105ms;
-    mWrapper->setTargetWorkDuration(newTarget.count());
-    EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0);
-    std::chrono::nanoseconds actual = 21ms;
-    // 100 / 105 * 21ms = 20ms
-    std::chrono::nanoseconds expectedActualSent = 20ms;
-    std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)};
-
-    EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
-            .WillOnce(DoAll(
-                    [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) {
-                        EXPECT_EQ(expectedDurations, durationsSent)
-                                << base::StringPrintf("actual sent: %s vs expected: %s",
-                                                      printWorkDurations(durationsSent).c_str(),
-                                                      printWorkDurations(expectedDurations)
-                                                              .c_str());
-                    },
-                    Return(Status::ok())));
-    mWrapper->sendActualWorkDuration(actual.count(), 1);
-}
-
 TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
     ASSERT_TRUE(mWrapper->supportsPowerHintSession());
 
@@ -269,22 +241,23 @@
     auto base = toWorkDuration(100ms, 0);
     // test cases with actual work durations and whether it should update hint against baseline
     // 100ms
-    const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
-            testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+    const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>,
+                                 std::chrono::nanoseconds, bool>>
+            testCases = {{{{100ms, 100}}, mStaleTimeout, true},
+                         {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true},
+                         {{{100ms, 100}}, mStaleTimeout / 2, false}};
 
     for (const auto& test : testCases) {
         // reset actual duration
-        sendActualWorkDurationGroup({base}, 80ms);
+        sendActualWorkDurationGroup({base}, mStaleTimeout);
 
-        auto raw = test.first;
+        auto raw = std::get<0>(test);
         std::vector<WorkDuration> durations(raw.size());
         std::transform(raw.begin(), raw.end(), durations.begin(),
-                       [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
-                           return toWorkDuration(d.first, d.second);
-                       });
+                       [](auto d) { return toWorkDuration(d); });
         EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
-                .Times(test.second ? 1 : 0);
-        sendActualWorkDurationGroup(durations, 80ms);
+                .Times(std::get<2>(test) ? 1 : 0);
+        sendActualWorkDurationGroup(durations, std::get<1>(test));
         verifyAndClearExpectations();
     }
 }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7823363..339d746 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,6 +24,7 @@
 filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
+        "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
         "mock/DisplayHardware/MockIPower.cpp",
@@ -95,6 +96,7 @@
         "LayerTest.cpp",
         "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
+        "PowerAdvisorTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
@@ -136,7 +138,7 @@
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index bbfedc7..fbc532e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -664,7 +664,8 @@
                     EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
                     EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
                     EXPECT_EQ(false, layer.source.buffer.isOpaque);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
@@ -714,7 +715,8 @@
                     EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                     LayerProperties::COLOR[2]),
                               layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
@@ -792,7 +794,8 @@
                     const renderengine::LayerSettings layer = layerSettings.back();
                     EXPECT_THAT(layer.source.buffer.buffer, IsNull());
                     EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(1.0f, layer.alpha);
                     return resultFuture;
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index ec27eda..67ace1a 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -292,9 +292,10 @@
 
 TEST_F(DispSyncSourceTest, getLatestVsyncData) {
     const nsecs_t now = systemTime();
-    const nsecs_t vsyncInternalDuration = mWorkDuration.count() + mReadyDuration.count();
+    const nsecs_t expectedPresentationTime =
+            now + mWorkDuration.count() + mReadyDuration.count() + 1;
     EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
-            .WillOnce(Return(now + vsyncInternalDuration + 1));
+            .WillOnce(Return(expectedPresentationTime));
     {
         InSequence seq;
         EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
@@ -306,10 +307,8 @@
     EXPECT_TRUE(mDispSyncSource);
 
     const auto vsyncData = mDispSyncSource->getLatestVSyncData();
-    ASSERT_GT(vsyncData.deadlineTimestamp, now);
-    ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
-    EXPECT_EQ(vsyncData.deadlineTimestamp,
-              vsyncData.expectedPresentationTime - vsyncInternalDuration);
+    ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
+    EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
index 225ad16..ac5e927 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -96,5 +96,23 @@
     EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
 }
 
+TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) {
+    ftl::FakeGuard guard(kMainThreadContext);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = -1.0f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index f04221c..5ddd1c8 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -120,51 +120,6 @@
     });
 }
 
-sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
-        std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
-    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
-    constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
-    constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
-    constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
-
-    // The DisplayDevice is required to have a framebuffer (behind the
-    // ANativeWindow interface) which uses the actual hardware display
-    // size.
-    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
-    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
-    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
-    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
-    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
-    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
-
-    auto compositionDisplay =
-            compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
-                                                   compositionengine::DisplayCreationArgsBuilder()
-                                                           .setId(DEFAULT_DISPLAY_ID)
-                                                           .setPixels({DEFAULT_DISPLAY_WIDTH,
-                                                                       DEFAULT_DISPLAY_HEIGHT})
-                                                           .setPowerAdvisor(&mPowerAdvisor)
-                                                           .build());
-
-    constexpr bool kIsPrimary = true;
-    auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
-                                              ui::DisplayConnectionType::Internal,
-                                              DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
-
-    injector.setNativeWindow(mNativeWindow);
-    if (injectExtra) {
-        injectExtra(injector);
-    }
-
-    auto displayDevice = injector.inject();
-
-    Mock::VerifyAndClear(mNativeWindow.get());
-
-    return displayDevice;
-}
-
 bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
     return mFlinger.hwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
 }
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index f5235ce..60f773f 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -42,6 +42,7 @@
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/DebugUtils.h>
 
+#include "FakeDisplayInjector.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -90,7 +91,9 @@
     void injectFakeBufferQueueFactory();
     void injectFakeNativeWindowSurfaceFactory();
     sp<DisplayDevice> injectDefaultInternalDisplay(
-            std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+            std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) {
+        return mFakeDisplayInjector.injectInternalDisplay(injectExtra);
+    }
 
     // --------------------------------------------------------------------
     // Postcondition helpers
@@ -115,6 +118,8 @@
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
     Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
+    FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
+
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
     // to keep a reference to them for use in setting up call expectations.
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..6e4bf2b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+
+struct FakeDisplayInjectorArgs {
+    PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+    HWDisplayId hwcDisplayId = 0;
+    bool isPrimary = true;
+};
+
+class FakeDisplayInjector {
+public:
+    FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+                        sp<mock::NativeWindow> nativeWindow)
+          : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
+
+    sp<DisplayDevice> injectInternalDisplay(
+            const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+            FakeDisplayInjectorArgs args = {}) {
+        using testing::_;
+        using testing::AnyNumber;
+        using testing::DoAll;
+        using testing::Mock;
+        using testing::Return;
+        using testing::SetArgPointee;
+
+        constexpr ui::Size kResolution = {1080, 1920};
+
+        // The DisplayDevice is required to have a framebuffer (behind the
+        // ANativeWindow interface) which uses the actual hardware display
+        // size.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getWidth()), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getHeight()), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(mFlinger.getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(args.displayId)
+                                      .setPixels(kResolution)
+                                      .setPowerAdvisor(&mPowerAdvisor)
+                                      .build());
+
+        auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+                                                  ui::DisplayConnectionType::Internal,
+                                                  args.hwcDisplayId, args.isPrimary);
+
+        injector.setNativeWindow(mNativeWindow);
+        if (injectExtra) {
+            injectExtra(injector);
+        }
+
+        auto displayDevice = injector.inject();
+
+        Mock::VerifyAndClear(mNativeWindow.get());
+
+        return displayDevice;
+    }
+
+    TestableSurfaceFlinger& mFlinger;
+    Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+    sp<mock::NativeWindow> mNativeWindow;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
new file mode 100644
index 0000000..8711a42
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "PowerAdvisorTest"
+
+#include <DisplayHardware/PowerAdvisor.h>
+#include <compositionengine/Display.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/DisplayId.h>
+#include <chrono>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class PowerAdvisorTest : public testing::Test {
+public:
+    void SetUp() override;
+    void startPowerHintSession();
+    void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+    void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+    nsecs_t getFenceWaitDelayDuration(bool skipValidate);
+
+protected:
+    TestableSurfaceFlinger mFlinger;
+    std::unique_ptr<PowerAdvisor> mPowerAdvisor;
+    NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
+    nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count();
+};
+
+void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
+    std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
+            std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
+    mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
+    ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
+    ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
+    mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
+    mMockAidlWrapper =
+            reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
+}
+
+void PowerAdvisorTest::startPowerHintSession() {
+    const std::vector<int32_t> threadIds = {1, 2, 3};
+    mPowerAdvisor->enablePowerHint(true);
+    mPowerAdvisor->startPowerHintSession(threadIds);
+}
+
+void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) {
+    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget);
+    mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
+}
+
+void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) {
+    mPowerAdvisor->setCommitStart(startTime);
+    mPowerAdvisor->setFrameDelay(0);
+    mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+}
+
+nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
+    return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
+                         : PowerAdvisor::kFenceWaitStartDelayValidated)
+            .count();
+}
+
+namespace {
+
+TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+    const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration +
+            getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000);
+    // now report the fence as having fired during the display HWC time
+    mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration,
+                                      startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
+                                      GpuVirtualDisplayId(1)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    // make present duration much later than the hwc display by itself will account for
+    const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+
+    // don't report timing for the gpu displays since they don't use hwc
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index fcde532..188fd58 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -564,9 +564,10 @@
     TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
                                        {.frameRateMultipleThreshold = 120});
 
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -639,6 +640,48 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "120Hz ExplicitDefault";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitExact;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 10_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "30Hz ExplicitExactOrMultiple";
+    lr3.vote = LayerVoteType::Heuristic;
+    lr3.desiredRefreshRate = 120_Hz;
+    lr3.name = "120Hz Heuristic";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index f19e554..409e1ef 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -106,40 +106,6 @@
                 testing::Eq(0.0));
 }
 
-// workaround for b/133849373
-TEST_F(RegionSamplingTest, orientation_90) {
-    std::generate(buffer.begin(), buffer.end(),
-                  [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
-
-    Rect tl_region{0, 0, 4, 4};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           tl_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           tl_region),
-                testing::Eq(0.0));
-
-    Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           br_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           br_region),
-                testing::Eq(1.0));
-}
-
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index aab2795..93c809e 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -159,8 +159,8 @@
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
-    constexpr bool kPowerStateNormal = true;
-    mScheduler->setDisplayPowerState(kPowerStateNormal);
+    constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+    mScheduler->setDisplayPowerMode(kPowerModeOn);
 
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
@@ -226,8 +226,8 @@
 
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
 
-    constexpr bool kPowerStateNormal = true;
-    mScheduler->setDisplayPowerState(kPowerStateNormal);
+    constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+    mScheduler->setDisplayPowerMode(kPowerModeOn);
 
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 5872a47..b58add8 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "mock/MockEventThread.h"
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include "DisplayTransactionTestHelpers.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 
 #include <scheduler/Fps.h>
 
@@ -42,14 +42,9 @@
 
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
 
-        {
-            DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
-            auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-
-            mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                               .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
-                               .inject();
-        }
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setDisplayModes(kModes, kModeId60)
+                           .inject();
 
         setupScheduler(mDisplay->holdRefreshRateConfigs());
 
@@ -77,6 +72,8 @@
     static constexpr ui::Size kResolution4K{3840, 2160};
     static inline const DisplayModePtr kMode90_4K =
             createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+
+    static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
 };
 
 void DisplayModeSwitchingTest::setupScheduler(
@@ -265,5 +262,112 @@
     ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
 }
 
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+    constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+    constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+    constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+    constexpr bool kIsPrimary = false;
+    TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+                                                   kIsPrimary)
+            .setHwcDisplayId(kOuterDisplayHwcId)
+            .inject(&mFlinger, mComposer);
+
+    const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+                                         kModeId120);
+            },
+            {.displayId = kOuterDisplayId,
+             .hwcDisplayId = kOuterDisplayHwcId,
+             .isPrimary = kIsPrimary});
+
+    const auto& innerDisplay = mDisplay;
+
+    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId60);
+    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+
+    mFlinger.onActiveDisplayChanged(innerDisplay);
+
+    EXPECT_EQ(NO_ERROR,
+              mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+                                                  kModeId90.value(), false, 0.f, 120.f, 0.f,
+                                                  120.f));
+
+    EXPECT_EQ(NO_ERROR,
+              mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+                                                  kModeId60.value(), false, 0.f, 120.f, 0.f,
+                                                  120.f));
+
+    // Transition on the inner display.
+    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+
+    // No transition on the outer display.
+    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(kInnerDisplayHwcId,
+                                               hal::HWConfigId(kModeId90.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    mFlinger.commit();
+
+    // Transition on the inner display.
+    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+
+    // No transition on the outer display.
+    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+    mFlinger.commit();
+
+    // Transition on the inner display.
+    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
+
+    // No transition on the outer display.
+    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+
+    mFlinger.onActiveDisplayChanged(outerDisplay);
+
+    // No transition on the inner display.
+    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+    // Transition on the outer display.
+    ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(kOuterDisplayHwcId,
+                                               hal::HWConfigId(kModeId60.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    mFlinger.commit();
+
+    // No transition on the inner display.
+    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+    // Transition on the outer display.
+    ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+
+    mFlinger.commit();
+
+    // No transition on the inner display.
+    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
+
+    // Transition on the outer display.
+    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId60);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 8de9e4b..2c9888d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -74,6 +74,7 @@
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+    mFlinger.setPowerHintSessionMode(true, true);
     mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
     static constexpr bool kIsPrimary = true;
     FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -96,6 +97,7 @@
                     .setNativeWindow(mNativeWindow)
                     .setPowerMode(hal::PowerMode::ON)
                     .inject();
+    mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
 }
 
 void SurfaceFlingerPowerHintTest::setupScheduler() {
@@ -142,10 +144,30 @@
                 std::this_thread::sleep_for(mockHwcRunTime);
                 return hardware::graphics::composer::V2_1::Error::NONE;
             });
-    EXPECT_CALL(*mPowerAdvisor,
-                sendActualWorkDuration(Gt(mockHwcRunTime.count()),
-                                       Gt(now + mockHwcRunTime.count())))
+    EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1);
+    static constexpr bool kVsyncId = 123; // arbitrary
+    mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
+}
+
+TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) {
+    ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true));
+
+    mDisplay->setPowerMode(hal::PowerMode::DOZE);
+
+    const std::chrono::nanoseconds mockVsyncPeriod = 15ms;
+    EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0);
+
+    const nsecs_t now = systemTime();
+    const std::chrono::nanoseconds mockHwcRunTime = 20ms;
+    EXPECT_CALL(*mDisplaySurface,
+                prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
             .Times(1);
+    EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+            .WillOnce([mockHwcRunTime] {
+                std::this_thread::sleep_for(mockHwcRunTime);
+                return hardware::graphics::composer::V2_1::Error::NONE;
+            });
+    EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0);
     static constexpr bool kVsyncId = 123; // arbitrary
     mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
 }
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 583cf5f..b560025 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -323,6 +323,9 @@
 public:
     template <typename Case>
     void transitionDisplayCommon();
+
+    template <bool kBoot>
+    sp<DisplayDevice> activeDisplayTest();
 };
 
 template <PowerMode PowerMode>
@@ -499,5 +502,87 @@
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
+template <bool kBoot>
+sp<DisplayDevice> SetPowerModeInternalTest::activeDisplayTest() {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Inject a primary display.
+    Case::Display::injectHwcDisplay(this);
+    auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+    injector.setPowerMode(kBoot ? std::nullopt : std::make_optional(PowerMode::OFF));
+
+    const auto display = injector.inject();
+    EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+    using PowerCase = PrimaryDisplayPowerCase<TransitionOffToOnVariant>;
+    TransitionOffToOnVariant::template setupCallExpectations<PowerCase>(this);
+
+    constexpr size_t kTimes = kBoot ? 1 : 0;
+    EXPECT_CALL(*mRenderEngine, onActiveDisplaySizeChanged(display->getSize())).Times(kTimes);
+    EXPECT_CALL(*mEventThread, onModeChanged(display->getActiveMode())).Times(kTimes);
+
+    if constexpr (kBoot) {
+        mFlinger.mutableActiveDisplayToken() = nullptr;
+    }
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The primary display should be the active display.
+    EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+    Mock::VerifyAndClearExpectations(mComposer);
+    Mock::VerifyAndClearExpectations(mRenderEngine);
+    Mock::VerifyAndClearExpectations(mEventThread);
+    Mock::VerifyAndClearExpectations(mVsyncController);
+    Mock::VerifyAndClearExpectations(mVSyncTracker);
+    Mock::VerifyAndClearExpectations(mFlinger.scheduler());
+    Mock::VerifyAndClearExpectations(&mFlinger.mockSchedulerCallback());
+
+    return display;
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplayBoot) {
+    constexpr bool kBoot = true;
+    activeDisplayTest<kBoot>();
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplaySingle) {
+    constexpr bool kBoot = false;
+    activeDisplayTest<kBoot>();
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplayDual) {
+    constexpr bool kBoot = false;
+    const auto innerDisplay = activeDisplayTest<kBoot>();
+
+    // Inject a powered-off outer display.
+    const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) { injector.setPowerMode(PowerMode::OFF); },
+            {.displayId = PhysicalDisplayId::fromPort(254u),
+             .hwcDisplayId = 1,
+             .isPrimary = false});
+
+    EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+
+    EXPECT_EQ(outerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+
+    EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f1a69fb..3cffec1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -330,6 +330,10 @@
         layer->mDrawingParent = drawingParent;
     }
 
+    void setPowerHintSessionMode(bool early, bool late) {
+        mFlinger->mPowerHintSessionMode = {.late = late, .early = early};
+    }
+
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
@@ -741,6 +745,7 @@
                 mHwcDisplayId(hwcDisplayId) {
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
+            mCreationArgs.initialPowerMode = hal::PowerMode::ON;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
@@ -793,7 +798,7 @@
             return *this;
         }
 
-        auto& setPowerMode(hal::PowerMode mode) {
+        auto& setPowerMode(std::optional<hal::PowerMode> mode) {
             mCreationArgs.initialPowerMode = mode;
             return *this;
         }
@@ -862,6 +867,10 @@
                                   .deviceProductInfo = {},
                                   .supportedModes = modes,
                                   .activeMode = activeMode->get()};
+
+                if (mCreationArgs.isPrimary) {
+                    mFlinger.mutableActiveDisplayToken() = mDisplayToken;
+                }
             }
 
             state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 4eb9055..30a3f9a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -349,6 +349,23 @@
     }
 }
 
+TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) {
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+
+    mReactor.startPeriodTransition(newPeriod);
+
+    auto time = 0;
+    // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    // Set power mode to DOZE to trigger period flushing.
+    mReactor.setDisplayPowerMode(hal::PowerMode::DOZE);
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
 TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
     auto time = 0;
     bool periodFlushed = false;
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
similarity index 65%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
index fc2542b..5049b1d 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-parcelable GenericDataParcelable {
-    int data;
-    float majorVersion;
-    float minorVersion;
-    IBinder binder;
-    ParcelFileDescriptor fileDescriptor;
-    int[] array;
-}
\ No newline at end of file
+#include "MockAidlPowerHalWrapper.h"
+#include "MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
+      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
+MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
new file mode 100644
index 0000000..657ced3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/PowerAdvisor.h"
+
+namespace android {
+namespace hardware {
+namespace power {
+class IPower;
+}
+} // namespace hardware
+} // namespace android
+
+namespace android::Hwc2::mock {
+
+class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper {
+public:
+    MockAidlPowerHalWrapper();
+    ~MockAidlPowerHalWrapper() override;
+    MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
+    MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, restartPowerHintSession, (), (override));
+    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+                (override));
+    MOCK_METHOD(bool, startPowerHintSession, (), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp),
+                (override));
+    MOCK_METHOD(bool, shouldReconnectHAL, (), (override));
+};
+
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index a83ecbc..6809580 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -33,4 +33,24 @@
             .build();
 }
 
+inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
+    return DisplayMode::Builder(modePtr->getHwcId())
+            .setId(modePtr->getId())
+            .setPhysicalDisplayId(displayId)
+            .setVsyncPeriod(modePtr->getVsyncPeriod())
+            .setGroup(modePtr->getGroup())
+            .setResolution(modePtr->getResolution())
+            .build();
+}
+
+inline DisplayModes cloneForDisplay(PhysicalDisplayId displayId, const DisplayModes& modes) {
+    DisplayModes clones;
+
+    for (const auto& [id, modePtr] : modes) {
+        clones.try_emplace(id, cloneForDisplay(displayId, modePtr));
+    }
+
+    return clones;
+}
+
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index c598cbc..aede250 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,11 +36,33 @@
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
-    MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
-                (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+    MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
     MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+    MOCK_METHOD(void, setGpuFenceTime,
+                (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+    MOCK_METHOD(void, setHwcValidateTiming,
+                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (override));
+    MOCK_METHOD(void, setHwcPresentTiming,
+                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (override));
+    MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+    MOCK_METHOD(void, setRequiresClientComposition,
+                (DisplayId displayId, bool requiresClientComposition), (override));
+    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+                (override));
+    MOCK_METHOD(void, setHwcPresentDelayedTime,
+                (DisplayId displayId,
+                 std::chrono::steady_clock::time_point earliestFrameStartTime));
+    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+    MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 314f681..4ef91da 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -31,6 +31,7 @@
     MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
     MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
     MOCK_METHOD1(setIgnorePresentFences, void(bool));
+    MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
deleted file mode 100644
index 80e9a3c..0000000
--- a/services/vr/hardware_composer/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_shared {
-    name: "libvr_hwc-hal",
-
-    system_ext_specific: true,
-
-    srcs: [
-        "impl/vr_hwc.cpp",
-        "impl/vr_composer_client.cpp",
-    ],
-
-    static_libs: [
-        "libbroadcastring",
-        "libdisplay",
-    ],
-
-    shared_libs: [
-        "android.frameworks.vr.composer@2.0",
-        "android.hardware.graphics.composer@2.1",
-        "android.hardware.graphics.composer@2.2",
-        "android.hardware.graphics.composer@2.3",
-        "android.hardware.graphics.composer@2.1-resources",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
-        "libbase",
-        "libbufferhubqueue",
-        "libbinder",
-        "libcutils",
-        "libfmq",
-        "libhardware",
-        "libhidlbase",
-        "liblog",
-        "libsync",
-        "libui",
-        "libutils",
-        "libpdx_default_transport",
-    ],
-
-    header_libs: [
-        "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.3-hal",
-    ],
-
-    export_header_lib_headers: [
-        "android.hardware.graphics.composer@2.3-hal",
-    ],
-
-    export_static_lib_headers: [
-        "libdisplay",
-    ],
-
-    export_shared_lib_headers: [
-        "android.frameworks.vr.composer@2.0",
-        "android.hardware.graphics.composer@2.1",
-        "android.hardware.graphics.composer@2.2",
-        "android.hardware.graphics.composer@2.3",
-    ],
-
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-DLOG_TAG=\"vr_hwc\"",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-Wall",
-        "-Werror",
-        "-Wno-error=unused-private-field",
-        // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
-        "-Wno-sign-compare",
-        "-Wno-unused-parameter",
-    ],
-
-}
-
-cc_library_static {
-    name: "libvr_hwc-impl",
-    srcs: [
-        "vr_composer.cpp",
-    ],
-    static_libs: [
-        "libvr_hwc-binder",
-    ],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libui",
-        "libutils",
-        "libvr_hwc-hal",
-    ],
-    export_shared_lib_headers: [
-        "libvr_hwc-hal",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"vr_hwc\"",
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_test {
-    name: "vr_hwc_test",
-    gtest: true,
-    srcs: ["tests/vr_composer_test.cpp"],
-    static_libs: [
-        "libgtest",
-        "libvr_hwc-impl",
-        // NOTE: This needs to be included after the *-impl lib otherwise the
-        // symbols in the *-binder library get optimized out.
-        "libvr_hwc-binder",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
-        "-Wno-sign-compare",
-        "-Wno-unused-parameter",
-    ],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libui",
-        "libutils",
-    ],
-}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
deleted file mode 100644
index fa71ed7..0000000
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_static {
-    name: "libvr_hwc-binder",
-    srcs: [
-        "android/dvr/IVrComposer.aidl",
-        "android/dvr/IVrComposerCallback.aidl",
-        "android/dvr/parcelable_composer_frame.cpp",
-        "android/dvr/parcelable_composer_layer.cpp",
-        "android/dvr/parcelable_unique_fd.cpp",
-    ],
-    aidl: {
-        local_include_dirs: ["."],
-        export_aidl_headers: true,
-    },
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    shared_libs: [
-        "libbinder",
-        "libui",
-        "libutils",
-        "libvr_hwc-hal",
-    ],
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
deleted file mode 100644
index be1ec5b..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-package android.dvr;
-
-import android.dvr.IVrComposerCallback;
-
-/**
- * Service interface exposed by VR HWC exposed to system apps which allows one
- * system app to connect to get SurfaceFlinger's outputs (all displays). This
- * is active when SurfaceFlinger is in VR mode, where all 2D output is
- * redirected to VR HWC.
- *
- * @hide */
-interface IVrComposer
-{
-  const String SERVICE_NAME = "vr_hwc";
-
-  /**
-   * Registers a callback used to receive frame notifications.
-   */
-  void registerObserver(in IVrComposerCallback callback);
-
-  /**
-   * Clears a previously registered frame notification callback.
-   */
-  void clearObserver();
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
deleted file mode 100644
index aa70de1..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-package android.dvr;
-
-import android.dvr.ParcelableComposerFrame;
-import android.dvr.ParcelableUniqueFd;
-
-/**
- * A system app will implement and register this callback with VRComposer
- * to receive the layers SurfaceFlinger presented when in VR mode.
- *
- * @hide */
-interface IVrComposerCallback {
-  /**
-   * Called by the VR HWC service when a new frame is ready to be presented.
-   *
-   * @param frame The new frame VR HWC wants to present.
-   * @return A fence FD used to signal when the previous frame is no longer
-   * used by the client. This may be an invalid fence (-1) if the client is not
-   * using the previous frame, in which case the previous frame may be re-used
-   * at any point in time.
-   */
-  ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame);
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
deleted file mode 100644
index 84abc19..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
deleted file mode 100644
index a200345..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
deleted file mode 100644
index eee9d13..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
deleted file mode 100644
index db7d5dc..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_frame.h"
-
-#include <binder/Parcel.h>
-
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerFrame::ParcelableComposerFrame() {}
-
-ParcelableComposerFrame::ParcelableComposerFrame(
-    const ComposerView::Frame& frame)
-    : frame_(frame) {}
-
-ParcelableComposerFrame::~ParcelableComposerFrame() {}
-
-status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeUint64(frame_.display_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.display_width);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.display_height);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeBool(frame_.removed);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.color_transform_hint);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < 16; i++) {
-    ret = parcel->writeFloat(frame_.color_transform[i]);
-    if (ret != OK) return ret;
-  }
-
-  std::vector<ParcelableComposerLayer> layers;
-  for (size_t i = 0; i < frame_.layers.size(); ++i)
-    layers.push_back(ParcelableComposerLayer(frame_.layers[i]));
-
-  ret = parcel->writeParcelableVector(layers);
-
-  return ret;
-}
-
-status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) {
-  status_t ret = parcel->readUint64(&frame_.display_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&frame_.display_width);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&frame_.display_height);
-  if (ret != OK) return ret;
-
-  ret = parcel->readBool(&frame_.removed);
-  if (ret != OK) return ret;
-
-  uint32_t value;
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.active_config = static_cast<Config>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.color_mode = static_cast<ColorMode>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.power_mode = static_cast<IComposerClient::PowerMode>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value);
-
-  ret = parcel->readInt32(&frame_.color_transform_hint);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < 16; i++) {
-    ret = parcel->readFloat(&frame_.color_transform[i]);
-    if (ret != OK) return ret;
-  }
-
-  std::vector<ParcelableComposerLayer> layers;
-  ret = parcel->readParcelableVector(&layers);
-  if (ret != OK) return ret;
-
-  frame_.layers.clear();
-  for (size_t i = 0; i < layers.size(); ++i)
-    frame_.layers.push_back(layers[i].layer());
-
-  return ret;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
deleted file mode 100644
index a82df7f..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerFrame : public Parcelable {
- public:
-  ParcelableComposerFrame();
-  explicit ParcelableComposerFrame(const ComposerView::Frame& frame);
-  ~ParcelableComposerFrame() override;
-
-  ComposerView::Frame frame() const { return frame_; }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  ComposerView::Frame frame_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
deleted file mode 100644
index c3621eb..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-#include <binder/Parcel.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicBufferMapper.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerLayer::ParcelableComposerLayer() {}
-
-ParcelableComposerLayer::ParcelableComposerLayer(
-    const ComposerView::ComposerLayer& layer) : layer_(layer) {}
-
-ParcelableComposerLayer::~ParcelableComposerLayer() {}
-
-status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeUint64(layer_.id);
-  if (ret != OK) return ret;
-
-  ret = parcel->write(*layer_.buffer);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeBool(layer_.fence->isValid());
-  if (ret != OK) return ret;
-
-  if (layer_.fence->isValid()) {
-    ret = parcel->writeFileDescriptor(layer_.fence->dup(), true);
-    if (ret != OK) return ret;
-  }
-
-  ret = parcel->writeInt32(layer_.display_frame.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.alpha);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.type);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.app_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.z_order);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.cursor_x);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.cursor_y);
-  if (ret != OK) return ret;
-
-  uint32_t color = layer_.color.r |
-      (static_cast<uint32_t>(layer_.color.g) << 8) |
-      (static_cast<uint32_t>(layer_.color.b) << 16) |
-      (static_cast<uint32_t>(layer_.color.a) << 24);
-  ret = parcel->writeUint32(color);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.dataspace);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.transform);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size()));
-  if (ret != OK) return ret;
-
-  for (auto& rect: layer_.visible_regions) {
-    ret = parcel->writeInt32(rect.left);
-    ret = parcel->writeInt32(rect.top);
-    ret = parcel->writeInt32(rect.right);
-    ret = parcel->writeInt32(rect.bottom);
-    if (ret != OK) return ret;
-  }
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size()));
-  if (ret != OK) return ret;
-
-  for (auto& rect: layer_.damaged_regions) {
-    ret = parcel->writeInt32(rect.left);
-    ret = parcel->writeInt32(rect.top);
-    ret = parcel->writeInt32(rect.right);
-    ret = parcel->writeInt32(rect.bottom);
-    if (ret != OK) return ret;
-  }
-
-  return OK;
-}
-
-status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) {
-  status_t ret = parcel->readUint64(&layer_.id);
-  if (ret != OK) return ret;
-
-  layer_.buffer = new GraphicBuffer();
-  ret = parcel->read(*layer_.buffer);
-  if (ret != OK) {
-    layer_.buffer.clear();
-    return ret;
-  }
-
-  bool has_fence = 0;
-  ret = parcel->readBool(&has_fence);
-  if (ret != OK) return ret;
-
-  if (has_fence)
-    layer_.fence = new Fence(dup(parcel->readFileDescriptor()));
-  else
-    layer_.fence = new Fence();
-
-  ret = parcel->readInt32(&layer_.display_frame.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.alpha);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.type);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.app_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.z_order);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.cursor_x);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.cursor_y);
-  if (ret != OK) return ret;
-
-  uint32_t color;
-  ret = parcel->readUint32(&color);
-  if (ret != OK) return ret;
-  layer_.color.r = color & 0xFF;
-  layer_.color.g = (color >> 8) & 0xFF;
-  layer_.color.b = (color >> 16) & 0xFF;
-  layer_.color.a = (color >> 24) & 0xFF;
-
-  ret = parcel->readInt32(&layer_.dataspace);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.transform);
-  if (ret != OK) return ret;
-
-  uint32_t size;
-  ret = parcel->readUint32(&size);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < size; i++) {
-    hwc_rect_t rect;
-    ret = parcel->readInt32(&rect.left);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.top);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.right);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.bottom);
-    if (ret != OK) return ret;
-
-    layer_.visible_regions.push_back(rect);
-  }
-
-  ret = parcel->readUint32(&size);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < size; i++) {
-    hwc_rect_t rect;
-    ret = parcel->readInt32(&rect.left);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.top);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.right);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.bottom);
-    if (ret != OK) return ret;
-
-    layer_.damaged_regions.push_back(rect);
-  }
-
-  return OK;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
deleted file mode 100644
index 6d2ac09..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerLayer : public Parcelable {
- public:
-  ParcelableComposerLayer();
-  explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer);
-  ~ParcelableComposerLayer() override;
-
-  ComposerView::ComposerLayer layer() const { return layer_; }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  ComposerView::ComposerLayer layer_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
deleted file mode 100644
index 9486f3c..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "android/dvr/parcelable_unique_fd.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableUniqueFd::ParcelableUniqueFd() {}
-
-ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence)
-    : fence_(dup(fence.get())) {}
-
-ParcelableUniqueFd::~ParcelableUniqueFd() {}
-
-status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeBool(fence_.get() >= 0);
-  if (ret != OK) return ret;
-
-  if (fence_.get() >= 0)
-    ret = parcel->writeUniqueFileDescriptor(fence_);
-
-  return ret;
-}
-
-status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) {
-  bool has_fence = 0;
-  status_t ret = parcel->readBool(&has_fence);
-  if (ret != OK) return ret;
-
-  if (has_fence)
-    ret = parcel->readUniqueFileDescriptor(&fence_);
-
-  return ret;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
deleted file mode 100644
index c4216f6..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-
-#include <android-base/unique_fd.h>
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace dvr {
-
-// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the
-// case where the FD is invalid (-1), unlike FileDescriptor which expects a
-// valid FD.
-class ParcelableUniqueFd : public Parcelable {
- public:
-  ParcelableUniqueFd();
-  explicit ParcelableUniqueFd(const base::unique_fd& fence);
-  ~ParcelableUniqueFd() override;
-
-  void set_fence(const base::unique_fd& fence) {
-    fence_.reset(dup(fence.get()));
-  }
-  base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  base::unique_fd fence_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
deleted file mode 100644
index dd1603d..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <hardware/gralloc.h>
-#include <hardware/gralloc1.h>
-#include <log/log.h>
-
-#include <memory>
-
-#include "impl/vr_hwc.h"
-#include "impl/vr_composer_client.h"
-
-namespace android {
-namespace dvr {
-
-using android::frameworks::vr::composer::V2_0::IVrComposerClient;
-
-VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
-    : ComposerClient(&hal), mVrHal(hal) {
-  if (!init()) {
-      LOG_ALWAYS_FATAL("failed to initialize VrComposerClient");
-  }
-}
-
-VrComposerClient::~VrComposerClient() {}
-
-std::unique_ptr<ComposerCommandEngine>
-VrComposerClient::createCommandEngine() {
-  return std::make_unique<VrCommandEngine>(*this);
-}
-
-VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
-    : ComposerCommandEngine(client.mHal, client.mResources.get()),
-      mVrHal(client.mVrHal) {}
-
-VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
-
-bool VrComposerClient::VrCommandEngine::executeCommand(
-    hardware::graphics::composer::V2_1::IComposerClient::Command command,
-    uint16_t length) {
-  IVrComposerClient::VrCommand vrCommand =
-      static_cast<IVrComposerClient::VrCommand>(command);
-  switch (vrCommand) {
-    case IVrComposerClient::VrCommand::SET_LAYER_INFO:
-      return executeSetLayerInfo(length);
-    case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA:
-      return executeSetClientTargetMetadata(length);
-    case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA:
-      return executeSetLayerBufferMetadata(length);
-    default:
-      return ComposerCommandEngine::executeCommand(command, length);
-  }
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) {
-  if (length != 2) {
-    return false;
-  }
-
-  auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
-  if (err != Error::NONE) {
-    mWriter->setError(getCommandLoc(), err);
-  }
-
-  return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata(
-    uint16_t length) {
-  if (length != 7)
-    return false;
-
-  auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
-  if (err != Error::NONE)
-    mWriter->setError(getCommandLoc(), err);
-
-  return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata(
-    uint16_t length) {
-  if (length != 7)
-    return false;
-
-  auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
-                                           readBufferMetadata());
-  if (err != Error::NONE)
-    mWriter->setError(getCommandLoc(), err);
-
-  return true;
-}
-
-IVrComposerClient::BufferMetadata
-VrComposerClient::VrCommandEngine::readBufferMetadata() {
-  IVrComposerClient::BufferMetadata metadata = {
-      .width = read(),
-      .height = read(),
-      .stride = read(),
-      .layerCount = read(),
-      .format =
-          static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
-              readSigned()),
-      .usage = read64(),
-  };
-  return metadata;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
deleted file mode 100644
index 1b2b5f4..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.1/ComposerCommandEngine.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-
-namespace android {
-namespace dvr {
-
-class VrHwc;
-
-using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_3::hal::ComposerHal;
-using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
-
-using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
-
-class VrComposerClient : public ComposerClient {
- public:
-  explicit VrComposerClient(android::dvr::VrHwc& hal);
-  virtual ~VrComposerClient();
-
- private:
-  class VrCommandEngine : public ComposerCommandEngine {
-   public:
-    explicit VrCommandEngine(VrComposerClient& client);
-    ~VrCommandEngine() override;
-
-    bool executeCommand(
-        hardware::graphics::composer::V2_1::IComposerClient::Command command,
-        uint16_t length) override;
-
-   private:
-    bool executeSetLayerInfo(uint16_t length);
-    bool executeSetClientTargetMetadata(uint16_t length);
-    bool executeSetLayerBufferMetadata(uint16_t length);
-
-    IVrComposerClient::BufferMetadata readBufferMetadata();
-
-    android::dvr::VrHwc& mVrHal;
-
-    VrCommandEngine(const VrCommandEngine&) = delete;
-    void operator=(const VrCommandEngine&) = delete;
-  };
-
-  VrComposerClient(const VrComposerClient&) = delete;
-  void operator=(const VrComposerClient&) = delete;
-
-  std::unique_ptr<ComposerCommandEngine> createCommandEngine() override;
-  dvr::VrHwc& mVrHal;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
deleted file mode 100644
index e530b16..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "impl/vr_hwc.h"
-
-#include "android-base/stringprintf.h"
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <private/dvr/display_client.h>
-#include <ui/Fence.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include "vr_composer_client.h"
-
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::base::StringPrintf;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-namespace types = android::hardware::graphics::common;
-
-namespace android {
-namespace dvr {
-namespace {
-
-const Display kDefaultDisplayId = 1;
-const Config kDefaultConfigId = 1;
-
-sp<GraphicBuffer> CreateGraphicBuffer(
-    const native_handle_t* handle,
-    const IVrComposerClient::BufferMetadata& metadata) {
-   sp<GraphicBuffer> buffer = new GraphicBuffer(
-      handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height,
-      static_cast<int32_t>(metadata.format), metadata.layerCount,
-      metadata.usage, metadata.stride);
-   if (buffer->initCheck() != OK) {
-     ALOGE("Failed to create graphic buffer");
-     return nullptr;
-   }
-
-   return buffer;
-}
-
-void GetPrimaryDisplaySize(int32_t* width, int32_t* height) {
-  *width = 1080;
-  *height = 1920;
-
-  int error = 0;
-  auto display_client = display::DisplayClient::Create(&error);
-  if (!display_client) {
-    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
-          error);
-    return;
-  }
-
-  auto status = display_client->GetDisplayMetrics();
-  if (!status) {
-    ALOGE("Could not get display metrics from display service : %s(%d)",
-          status.GetErrorMessage().c_str(), status.error());
-    return;
-  }
-
-  *width = status.get().display_width;
-  *height = status.get().display_height;
-}
-
-}  // namespace
-
-HwcDisplay::HwcDisplay(int32_t width, int32_t height)
-    : width_(width), height_(height) {}
-
-HwcDisplay::~HwcDisplay() {}
-
-bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
-                                 base::unique_fd fence) {
-  if (handle)
-    buffer_ = CreateGraphicBuffer(handle, buffer_metadata_);
-
-  fence_ = new Fence(fence.release());
-  return true;
-}
-
-void HwcDisplay::SetClientTargetMetadata(
-    const IVrComposerClient::BufferMetadata& metadata) {
-  buffer_metadata_ = metadata;
-}
-
-HwcLayer* HwcDisplay::CreateLayer() {
-  uint64_t layer_id = layer_ids_++;
-  layers_.push_back(HwcLayer(layer_id));
-  return &layers_.back();
-}
-
-HwcLayer* HwcDisplay::GetLayer(Layer id) {
-  for (size_t i = 0; i < layers_.size(); ++i)
-    if (layers_[i].info.id == id)
-      return &layers_[i];
-
-  return nullptr;
-}
-
-bool HwcDisplay::DestroyLayer(Layer id) {
-  for (auto it = layers_.begin(); it != layers_.end(); ++it) {
-    if (it->info.id == id) {
-      layers_.erase(it);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void HwcDisplay::GetChangedCompositionTypes(
-    std::vector<Layer>* layer_ids,
-    std::vector<IComposerClient::Composition>* types) {
-  std::sort(layers_.begin(), layers_.end(),
-            [](const auto& lhs, const auto& rhs) {
-              return lhs.info.z_order < rhs.info.z_order;
-            });
-
-  const size_t no_layer = std::numeric_limits<size_t>::max();
-  size_t first_client_layer = no_layer, last_client_layer = no_layer;
-  for (size_t i = 0; i < layers_.size(); ++i) {
-    switch (layers_[i].composition_type) {
-      case IComposerClient::Composition::SOLID_COLOR:
-      case IComposerClient::Composition::CURSOR:
-      case IComposerClient::Composition::SIDEBAND:
-        if (first_client_layer == no_layer)
-          first_client_layer = i;
-
-        last_client_layer = i;
-        break;
-      default:
-        break;
-    }
-  }
-
-  for (size_t i = 0; i < layers_.size(); ++i) {
-    if (i >= first_client_layer && i <= last_client_layer) {
-      if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
-        layer_ids->push_back(layers_[i].info.id);
-        types->push_back(IComposerClient::Composition::CLIENT);
-        layers_[i].composition_type = IComposerClient::Composition::CLIENT;
-      }
-
-      continue;
-    }
-
-    if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
-      layer_ids->push_back(layers_[i].info.id);
-      types->push_back(IComposerClient::Composition::DEVICE);
-      layers_[i].composition_type = IComposerClient::Composition::DEVICE;
-    }
-  }
-}
-
-Error HwcDisplay::GetFrame(
-    std::vector<ComposerView::ComposerLayer>* out_frames) {
-  bool queued_client_target = false;
-  std::vector<ComposerView::ComposerLayer> frame;
-  for (const auto& layer : layers_) {
-    if (layer.composition_type == IComposerClient::Composition::CLIENT) {
-      if (queued_client_target)
-        continue;
-
-      if (!buffer_.get()) {
-        ALOGE("Client composition requested but no client target buffer");
-        return Error::BAD_LAYER;
-      }
-
-      ComposerView::ComposerLayer client_target_layer = {
-          .buffer = buffer_,
-          .fence = fence_.get() ? fence_ : new Fence(-1),
-          .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
-            static_cast<int32_t>(buffer_->getHeight())},
-          .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
-            static_cast<float>(buffer_->getHeight())},
-          .blend_mode = IComposerClient::BlendMode::NONE,
-      };
-
-      frame.push_back(client_target_layer);
-      queued_client_target = true;
-    } else {
-      if (!layer.info.buffer.get() || !layer.info.fence.get()) {
-        ALOGV("Layer requested without valid buffer");
-        continue;
-      }
-
-      frame.push_back(layer.info);
-    }
-  }
-
-  out_frames->swap(frame);
-  return Error::NONE;
-}
-
-std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
-  std::vector<Layer> last_frame_layers;
-  last_frame_layers.swap(last_frame_layers_ids_);
-
-  for (const auto& layer : layers_)
-    last_frame_layers_ids_.push_back(layer.info.id);
-
-  return last_frame_layers;
-}
-
-void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) {
-  color_transform_hint_ = hint;
-  if (matrix)
-    memcpy(color_transform_, matrix, sizeof(color_transform_));
-}
-
-void HwcDisplay::dumpDebugInfo(std::string* result) const {
-  if (!result) {
-    return;
-  }
-  *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\
-      , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_);
-  *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\
-      layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height,
-      buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format);
-  for (const auto& layer : layers_) {
-    layer.dumpDebugInfo(result);
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// VrHwcClient
-
-VrHwc::VrHwc() {
-  vsync_callback_ = new VsyncCallback;
-}
-
-VrHwc::~VrHwc() {
-  vsync_callback_->SetEventCallback(nullptr);
-}
-
-bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
-
-void VrHwc::registerEventCallback(EventCallback* callback) {
-  std::unique_lock<std::mutex> lock(mutex_);
-  event_callback_ = callback;
-  int32_t width, height;
-  GetPrimaryDisplaySize(&width, &height);
-  // Create the primary display late to avoid initialization issues between
-  // VR HWC and SurfaceFlinger.
-  displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-
-  // Surface flinger will make calls back into vr_hwc when it receives the
-  // onHotplug() call, so it's important to release mutex_ here.
-  lock.unlock();
-  event_callback_->onHotplug(kDefaultDisplayId,
-                             hardware::graphics::composer::V2_1::
-                                 IComposerCallback::Connection::CONNECTED);
-  lock.lock();
-  UpdateVsyncCallbackEnabledLocked();
-}
-
-void VrHwc::unregisterEventCallback() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  event_callback_ = nullptr;
-  UpdateVsyncCallbackEnabledLocked();
-}
-
-uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
-
-Error VrHwc::destroyVirtualDisplay(Display display) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (display == kDefaultDisplayId || displays_.erase(display) == 0)
-    return Error::BAD_DISPLAY;
-  ComposerView::Frame frame;
-  frame.display_id = display;
-  frame.removed = true;
-  if (observer_)
-    observer_->OnNewFrame(frame);
-  return Error::NONE;
-}
-
-Error VrHwc::createLayer(Display display, Layer* outLayer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* layer = display_ptr->CreateLayer();
-  *outLayer = layer->info.id;
-  return Error::NONE;
-}
-
-Error VrHwc::destroyLayer(Display display, Layer layer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    return Error::BAD_DISPLAY;
-  }
-
-  return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
-}
-
-Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  *outConfig = kDefaultConfigId;
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayAttribute(Display display, Config config,
-                                 IComposerClient::Attribute attribute,
-                                 int32_t* outValue) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    return Error::BAD_DISPLAY;
-  }
-  if (config != kDefaultConfigId) {
-    return Error::BAD_CONFIG;
-  }
-
-  switch (attribute) {
-    case IComposerClient::Attribute::WIDTH:
-      *outValue = display_ptr->width();
-      break;
-    case IComposerClient::Attribute::HEIGHT:
-      *outValue = display_ptr->height();
-      break;
-    case IComposerClient::Attribute::VSYNC_PERIOD:
-      {
-        int error = 0;
-        auto display_client = display::DisplayClient::Create(&error);
-        if (!display_client) {
-          ALOGE("Could not connect to display service : %s(%d)",
-                strerror(error), error);
-          // Return a default value of 30 fps
-          *outValue = 1000 * 1000 * 1000 / 30;
-        } else {
-          auto metrics = display_client->GetDisplayMetrics();
-          *outValue = metrics.get().vsync_period_ns;
-        }
-      }
-      break;
-    case IComposerClient::Attribute::DPI_X:
-    case IComposerClient::Attribute::DPI_Y:
-      {
-        constexpr int32_t kDefaultDPI = 300;
-        int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
-        if (dpi <= 0) {
-          dpi = kDefaultDPI;
-        }
-        *outValue = 1000 * dpi;
-      }
-      break;
-    default:
-      return Error::BAD_PARAMETER;
-  }
-
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  std::vector<Config> configs(1, kDefaultConfigId);
-  *outConfigs = hidl_vec<Config>(configs);
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
-  *outName = hidl_string();
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayType(Display display,
-                            IComposerClient::DisplayType* outType) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    *outType = IComposerClient::DisplayType::INVALID;
-    return Error::BAD_DISPLAY;
-  }
-
-  if (display == kDefaultDisplayId)
-    *outType = IComposerClient::DisplayType::PHYSICAL;
-  else
-    *outType = IComposerClient::DisplayType::VIRTUAL;
-
-  return Error::NONE;
-}
-
-Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
-  *outSupport = false;
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  return Error::NONE;
-}
-
-Error VrHwc::setActiveConfig(Display display, Config config) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-  if (config != kDefaultConfigId)
-    return Error::BAD_CONFIG;
-
-  display_ptr->set_active_config(config);
-  return Error::NONE;
-}
-
-Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (enabled != IComposerClient::Vsync::ENABLE &&
-      enabled != IComposerClient::Vsync::DISABLE) {
-    return Error::BAD_PARAMETER;
-  }
-
-  Error set_vsync_result = Error::NONE;
-  if (display == kDefaultDisplayId) {
-    sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
-        defaultServiceManager()->getService(
-            String16(IVsyncService::GetServiceName())));
-    if (vsync_service == nullptr) {
-      ALOGE("Failed to get vsync service");
-      return Error::NO_RESOURCES;
-    }
-
-    if (enabled == IComposerClient::Vsync::ENABLE) {
-      ALOGI("Enable vsync");
-      display_ptr->set_vsync_enabled(true);
-      status_t result = vsync_service->registerCallback(vsync_callback_);
-      if (result != OK) {
-        ALOGE("%s service registerCallback() failed: %s (%d)",
-            IVsyncService::GetServiceName(), strerror(-result), result);
-        set_vsync_result = Error::NO_RESOURCES;
-      }
-    } else if (enabled == IComposerClient::Vsync::DISABLE) {
-      ALOGI("Disable vsync");
-      display_ptr->set_vsync_enabled(false);
-      status_t result = vsync_service->unregisterCallback(vsync_callback_);
-      if (result != OK) {
-        ALOGE("%s service unregisterCallback() failed: %s (%d)",
-            IVsyncService::GetServiceName(), strerror(-result), result);
-        set_vsync_result = Error::NO_RESOURCES;
-      }
-    }
-
-    UpdateVsyncCallbackEnabledLocked();
-  }
-
-  return set_vsync_result;
-}
-
-Error VrHwc::setColorTransform(Display display, const float* matrix,
-                               int32_t hint) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->SetColorTransform(matrix, hint);
-  return Error::NONE;
-}
-
-Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
-                             int32_t acquireFence, int32_t /* dataspace */,
-                             const std::vector<hwc_rect_t>& /* damage */) {
-  base::unique_fd fence(acquireFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (target == nullptr)
-    return Error::NONE;
-
-  if (!display_ptr->SetClientTarget(target, std::move(fence)))
-    return Error::BAD_PARAMETER;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
-                             int32_t releaseFence) {
-  base::unique_fd fence(releaseFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  // TODO(dnicoara): Is it necessary to do anything here?
-  return Error::NONE;
-}
-
-Error VrHwc::validateDisplay(
-    Display display, std::vector<Layer>* outChangedLayers,
-    std::vector<IComposerClient::Composition>* outCompositionTypes,
-    uint32_t* /* outDisplayRequestMask */,
-    std::vector<Layer>* /* outRequestedLayers */,
-    std::vector<uint32_t>* /* outRequestMasks */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->GetChangedCompositionTypes(outChangedLayers,
-                                          outCompositionTypes);
-  return Error::NONE;
-}
-
-Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
-
-Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
-                            std::vector<Layer>* outLayers,
-                            std::vector<int32_t>* outReleaseFences) {
-  *outPresentFence = -1;
-  outLayers->clear();
-  outReleaseFences->clear();
-
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  ComposerView::Frame frame;
-  std::vector<Layer> last_frame_layers;
-  Error status = display_ptr->GetFrame(&frame.layers);
-  frame.display_id = display;
-  frame.display_width = display_ptr->width();
-  frame.display_height = display_ptr->height();
-  frame.active_config = display_ptr->active_config();
-  frame.power_mode = display_ptr->power_mode();
-  frame.vsync_enabled = display_ptr->vsync_enabled() ?
-      IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
-  frame.color_transform_hint = display_ptr->color_transform_hint();
-  frame.color_mode = display_ptr->color_mode();
-  memcpy(frame.color_transform, display_ptr->color_transform(),
-         sizeof(frame.color_transform));
-  if (status != Error::NONE)
-    return status;
-
-  last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
-
-  base::unique_fd fence;
-  if (observer_)
-    fence = observer_->OnNewFrame(frame);
-
-  if (fence.get() < 0)
-    return Error::NONE;
-
-  *outPresentFence = dup(fence.get());
-  outLayers->swap(last_frame_layers);
-  for (size_t i = 0; i < outLayers->size(); ++i)
-    outReleaseFences->push_back(dup(fence.get()));
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
-                                    int32_t y) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.cursor_x = x;
-  hwc_layer->info.cursor_y = y;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBuffer(Display display, Layer layer,
-                            buffer_handle_t buffer, int32_t acquireFence) {
-  base::unique_fd fence(acquireFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.buffer = CreateGraphicBuffer(
-      buffer, hwc_layer->buffer_metadata);
-  hwc_layer->info.fence = new Fence(fence.release());
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
-                                   const std::vector<hwc_rect_t>& damage) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.damaged_regions = damage;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.blend_mode =
-      static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerColor(Display display, Layer layer,
-                           IComposerClient::Color color) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.color = color;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerCompositionType(Display display, Layer layer,
-                                     int32_t type) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerDataspace(Display display, Layer layer,
-                               int32_t dataspace) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.dataspace = dataspace;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
-                                  const hwc_rect_t& frame) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.display_frame =
-      {frame.left, frame.top, frame.right, frame.bottom};
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.alpha = alpha;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
-                                    buffer_handle_t /* stream */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
-                                const hwc_frect_t& crop) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerTransform(Display display, Layer layer,
-                               int32_t transform) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.transform = transform;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
-                                   const std::vector<hwc_rect_t>& visible) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.visible_regions = visible;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.z_order = z;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
-                          uint32_t appId) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.type = type;
-  hwc_layer->info.app_id = appId;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setClientTargetMetadata(
-    Display display, const IVrComposerClient::BufferMetadata& metadata) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->SetClientTargetMetadata(metadata);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBufferMetadata(
-    Display display, Layer layer,
-    const IVrComposerClient::BufferMetadata& metadata) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->buffer_metadata = metadata;
-
-  return Error::NONE;
-}
-
-Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) {
-  hidl_cb(hidl_vec<Capability>());
-  return Void();
-}
-
-Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-  std::string result;
-
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    result = "\nVrHwc states:\n";
-    for (const auto& pair : displays_) {
-      result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first);
-      pair.second->dumpDebugInfo(&result);
-    }
-    result += "\n";
-  }
-
-  hidl_cb(hidl_string(result));
-  return Void();
-}
-
-Return<void> VrHwc::createClient(createClient_cb hidl_cb) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  Error status = Error::NONE;
-  sp<VrComposerClient> client;
-  if (!client_.promote().get()) {
-    client = new VrComposerClient(*this);
-  } else {
-    ALOGE("Already have a client");
-    status = Error::NO_RESOURCES;
-  }
-
-  client_ = client;
-  hidl_cb(status, client);
-  return Void();
-}
-
-Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  Error status = Error::NONE;
-  sp<VrComposerClient> client;
-  if (!client_.promote().get()) {
-    client = new VrComposerClient(*this);
-  } else {
-    ALOGE("Already have a client");
-    status = Error::NO_RESOURCES;
-  }
-
-  client_ = client;
-  hidl_cb(status, client);
-  return Void();
-}
-
-void VrHwc::ForceDisplaysRefresh() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (event_callback_ != nullptr) {
-    for (const auto& pair : displays_)
-      event_callback_->onRefresh(pair.first);
-  }
-}
-
-void VrHwc::RegisterObserver(Observer* observer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (observer_)
-    ALOGE("Overwriting observer");
-  else
-    observer_ = observer;
-}
-
-void VrHwc::UnregisterObserver(Observer* observer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (observer != observer_)
-    ALOGE("Trying to unregister unknown observer");
-  else
-    observer_ = nullptr;
-}
-
-HwcDisplay* VrHwc::FindDisplay(Display display) {
-  auto iter = displays_.find(display);
-  return iter == displays_.end() ? nullptr : iter->second.get();
-}
-
-void VrHwc::UpdateVsyncCallbackEnabledLocked() {
-  auto primary_display = FindDisplay(kDefaultDisplayId);
-  LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
-      "Should have created the primary display by now");
-  bool send_vsync =
-      event_callback_ != nullptr && primary_display->vsync_enabled();
-  vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
-}
-
-Return<void> VrHwc::debug(const hidl_handle& fd,
-                          const hidl_vec<hidl_string>& args) {
-  std::string result;
-
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    for (const auto& pair : displays_) {
-      result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
-      pair.second->dumpDebugInfo(&result);
-    }
-    result += "\n";
-  }
-
-  FILE* out = fdopen(dup(fd->data[0]), "w");
-  fprintf(out, "%s", result.c_str());
-  fclose(out);
-
-  return Void();
-}
-
-void HwcLayer::dumpDebugInfo(std::string* result) const {
-  if (!result) {
-    return;
-  }
-  *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\
-      cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\
-      display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n",
-      composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y,
-      info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform,
-      info.display_frame.left, info.display_frame.top, info.display_frame.right,
-      info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right,
-      info.crop.bottom, info.blend_mode);
-  *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\
-      , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride,
-      buffer_metadata.layerCount, buffer_metadata.format);
-}
-
-status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
-  ATRACE_NAME("vr_hwc onVsync");
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (callback_ != nullptr)
-    callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
-  return OK;
-}
-
-void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  callback_ = callback;
-}
-
-// composer::V2_2::ComposerHal
-Error VrHwc::setReadbackBuffer(Display display,
-                               const native_handle_t* bufferHandle,
-                               android::base::unique_fd fenceFd) {
-  return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferFence(Display display,
-                                    android::base::unique_fd* outFenceFd) {
-  return Error::NONE;
-}
-
-Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                      types::V1_1::PixelFormat* format,
-                                      Display* outDisplay) {
-  *format = types::V1_1::PixelFormat::RGBA_8888;
-  *outDisplay = display_count_;
-  displays_[display_count_].reset(new HwcDisplay(width, height));
-  display_count_++;
-  return Error::NONE;
-}
-
-Error VrHwc::setPowerMode_2_2(Display display,
-                              IComposerClient::PowerMode mode) {
-  bool dozeSupported = false;
-
-  Error dozeSupportError = getDozeSupport(display, &dozeSupported);
-
-  if (dozeSupportError != Error::NONE)
-    return dozeSupportError;
-
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < IComposerClient::PowerMode::OFF ||
-      mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
-    return Error::BAD_PARAMETER;
-  }
-
-  if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE ||
-                         mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
-    return Error::UNSUPPORTED;
-  }
-
-  display_ptr->set_power_mode(mode);
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerFloatColor(Display display, Layer layer,
-                                IComposerClient::FloatColor color) {
-  return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
-                              std::vector<RenderIntent>* outIntents) {
-  return Error::NONE;
-}
-
-std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
-    types::V1_1::Dataspace dataspace) {
-  return {};
-}
-
-// composer::V2_3::ComposerHal
-Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
-                                    hidl_vec<Hdr>* /*outTypes*/,
-                                    float* outMaxLuminance,
-                                    float* outMaxAverageLuminance,
-                                    float* outMinLuminance) {
-  *outMaxLuminance = 0;
-  *outMaxAverageLuminance = 0;
-  *outMinLuminance = 0;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadata_2_3(
-    Display display, Layer layer,
-    const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
-  return Error::NONE;
-}
-
-Error VrHwc::getPerFrameMetadataKeys_2_3(
-    Display display,
-    std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
-  return Error::NONE;
-}
-
-Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
-                              RenderIntent intent) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
-    return Error::BAD_PARAMETER;
-
-  display_ptr->set_color_mode(mode);
-  return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode,
-                                  std::vector<RenderIntent>* outIntents) {
-  return Error::NONE;
-}
-
-Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
-  return Error::NONE;
-}
-
-Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
-                                        uint32_t height, PixelFormat format,
-                                        Dataspace dataspace) {
-  return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
-                                             PixelFormat* outFormat,
-                                             Dataspace* outDataspace) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                          std::vector<uint8_t>* outData) {
-  int error = 0;
-  auto display_client = display::DisplayClient::Create(&error);
-  if (!display_client) {
-    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
-          error);
-    return Error::BAD_CONFIG;
-  }
-  auto edid_data = display_client->GetConfigurationData(
-      display::ConfigFileType::kDeviceEdid);
-  auto display_identification_port =
-      display_client->GetDisplayIdentificationPort();
-  *outPort = display_identification_port.get();
-
-  std::copy(edid_data.get().begin(), edid_data.get().end(),
-            std::back_inserter(*outData));
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerColorTransform(Display display, Layer layer,
-                                    const float* matrix) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSamplingAttributes(
-    Display display, PixelFormat& format, Dataspace& dataspace,
-    hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
-  return Error::NONE;
-}
-
-Error VrHwc::setDisplayedContentSamplingEnabled(
-    Display display, IComposerClient::DisplayedContentSampling enable,
-    hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
-    uint64_t maxFrames) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
-                                       uint64_t timestamp, uint64_t& frameCount,
-                                       hidl_vec<uint64_t>& sampleComponent0,
-                                       hidl_vec<uint64_t>& sampleComponent1,
-                                       hidl_vec<uint64_t>& sampleComponent2,
-                                       hidl_vec<uint64_t>& sampleComponent3) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayCapabilities(
-    Display display,
-    std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadataBlobs(
-    Display display, Layer layer,
-    std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
-  return Error::NONE;
-}
-
-Error VrHwc::setDisplayBrightness(Display display, float brightness) {
-  return Error::NONE;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
deleted file mode 100644
index 3e3a630..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-
-#include <android-base/unique_fd.h>
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <composer-hal/2.3/ComposerHal.h>
-#include <private/dvr/vsync_service.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/StrongPointer.h>
-
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::frameworks::vr::composer::V2_0;
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::hardware::hidl_bitfield;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::graphics::composer::V2_1::Config;
-using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
-using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
-
-namespace android {
-
-class Fence;
-
-namespace dvr {
-
-class VrComposerClient;
-
-using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
-
-namespace types = android::hardware::graphics::common;
-
-using types::V1_1::RenderIntent;
-using types::V1_2::ColorMode;
-using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
-using types::V1_2::PixelFormat;
-
-class ComposerView {
- public:
-  struct ComposerLayer {
-    using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
-    using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
-    using BlendMode =
-        hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
-
-    Layer id;
-    sp<GraphicBuffer> buffer;
-    sp<Fence> fence;
-    Recti display_frame;
-    Rectf crop;
-    BlendMode blend_mode;
-    float alpha;
-    uint32_t type;
-    uint32_t app_id;
-    uint32_t z_order;
-    int32_t cursor_x;
-    int32_t cursor_y;
-    IComposerClient::Color color;
-    int32_t dataspace;
-    int32_t transform;
-    std::vector<hwc_rect_t> visible_regions;
-    std::vector<hwc_rect_t> damaged_regions;
-  };
-
-  struct Frame {
-    Display display_id;
-    // This is set to true to notify the upper layer that the display is
-    // being removed, or left false in the case of a normal frame. The upper
-    // layer tracks display IDs and will handle new ones showing up.
-    bool removed = false;
-    int32_t display_width;
-    int32_t display_height;
-    Config active_config;
-    ColorMode color_mode;
-    IComposerClient::PowerMode power_mode;
-    IComposerClient::Vsync vsync_enabled;
-    float color_transform[16];
-    int32_t color_transform_hint;
-    std::vector<ComposerLayer> layers;
-  };
-
-  class Observer {
-   public:
-    virtual ~Observer() {}
-
-    // Returns a list of layers that need to be shown together. Layers are
-    // returned in z-order, with the lowest layer first.
-    virtual base::unique_fd OnNewFrame(const Frame& frame) = 0;
-  };
-
-  virtual ~ComposerView() {}
-
-  virtual void ForceDisplaysRefresh() = 0;
-  virtual void RegisterObserver(Observer* observer) = 0;
-  virtual void UnregisterObserver(Observer* observer) = 0;
-};
-
-struct HwcLayer {
-  using Composition =
-      hardware::graphics::composer::V2_3::IComposerClient::Composition;
-
-  explicit HwcLayer(Layer new_id) { info.id = new_id; }
-
-  void dumpDebugInfo(std::string* result) const;
-
-  Composition composition_type;
-  ComposerView::ComposerLayer info;
-  IVrComposerClient::BufferMetadata buffer_metadata;
-};
-
-class HwcDisplay {
- public:
-  HwcDisplay(int32_t width, int32_t height);
-  ~HwcDisplay();
-
-  int32_t width() const { return width_; }
-  int32_t height() const { return height_; }
-
-  HwcLayer* CreateLayer();
-  bool DestroyLayer(Layer id);
-  HwcLayer* GetLayer(Layer id);
-
-  bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
-  void SetClientTargetMetadata(
-      const IVrComposerClient::BufferMetadata& metadata);
-
-  void GetChangedCompositionTypes(
-      std::vector<Layer>* layer_ids,
-      std::vector<IComposerClient::Composition>* composition);
-
-  Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
-
-  std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
-
-  Config active_config() const { return active_config_; }
-  void set_active_config(Config config) { active_config_ = config; }
-
-  ColorMode color_mode() const { return color_mode_; }
-  void set_color_mode(ColorMode mode) { color_mode_ = mode; }
-
-  IComposerClient::PowerMode power_mode() const { return power_mode_; }
-  void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
-
-  bool vsync_enabled() const { return vsync_enabled_; }
-  void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
-
-  const float* color_transform() const { return color_transform_; }
-  int32_t color_transform_hint() const { return color_transform_hint_; }
-  void SetColorTransform(const float* matrix, int32_t hint);
-
-  void dumpDebugInfo(std::string* result) const;
-
- private:
-  // The client target buffer and the associated fence.
-  sp<GraphicBuffer> buffer_;
-  IVrComposerClient::BufferMetadata buffer_metadata_;
-  sp<Fence> fence_;
-
-  // List of currently active layers.
-  std::vector<HwcLayer> layers_;
-
-  std::vector<Layer> last_frame_layers_ids_;
-
-  // Layer ID generator.
-  uint64_t layer_ids_ = 1;
-
-  int32_t width_;
-  int32_t height_;
-
-  Config active_config_;
-  ColorMode color_mode_;
-  IComposerClient::PowerMode power_mode_;
-  bool vsync_enabled_ = false;
-  float color_transform_[16];
-  int32_t color_transform_hint_;
-
-  HwcDisplay(const HwcDisplay&) = delete;
-  void operator=(const HwcDisplay&) = delete;
-};
-
-class VrHwc : public IComposer, public ComposerHal, public ComposerView {
- public:
-  VrHwc();
-  ~VrHwc() override;
-
-  Error setLayerInfo(Display display, Layer layer, uint32_t type,
-                     uint32_t appId);
-  Error setClientTargetMetadata(
-      Display display, const IVrComposerClient::BufferMetadata& metadata);
-  Error setLayerBufferMetadata(
-      Display display, Layer layer,
-      const IVrComposerClient::BufferMetadata& metadata);
-
-  // composer::V2_1::ComposerHal
-  bool hasCapability(hwc2_capability_t capability) override;
-
-  std::string dumpDebugInfo() override { return {}; }
-
-  void registerEventCallback(ComposerHal::EventCallback* callback) override;
-  void unregisterEventCallback() override;
-
-  uint32_t getMaxVirtualDisplayCount() override;
-  Error destroyVirtualDisplay(Display display) override;
-
-  Error createLayer(Display display, Layer* outLayer) override;
-  Error destroyLayer(Display display, Layer layer) override;
-
-  Error getActiveConfig(Display display, Config* outConfig) override;
-  Error getDisplayAttribute(Display display, Config config,
-                            IComposerClient::Attribute attribute,
-                            int32_t* outValue) override;
-  Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-  Error getDisplayName(Display display, hidl_string* outName) override;
-  Error getDisplayType(Display display,
-                       IComposerClient::DisplayType* outType) override;
-  Error getDozeSupport(Display display, bool* outSupport) override;
-
-  Error setActiveConfig(Display display, Config config) override;
-  Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
-  Error setColorTransform(Display display, const float* matrix,
-                          int32_t hint) override;
-  Error setClientTarget(Display display, buffer_handle_t target,
-                        int32_t acquireFence, int32_t dataspace,
-                        const std::vector<hwc_rect_t>& damage) override;
-  Error setOutputBuffer(Display display, buffer_handle_t buffer,
-                        int32_t releaseFence) override;
-  Error validateDisplay(
-      Display display, std::vector<Layer>* outChangedLayers,
-      std::vector<IComposerClient::Composition>* outCompositionTypes,
-      uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-      std::vector<uint32_t>* outRequestMasks) override;
-  Error acceptDisplayChanges(Display display) override;
-  Error presentDisplay(Display display, int32_t* outPresentFence,
-                       std::vector<Layer>* outLayers,
-                       std::vector<int32_t>* outReleaseFences) override;
-
-  Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
-                               int32_t y) override;
-  Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                       int32_t acquireFence) override;
-  Error setLayerSurfaceDamage(Display display, Layer layer,
-                              const std::vector<hwc_rect_t>& damage) override;
-  Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-  Error setLayerColor(Display display, Layer layer,
-                      IComposerClient::Color color) override;
-  Error setLayerCompositionType(Display display, Layer layer,
-                                int32_t type) override;
-  Error setLayerDataspace(Display display, Layer layer,
-                          int32_t dataspace) override;
-  Error setLayerDisplayFrame(Display display, Layer layer,
-                             const hwc_rect_t& frame) override;
-  Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-  Error setLayerSidebandStream(Display display, Layer layer,
-                               buffer_handle_t stream) override;
-  Error setLayerSourceCrop(Display display, Layer layer,
-                           const hwc_frect_t& crop) override;
-  Error setLayerTransform(Display display, Layer layer,
-                          int32_t transform) override;
-  Error setLayerVisibleRegion(Display display, Layer layer,
-                              const std::vector<hwc_rect_t>& visible) override;
-  Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
-  // composer::V2_2::ComposerHal
-  Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
-                          android::base::unique_fd fenceFd) override;
-  Error getReadbackBufferFence(Display display,
-                               android::base::unique_fd* outFenceFd) override;
-  Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                 types::V1_1::PixelFormat* format,
-                                 Display* outDisplay) override;
-  Error setPowerMode_2_2(Display display,
-                         IComposerClient::PowerMode mode) override;
-  Error setLayerFloatColor(Display display, Layer layer,
-                           IComposerClient::FloatColor color) override;
-  Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
-                         std::vector<RenderIntent>* outIntents) override;
-  std::array<float, 16> getDataspaceSaturationMatrix(
-      types::V1_1::Dataspace dataspace) override;
-
-  // composer::V2_3::ComposerHal
-  Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
-                               float* outMaxLuminance,
-                               float* outMaxAverageLuminance,
-                               float* outMinLuminance) override;
-  Error setLayerPerFrameMetadata_2_3(
-      Display display, Layer layer,
-      const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
-  Error getPerFrameMetadataKeys_2_3(
-      Display display,
-      std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
-  Error setColorMode_2_3(Display display, ColorMode mode,
-                         RenderIntent intent) override;
-  Error getRenderIntents_2_3(Display display, ColorMode mode,
-                             std::vector<RenderIntent>* outIntents) override;
-  Error getColorModes_2_3(Display display,
-                          hidl_vec<ColorMode>* outModes) override;
-  Error getClientTargetSupport_2_3(Display display, uint32_t width,
-                                   uint32_t height, PixelFormat format,
-                                   Dataspace dataspace) override;
-  Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
-                                        Dataspace* outDataspace) override;
-  Error getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                     std::vector<uint8_t>* outData) override;
-  Error setLayerColorTransform(Display display, Layer layer,
-                               const float* matrix) override;
-  Error getDisplayedContentSamplingAttributes(
-      Display display, PixelFormat& format, Dataspace& dataspace,
-      hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
-      override;
-  Error setDisplayedContentSamplingEnabled(
-      Display display, IComposerClient::DisplayedContentSampling enable,
-      hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
-      uint64_t maxFrames) override;
-  Error getDisplayedContentSample(
-      Display display, uint64_t maxFrames, uint64_t timestamp,
-      uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
-      hidl_vec<uint64_t>& sampleComponent1,
-      hidl_vec<uint64_t>& sampleComponent2,
-      hidl_vec<uint64_t>& sampleComponent3) override;
-  Error getDisplayCapabilities(Display display,
-                               std::vector<IComposerClient::DisplayCapability>*
-                                   outCapabilities) override;
-  Error setLayerPerFrameMetadataBlobs(
-      Display display, Layer layer,
-      std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
-  Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
-  Error setDisplayBrightness(Display display, float brightness) override;
-
-  // IComposer:
-  Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-  Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-  Return<void> createClient(createClient_cb hidl_cb) override;
-  Return<void> createClient_2_3(
-      IComposer::createClient_2_3_cb hidl_cb) override;
-
-  // ComposerView:
-  void ForceDisplaysRefresh() override;
-  void RegisterObserver(Observer* observer) override;
-  void UnregisterObserver(Observer* observer) override;
-
-  Return<void> debug(const hidl_handle& fd,
-                     const hidl_vec<hidl_string>& args) override;
-
- private:
-  class VsyncCallback : public BnVsyncCallback {
-   public:
-    status_t onVsync(int64_t vsync_timestamp) override;
-    void SetEventCallback(EventCallback* callback);
-   private:
-    std::mutex mutex_;
-    EventCallback* callback_;
-  };
-
-  HwcDisplay* FindDisplay(Display display);
-
-  // Re-evaluate whether or not we should start making onVsync() callbacks to
-  // the client. We need enableCallback(true) to have been called, and
-  // setVsyncEnabled() to have been called for the primary display. The caller
-  // must have mutex_ locked already.
-  void UpdateVsyncCallbackEnabledLocked();
-
-  wp<VrComposerClient> client_;
-
-  // Guard access to internal state from binder threads.
-  std::mutex mutex_;
-
-  std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
-  Display display_count_ = 2;
-
-  EventCallback* event_callback_ = nullptr;
-  Observer* observer_ = nullptr;
-
-  sp<VsyncCallback> vsync_callback_;
-
-  VrHwc(const VrHwc&) = delete;
-  void operator=(const VrHwc&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp
deleted file mode 100644
index 2e70928..0000000
--- a/services/vr/hardware_composer/tests/vr_composer_test.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <android/dvr/BnVrComposerCallback.h>
-#include <binder/IServiceManager.h>
-#include <gtest/gtest.h>
-#include <sys/eventfd.h>
-#include <vr_composer.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-const char kVrDisplayName[] = "VrDisplay_Test";
-
-class TestComposerView : public ComposerView {
- public:
-  TestComposerView() {}
-  ~TestComposerView() override = default;
-
-  size_t display_refresh_count() const { return display_refresh_count_; }
-
-  void ForceDisplaysRefresh() override { display_refresh_count_++; }
-  void RegisterObserver(Observer* observer) override {}
-  void UnregisterObserver(Observer* observer) override {}
-
-  TestComposerView(const TestComposerView&) = delete;
-  void operator=(const TestComposerView&) = delete;
-
- private:
-  size_t display_refresh_count_ = 0;
-};
-
-class TestComposerCallback : public BnVrComposerCallback {
- public:
-  TestComposerCallback() {}
-  ~TestComposerCallback() override = default;
-
-  ComposerView::Frame last_frame() const { return last_frame_; }
-
-  binder::Status onNewFrame(
-      const ParcelableComposerFrame& frame,
-      ParcelableUniqueFd* /* fence */) override {
-    last_frame_ = frame.frame();
-    return binder::Status::ok();
-  }
-
- private:
-  ComposerView::Frame last_frame_;
-
-  TestComposerCallback(const TestComposerCallback&) = delete;
-  void operator=(const TestComposerCallback&) = delete;
-};
-
-class TestComposerCallbackWithFence : public TestComposerCallback {
- public:
-  ~TestComposerCallbackWithFence() override = default;
-
-  binder::Status onNewFrame(
-      const ParcelableComposerFrame& frame,
-      ParcelableUniqueFd* fence) override {
-    binder::Status status = TestComposerCallback::onNewFrame(frame, fence);
-
-    base::unique_fd fd(eventfd(0, 0));
-    EXPECT_LE(0, fd.get());
-    fence->set_fence(fd);
-
-    return status;
-  }
-};
-
-sp<GraphicBuffer> CreateBuffer() {
-  return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888,
-                           GraphicBuffer::USAGE_HW_TEXTURE);
-}
-
-}  // namespace
-
-class VrComposerTest : public testing::Test {
- public:
-  VrComposerTest() : composer_(new VrComposer(&composer_view_)) {}
-  ~VrComposerTest() override = default;
-
-  sp<IVrComposer> GetComposerProxy() const {
-    sp<IServiceManager> sm(defaultServiceManager());
-    return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName)));
-  }
-
-  void SetUp() override {
-    sp<IServiceManager> sm(defaultServiceManager());
-    EXPECT_EQ(OK,
-              sm->addService(String16(kVrDisplayName), composer_, false));
-  }
-
- protected:
-  TestComposerView composer_view_;
-  sp<VrComposer> composer_;
-
-  VrComposerTest(const VrComposerTest&) = delete;
-  void operator=(const VrComposerTest&) = delete;
-};
-
-TEST_F(VrComposerTest, TestWithoutObserver) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  ComposerView::Frame frame;
-
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithObserver) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  sp<TestComposerCallback> callback = new TestComposerCallback();
-  ASSERT_EQ(0, composer_view_.display_refresh_count());
-  ASSERT_TRUE(composer->registerObserver(callback).isOk());
-  ASSERT_EQ(1, composer_view_.display_refresh_count());
-
-  ComposerView::Frame frame;
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithOneLayer) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  sp<TestComposerCallback> callback = new TestComposerCallbackWithFence();
-  ASSERT_TRUE(composer->registerObserver(callback).isOk());
-
-  ComposerView::Frame frame;
-  frame.display_id = 1;
-  frame.removed = false;
-  frame.display_width = 600;
-  frame.display_height = 400;
-  frame.layers.push_back(ComposerView::ComposerLayer{
-    .id = 1,
-    .buffer = CreateBuffer(),
-    .fence = new Fence(eventfd(0, 0)),
-    .display_frame = {0, 0, 600, 400},
-    .crop = {0.0f, 0.0f, 600.0f, 400.0f},
-    .blend_mode = IComposerClient::BlendMode::NONE,
-    .alpha = 1.0f,
-    .type = 1,
-    .app_id = 1,
-  });
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_LE(0, fence.get());
-
-  ComposerView::Frame received_frame = callback->last_frame();
-  ASSERT_EQ(frame.display_id, received_frame.display_id);
-  ASSERT_EQ(frame.display_width, received_frame.display_width);
-  ASSERT_EQ(frame.display_height, received_frame.display_height);
-  ASSERT_EQ(frame.removed, received_frame.removed);
-  ASSERT_EQ(1u, received_frame.layers.size());
-  ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id);
-  ASSERT_NE(nullptr, received_frame.layers[0].buffer.get());
-  ASSERT_TRUE(received_frame.layers[0].fence->isValid());
-  ASSERT_EQ(frame.layers[0].display_frame.left,
-            received_frame.layers[0].display_frame.left);
-  ASSERT_EQ(frame.layers[0].display_frame.top,
-            received_frame.layers[0].display_frame.top);
-  ASSERT_EQ(frame.layers[0].display_frame.right,
-            received_frame.layers[0].display_frame.right);
-  ASSERT_EQ(frame.layers[0].display_frame.bottom,
-            received_frame.layers[0].display_frame.bottom);
-  ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left);
-  ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top);
-  ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right);
-  ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom);
-  ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode);
-  ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha);
-  ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type);
-  ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp
deleted file mode 100644
index d93f370..0000000
--- a/services/vr/hardware_composer/vr_composer.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "vr_composer.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionCache.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-bool CheckPermission() {
-  const android::IPCThreadState* ipc = android::IPCThreadState::self();
-  const pid_t pid = ipc->getCallingPid();
-  const uid_t uid = ipc->getCallingUid();
-  const bool permission = PermissionCache::checkPermission(
-      String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid);
-  if (!permission)
-    ALOGE("permission denied to pid=%d uid=%u", pid, uid);
-
-  return permission;
-}
-
-}  // namespace
-
-VrComposer::VrComposer(ComposerView* composer_view)
-  : composer_view_(composer_view) {
-  composer_view_->RegisterObserver(this);
-}
-
-VrComposer::~VrComposer() {
-  composer_view_->UnregisterObserver(this);
-}
-
-binder::Status VrComposer::registerObserver(
-    const sp<IVrComposerCallback>& callback) {
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    if (!CheckPermission())
-      return binder::Status::fromStatusT(PERMISSION_DENIED);
-
-    if (callback_.get()) {
-      ALOGE("Failed to register callback, already registered");
-      return binder::Status::fromStatusT(ALREADY_EXISTS);
-    }
-
-    callback_ = callback;
-    IInterface::asBinder(callback_)->linkToDeath(this);
-  }
-
-  // Don't take the lock to force display refresh otherwise it could end in a
-  // deadlock since HWC calls this with new frames and it has a lock of its own
-  // to serialize access to the display information.
-  composer_view_->ForceDisplaysRefresh();
-  return binder::Status::ok();
-}
-
-binder::Status VrComposer::clearObserver() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  callback_ = nullptr;
-  return binder::Status::ok();
-}
-
-base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  if (!callback_.get())
-    return base::unique_fd();
-
-  ParcelableComposerFrame parcelable_frame(frame);
-  ParcelableUniqueFd fence;
-  binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence);
-  if (!ret.isOk())
-    ALOGE("Failed to send new frame: %s", ret.toString8().string());
-
-  return fence.fence();
-}
-
-void VrComposer::binderDied(const wp<IBinder>& /* who */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  callback_ = nullptr;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h
deleted file mode 100644
index 1273352..0000000
--- a/services/vr/hardware_composer/vr_composer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-
-#include <android/dvr/BnVrComposer.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class VrComposerCallback;
-
-// Implementation of the IVrComposer service used to notify VR Window Manager
-// when SurfaceFlinger presents 2D UI changes.
-//
-// VR HWC updates the presented frame via the ComposerView::Observer interface.
-// On notification |callback_| is called to update VR Window Manager.
-// NOTE: If VR Window Manager isn't connected, the notification is a no-op.
-class VrComposer
-    : public BnVrComposer,
-      public ComposerView::Observer,
-      public IBinder::DeathRecipient {
- public:
-  explicit VrComposer(ComposerView* composer_view);
-  ~VrComposer() override;
-
-  // BnVrComposer:
-  binder::Status registerObserver(
-      const sp<IVrComposerCallback>& callback) override;
-
-  binder::Status clearObserver() override;
-
-  // ComposerView::Observer:
-  base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
-
- private:
-  // IBinder::DeathRecipient:
-  void binderDied(const wp<IBinder>& who) override;
-
-  std::mutex mutex_;
-
-  sp<IVrComposerCallback> callback_;
-
-  ComposerView* composer_view_;  // Not owned.
-
-  VrComposer(const VrComposer&) = delete;
-  void operator=(const VrComposer&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  //  ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
         "vkGetPhysicalDeviceMemoryProperties2",
         "vkGetPhysicalDeviceMemoryProperties2KHR",
         "vkGetPhysicalDeviceMultisamplePropertiesEXT",
+        "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
         "vkGetPhysicalDevicePresentRectanglesKHR",
         "vkGetPhysicalDeviceProperties",
         "vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4927150..a99355f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,8 +1027,11 @@
     }
 }
 
-bool GetAndroidNativeBufferSpecVersion9Support(
-    VkPhysicalDevice physicalDevice) {
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+    VkPhysicalDevice physicalDevice,
+    bool& support) {
+    support = false;
+
     const InstanceData& data = GetData(physicalDevice);
 
     // Call to get propertyCount
@@ -1038,6 +1041,10 @@
         physicalDevice, nullptr, &propertyCount, nullptr);
     ATRACE_END();
 
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
+
     // Call to enumerate properties
     std::vector<VkExtensionProperties> properties(propertyCount);
     ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
@@ -1045,6 +1052,10 @@
         physicalDevice, nullptr, &propertyCount, properties.data());
     ATRACE_END();
 
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
+
     for (uint32_t i = 0; i < propertyCount; i++) {
         auto& prop = properties[i];
 
@@ -1053,11 +1064,12 @@
             continue;
 
         if (prop.specVersion >= 9) {
-            return true;
+            support = true;
+            return result;
         }
     }
 
-    return false;
+    return result;
 }
 
 VkResult EnumerateDeviceExtensionProperties(
@@ -1101,18 +1113,30 @@
     swapchainCompFeats.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
     swapchainCompFeats.pNext = nullptr;
+    swapchainCompFeats.imageCompressionControlSwapchain = false;
     VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
     imageCompFeats.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
     imageCompFeats.pNext = &swapchainCompFeats;
+    imageCompFeats.imageCompressionControl = false;
 
     VkPhysicalDeviceFeatures2 feats2 = {};
     feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
     feats2.pNext = &imageCompFeats;
 
-    GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+    const auto& driver = GetData(physicalDevice).driver;
+    if (driver.GetPhysicalDeviceFeatures2 ||
+        driver.GetPhysicalDeviceFeatures2KHR) {
+        GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+    }
 
-    bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+    bool anb9 = false;
+    VkResult result =
+        GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
 
     if (anb9 && imageCompFeats.imageCompressionControl) {
         loader_extensions.push_back(
@@ -1142,7 +1166,7 @@
     }
 
     ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
-    VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+    result = data.driver.EnumerateDeviceExtensionProperties(
         physicalDevice, pLayerName, pPropertyCount, pProperties);
     ATRACE_END();
 
@@ -1532,6 +1556,11 @@
             } break;
 
             case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+                VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+                    compressionFeat = reinterpret_cast<
+                        VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+                        pFeats);
+                compressionFeat->imageCompressionControlSwapchain = false;
                 imageCompressionControlSwapchainInChain = true;
             } break;
 
@@ -1551,6 +1580,7 @@
         imageCompFeats.sType =
             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
         imageCompFeats.pNext = nullptr;
+        imageCompFeats.imageCompressionControl = false;
 
         VkPhysicalDeviceFeatures2 feats2 = {};
         feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 48a75c5..c46036a 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -508,6 +508,9 @@
         case VK_FORMAT_R8_UNORM:
             native_format = android::PIXEL_FORMAT_R_8;
             break;
+        case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+            native_format = android::PIXEL_FORMAT_RGBA_10101010;
+            break;
         default:
             ALOGV("unsupported swapchain format %d", format);
             break;
@@ -754,7 +757,6 @@
 
     const InstanceData& instance_data = GetData(pdev);
 
-    bool wide_color_support = false;
     uint64_t consumer_usage = 0;
     bool colorspace_ext =
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
@@ -765,27 +767,15 @@
         if (!surfaceless_enabled) {
             return VK_ERROR_SURFACE_LOST_KHR;
         }
-        // Support for VK_GOOGLE_surfaceless_query.  The EGL loader
-        // unconditionally supports wide color formats, even if they will cause
-        // a SurfaceFlinger fallback.  Based on that, wide_color_support will be
-        // set to true in this case.
-        wide_color_support = true;
+        // Support for VK_GOOGLE_surfaceless_query.
 
         // TODO(b/203826952): research proper value; temporarily use the
         // values seen on Pixel
         consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
     } else {
         Surface& surface = *SurfaceFromHandle(surface_handle);
-        int err = native_window_get_wide_color_support(surface.window.get(),
-                                                       &wide_color_support);
-        if (err) {
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-        ALOGV("wide_color_support is: %d", wide_color_support);
-
         consumer_usage = surface.consumer_usage;
     }
-    wide_color_support = wide_color_support && colorspace_ext;
 
     AHardwareBuffer_Desc desc = {};
     desc.width = 1;
@@ -807,9 +797,6 @@
             VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT});
         all_formats.emplace_back(VkSurfaceFormatKHR{
             VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
-    }
-
-    if (wide_color_support) {
         all_formats.emplace_back(VkSurfaceFormatKHR{
             VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
         all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -839,8 +826,6 @@
             all_formats.emplace_back(
                 VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
                                    VK_COLOR_SPACE_PASS_THROUGH_EXT});
-        }
-        if (wide_color_support) {
             all_formats.emplace_back(
                 VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
                                    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
@@ -859,8 +844,6 @@
             all_formats.emplace_back(
                 VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
                                    VK_COLOR_SPACE_PASS_THROUGH_EXT});
-        }
-        if (wide_color_support) {
             all_formats.emplace_back(
                 VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
                                    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -875,6 +858,36 @@
         }
     }
 
+    bool rgba10x6_formats_ext = false;
+    uint32_t exts_count;
+    const auto& driver = GetData(pdev).driver;
+    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
+                                              nullptr);
+    std::vector<VkExtensionProperties> props(exts_count);
+    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
+                                              props.data());
+    for (uint32_t i = 0; i < exts_count; i++) {
+        VkExtensionProperties prop = props[i];
+        if (strcmp(prop.extensionName,
+                   VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME) == 0) {
+            rgba10x6_formats_ext = true;
+        }
+    }
+    desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
+    if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
+        all_formats.emplace_back(
+            VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+                               VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+        if (colorspace_ext) {
+            all_formats.emplace_back(
+                VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+                                   VK_COLOR_SPACE_PASS_THROUGH_EXT});
+            all_formats.emplace_back(
+                VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+                                   VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+        }
+    }
+
     // NOTE: Any new formats that are added must be coordinated across different
     // Android users.  This includes the ANGLE team (a layered implementation of
     // OpenGL-ES).
@@ -950,27 +963,36 @@
         return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
                                                   pSurfaceInfo->surface,
                                                   pSurfaceFormatCount, nullptr);
-    } else {
-        // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
-        // after the call.
-        std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
-        VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
-            physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
-            surface_formats.data());
+    }
 
-        if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
-            const auto& driver = GetData(physicalDevice).driver;
+    // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+    // after the call.
+    std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+    VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+        physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+        surface_formats.data());
 
-            // marshal results individually due to stride difference.
-            uint32_t formats_to_marshal = *pSurfaceFormatCount;
-            for (uint32_t i = 0u; i < formats_to_marshal; i++) {
-                pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
 
-                // Query the compression properties for the surface format
-                if (pSurfaceFormats[i].pNext) {
+    const auto& driver = GetData(physicalDevice).driver;
+
+    // marshal results individually due to stride difference.
+    uint32_t formats_to_marshal = *pSurfaceFormatCount;
+    for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+        pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+        // Query the compression properties for the surface format
+        VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+        while (pSurfaceFormat->pNext) {
+            pSurfaceFormat =
+                reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+            switch (pSurfaceFormat->sType) {
+                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
                     VkImageCompressionPropertiesEXT* surfaceCompressionProps =
                         reinterpret_cast<VkImageCompressionPropertiesEXT*>(
-                            pSurfaceFormats[i].pNext);
+                            pSurfaceFormat);
 
                     if (surfaceCompressionProps &&
                         driver.GetPhysicalDeviceImageFormatProperties2KHR) {
@@ -1012,12 +1034,16 @@
                             return compressionRes;
                         }
                     }
-                }
+                } break;
+
+                default:
+                    // Ignore all other extension structs
+                    break;
             }
         }
-
-        return result;
     }
+
+    return result;
 }
 
 VKAPI_ATTR
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index b6d3a0b..b544245 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -25,10 +25,8 @@
         ".",
     ],
     shared_libs: [
-        "libvulkan",
-    ],
-    whole_static_libs: [
         "libjsoncpp",
+        "libvulkan",
     ],
     export_shared_lib_headers: [
         "libvulkan",
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index da6b00a..0284192 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -731,7 +731,7 @@
     visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) &&
     visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) &&
     visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) &&
-    visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer);
+    visitor->Visit("subgroupBroadcastDynamicId", &features->subgroupBroadcastDynamicId);
 }
 
 template <typename Visitor>