Merge "[sf] Trigger input updates in post composition" into udc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c71c4a0..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,6 +15,7 @@
                include/powermanager/
                libs/binder/fuzzer/
                libs/binder/
+               libs/binderdebug/
                libs/binderthreadstate/
                libs/graphicsenv/
                libs/gui/
@@ -40,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/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 95f5c03..e66cc41 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -501,6 +501,33 @@
     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
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 23cdd10..baf8e42 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -816,6 +816,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());
@@ -1254,8 +1256,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 &&
@@ -1272,6 +1276,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);
             }
         }
 
@@ -1902,6 +1909,9 @@
     }
     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);
@@ -2062,6 +2072,8 @@
                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) {
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 794750f..1693ed5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1942,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;
@@ -1964,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_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 bf2c0d1..7cabdb0 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -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/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 5c4e1a4..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());
 }
@@ -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(
@@ -962,6 +974,10 @@
 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,
@@ -976,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();
@@ -998,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);
@@ -1063,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);
@@ -1436,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;
@@ -1449,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/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4da0cd6..d4ce466 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -360,6 +360,10 @@
     }
 #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) {
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index a737bd3..754e7b2 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -77,6 +77,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.context_hub.prebuilt.xml",
+    src: "android.hardware.context_hub.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.ethernet.prebuilt.xml",
     src: "android.hardware.ethernet.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -107,30 +113,102 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.prebuilt.xml",
+    src: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.accelerometer_limited_axes.prebuilt.xml",
+    src: "android.hardware.sensor.accelerometer_limited_axes.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.accelerometer.prebuilt.xml",
+    src: "android.hardware.sensor.accelerometer.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.sensor.ambient_temperature.prebuilt.xml",
     src: "android.hardware.sensor.ambient_temperature.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.assist.prebuilt.xml",
+    src: "android.hardware.sensor.assist.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.sensor.barometer.prebuilt.xml",
     src: "android.hardware.sensor.barometer.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.compass.prebuilt.xml",
+    src: "android.hardware.sensor.compass.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.sensor.dynamic.head_tracker.prebuilt.xml",
     src: "android.hardware.sensor.dynamic.head_tracker.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.gyroscope_limited_axes_uncalibrated.prebuilt.xml",
+    src: "android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.gyroscope_limited_axes.prebuilt.xml",
+    src: "android.hardware.sensor.gyroscope_limited_axes.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.sensor.gyroscope.prebuilt.xml",
     src: "android.hardware.sensor.gyroscope.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.heading.prebuilt.xml",
+    src: "android.hardware.sensor.heading.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.heartrate.ecg.prebuilt.xml",
+    src: "android.hardware.sensor.heartrate.ecg.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.heartrate.fitness.prebuilt.xml",
+    src: "android.hardware.sensor.heartrate.fitness.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.heartrate.prebuilt.xml",
+    src: "android.hardware.sensor.heartrate.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.hifi_sensors.prebuilt.xml",
+    src: "android.hardware.sensor.hifi_sensors.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.sensor.hinge_angle.prebuilt.xml",
     src: "android.hardware.sensor.hinge_angle.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -155,6 +233,18 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.sensor.stepcounter.prebuilt.xml",
+    src: "android.hardware.sensor.stepcounter.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+    name: "android.hardware.sensor.stepdetector.prebuilt.xml",
+    src: "android.hardware.sensor.stepdetector.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.telephony.gsm.prebuilt.xml",
     src: "android.hardware.telephony.gsm.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
index 5966cba..d36c958 100644
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
+<!-- Feature for devices that support satellite communication via satellite vendor service APIs. -->
 <permissions>
     <feature name="android.hardware.telephony.satellite" />
 </permissions>
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index e4926a6..daeebec 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -555,6 +555,8 @@
  *                     range in use.
  *
  *                     Must be finite && >= 1.0f
+ *
+ * Available since API level 34.
  */
 void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transaction,
                                             ASurfaceControl* surface_control,
diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h
index bff7c1e..e45c1f5 100644
--- a/include/ftl/details/optional.h
+++ b/include/ftl/details/optional.h
@@ -54,5 +54,15 @@
 template <typename F, typename T>
 using and_then_result_t = typename and_then_result<F, T>::type;
 
+template <typename F, typename T>
+struct or_else_result {
+  using type = remove_cvref_t<std::invoke_result_t<F>>;
+  static_assert(std::is_same_v<type, std::optional<T>> || std::is_same_v<type, Optional<T>>,
+                "or_else function must return an optional T");
+};
+
+template <typename F, typename T>
+using or_else_result_t = typename or_else_result<F, T>::type;
+
 }  // namespace details
 }  // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index a818128..94d8e3d 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -96,13 +96,25 @@
     return R();
   }
 
+  // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F.
+  template <typename F>
+  constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> {
+    if (has_value()) return *this;
+    return std::forward<F>(f)();
+  }
+
+  template <typename F>
+  constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> {
+    if (has_value()) return std::move(*this);
+    return std::forward<F>(f)();
+  }
+
   // Delete new for this class. Its base doesn't have a virtual destructor, and
   // if it got deleted via base class pointer, it would cause undefined
   // behavior. There's not a good reason to allocate this object on the heap
   // anyway.
   static void* operator new(size_t) = delete;
   static void* operator new[](size_t) = delete;
-
 };
 
 template <typename T, typename U>
diff --git a/include/input/Input.h b/include/input/Input.h
index 608519b..e8af5f7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -855,6 +855,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/PropertyMap.h b/include/input/PropertyMap.h
index 18ce16d..2e44142 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -19,6 +19,7 @@
 #include <android-base/result.h>
 #include <utils/Tokenizer.h>
 
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -63,15 +64,15 @@
     /* Returns a set of all property keys starting with the given prefix. */
     std::unordered_set<std::string> getKeysWithPrefix(const std::string& prefix) const;
 
-    /* Gets the value of a property and parses it.
-     * Returns true and sets outValue if the key was found and its value was parsed successfully.
-     * Otherwise returns false and does not modify outValue.  (Also logs a warning.)
+    /* Gets the value of a property and parses it. Returns nullopt if the key wasn't found or
+     * couldn't be parsed as the requested type. (Warnings are also logged in the case of parsing
+     * failures.)
      */
-    bool tryGetProperty(const std::string& key, std::string& outValue) const;
-    bool tryGetProperty(const std::string& key, bool& outValue) const;
-    bool tryGetProperty(const std::string& key, int32_t& outValue) const;
-    bool tryGetProperty(const std::string& key, float& outValue) const;
-    bool tryGetProperty(const std::string& key, double& outValue) const;
+    std::optional<std::string> getString(const std::string& key) const;
+    std::optional<bool> getBool(const std::string& key) const;
+    std::optional<int32_t> getInt(const std::string& key) const;
+    std::optional<float> getFloat(const std::string& key) const;
+    std::optional<double> getDouble(const std::string& key) const;
 
     /* Adds all values from the specified property map. */
     void addAll(const PropertyMap* map);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6a354b4..49dd9c7 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -191,6 +191,7 @@
         "google-*",
         "misc-*",
         "performance*",
+        "-performance-move-const-arg", // b/273486801
         "portability*",
     ],
 }
@@ -354,6 +355,7 @@
 
 cc_library_static {
     name: "libbinder_rpc_no_kernel",
+    vendor_available: true,
     defaults: [
         "libbinder_common_defaults",
         "libbinder_android_defaults",
@@ -406,6 +408,7 @@
 cc_defaults {
     name: "libbinder_tls_defaults",
     defaults: ["libbinder_tls_shared_deps"],
+    vendor_available: true,
     host_supported: true,
 
     header_libs: [
@@ -432,7 +435,7 @@
     defaults: ["libbinder_tls_defaults"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libbinder_trusty",
     vendor: true,
     srcs: [
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 08169f5..ebdaa4c 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -131,6 +131,53 @@
         *outResult = reply.readInt32();
         return NO_ERROR;
     }
+
+    virtual status_t logFgsApiBegin(int32_t apiType, int32_t appUid, int32_t appPid) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeInt32(apiType);
+        data.writeInt32(appUid);
+        data.writeInt32(appPid);
+        status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply);
+        if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+            ALOGD("FGS Logger Transaction failed");
+            ALOGD("%d", err);
+            return err;
+        }
+        return NO_ERROR;
+    }
+
+    virtual status_t logFgsApiEnd(int32_t apiType, int32_t appUid, int32_t appPid) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeInt32(apiType);
+        data.writeInt32(appUid);
+        data.writeInt32(appPid);
+        status_t err = remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply);
+        if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+            ALOGD("FGS Logger Transaction failed");
+            ALOGD("%d", err);
+            return err;
+        }
+        return NO_ERROR;
+    }
+
+    virtual status_t logFgsApiStateChanged(int32_t apiType, int32_t state, int32_t appUid,
+                                           int32_t appPid) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeInt32(apiType);
+        data.writeInt32(state);
+        data.writeInt32(appUid);
+        data.writeInt32(appPid);
+        status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply);
+        if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+            ALOGD("FGS Logger Transaction failed");
+            ALOGD("%d", err);
+            return err;
+        }
+        return NO_ERROR;
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 51b97165..ef58ed3 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -127,8 +127,7 @@
     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: "
-                   << interfaceName;
+                      "utf-8.";
         return std::nullopt;
     }
 
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 38bd081..ed3ce24 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -262,8 +262,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 +294,7 @@
     auto temp = std::move(mNodeForAddress);
     mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit
 
-    _l.unlock();
+    nodeLock.unlock();
     temp.clear(); // explicit
 }
 
@@ -704,7 +706,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 +722,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 = {
@@ -1164,8 +1167,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 +1232,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 +1245,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..0e23ea7 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -168,6 +168,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 +269,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/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 8b3ddfb..d5a6da2 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -54,7 +54,7 @@
             }
 
             LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno));
-            return -savedErrno;
+            return adjustStatus(-savedErrno);
         }
 
         if (pfd.revents & POLLNVAL) {
@@ -87,8 +87,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(
@@ -121,13 +123,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;
@@ -167,7 +182,7 @@
                     continue;
                 } else {
                     LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno));
-                    return -savedErrno;
+                    return adjustStatus(-savedErrno);
                 }
             } else {
                 mReadBufferSize = static_cast<size_t>(processSize);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1488400..07b38d7 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -119,5 +119,10 @@
     {
       "name": "memunreachable_binder_test"
     }
+  ],
+  "imports": [
+    {
+      "path": "packages/modules/Virtualization"
+    }
   ]
 }
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 0750ccf..a323feb 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -51,6 +51,9 @@
  * 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:
diff --git a/libs/binder/include_activitymanager/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
index 4632b2e..20d12ae 100644
--- a/libs/binder/include_activitymanager/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -42,6 +42,10 @@
                                     const pid_t pid,
                                     const uid_t uid,
                                     int32_t* outResult) = 0;
+    virtual status_t logFgsApiBegin(int32_t apiType, int32_t appUid, int32_t appPid) = 0;
+    virtual status_t logFgsApiEnd(int32_t apiType, int32_t appUid, int32_t appPid) = 0;
+    virtual status_t logFgsApiStateChanged(int32_t apiType, int32_t state, int32_t appUid,
+                                           int32_t appPid) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -50,6 +54,9 @@
         IS_UID_ACTIVE_TRANSACTION,
         GET_UID_PROCESS_STATE_TRANSACTION,
         CHECK_PERMISSION_TRANSACTION,
+        LOG_FGS_API_BEGIN_TRANSACTION,
+        LOG_FGS_API_END_TRANSACTION,
+        LOG_FGS_API_STATE_CHANGED_TRANSACTION
     };
 };
 
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 43159d8..89fd7a3 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -56,12 +56,12 @@
  *
  * \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 flag an AServiceManager_AddServiceFlag enum to denote how the service should be added.
+ * \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_addServiceWithFlag(
-        AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flag)
+__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithFlags(
+        AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flags)
         __INTRODUCED_IN(34);
 
 /**
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index ffcad55..3fbe90d 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -32,7 +32,7 @@
  * 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
@@ -48,7 +48,7 @@
  * you should use this in a library to abort if the threadpool is not started.
  * Programs should configure binder threadpools once at the beginning.
  */
-bool ABinderProcess_isThreadPoolStarted();
+bool ABinderProcess_isThreadPoolStarted(void);
 /**
  * This adds the current thread to the threadpool. This may cause the threadpool to exceed the
  * maximum size.
@@ -56,7 +56,7 @@
  * 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 ABinderProcess_joinThreadPool(void);
 
 /**
  * This gives you an fd to wait on. Whenever data is available on the fd,
@@ -79,6 +79,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/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1078fb2..1c5f79f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -158,7 +158,7 @@
     AServiceManager_getUpdatableApexName; # systemapi
     AServiceManager_registerForServiceNotifications; # systemapi llndk
     AServiceManager_NotificationRegistration_delete; # systemapi llndk
-    AServiceManager_addServiceWithFlag; # systemapi llndk
+    AServiceManager_addServiceWithFlags; # systemapi llndk
 };
 
 LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index bc6610e..0fea57b 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -24,17 +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;
 }
-bool ABinderProcess_isThreadPoolStarted() {
+bool ABinderProcess_isThreadPoolStarted(void) {
     return ProcessState::self()->isThreadPoolStarted();
 }
-void ABinderProcess_joinThreadPool() {
+void ABinderProcess_joinThreadPool(void) {
     IPCThreadState::self()->joinThreadPool();
 }
 
@@ -42,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 84da459..2977786 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -42,15 +42,15 @@
     return PruneException(exception);
 }
 
-binder_exception_t AServiceManager_addServiceWithFlag(AIBinder* binder, const char* instance,
-                                                      const AServiceManager_AddServiceFlag flag) {
+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 = flag & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
+    bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
     status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated);
     return PruneException(exception);
 }
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 882f1d6..cefc42f 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -51,6 +51,7 @@
 constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
 constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
 constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
+constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
 
 constexpr unsigned int kShutdownWaitTime = 11;
 constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
@@ -158,6 +159,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 {
@@ -847,6 +866,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));
 }
@@ -901,6 +926,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/src/binder.rs b/libs/binder/rust/src/binder.rs
index 976f54d..d0e35de 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::*;
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 6f4c375..4b658fc 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -771,7 +771,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<'_>,
@@ -780,9 +786,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<'_>,
@@ -808,7 +814,13 @@
 #[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),* > {
             fn deserialize(
                 parcel: &$crate::binder_impl::BorrowedParcel<'_>,
             ) -> std::result::Result<Self, $crate::StatusCode> {
@@ -830,9 +842,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> {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 0f0d64a..61a047b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,6 +138,7 @@
 
 aidl_interface {
     name: "binderRpcTestIface",
+    vendor_available: true,
     host_supported: true,
     unstable: true,
     srcs: [
@@ -159,6 +160,7 @@
 cc_library_static {
     name: "libbinder_tls_test_utils",
     host_supported: true,
+    vendor_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -441,6 +443,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: {
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/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 52ba9b0..5939273 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,9 +120,9 @@
             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;
@@ -254,11 +256,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 5952c41..504b3ce 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -28,10 +28,10 @@
 #include <sys/prctl.h>
 #include <sys/socket.h>
 
-#ifdef __ANDROID_VENDOR__
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
 #include <binder/RpcTransportTipcAndroid.h>
 #include <trusty/tipc.h>
-#endif // __ANDROID_VENDOR__
+#endif // BINDER_RPC_TO_TRUSTY_TEST
 
 #include "binderRpcTestCommon.h"
 #include "binderRpcTestFixture.h"
@@ -50,7 +50,7 @@
 constexpr bool kEnableSharedLibs = true;
 #endif
 
-#ifdef __ANDROID_VENDOR__
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
 constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
 #endif
 
@@ -163,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);
@@ -179,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;
@@ -212,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)) {
@@ -230,22 +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";
-    } else {
-        ret += "_multi_threaded";
-    }
-    if (noKernel) {
-        ret += "_no_kernel";
-    } else {
-        ret += "_with_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
@@ -254,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());
@@ -315,13 +310,13 @@
     for (size_t i = 0; i < options.numSessions; i++) {
         std::unique_ptr<RpcTransportCtxFactory> factory;
         if (socketType == SocketType::TIPC) {
-#ifdef __ANDROID_VENDOR__
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
             factory = RpcTransportCtxFactoryTipcAndroid::make();
 #else
             LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
 #endif
         } else {
-            factory = newFactory(rpcSecurity, certVerifier);
+            factory = newTlsFactory(rpcSecurity, certVerifier);
         }
         sessions.emplace_back(RpcSession::make(std::move(factory)));
     }
@@ -351,9 +346,15 @@
 
     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->setMaxIncomingThreads(numIncoming);
         session->setMaxOutgoingConnections(options.numOutgoingConnections);
         session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
 
@@ -379,11 +380,18 @@
                 break;
             case SocketType::TIPC:
                 status = session->setupPreconnectedClient({}, [=]() {
-#ifdef __ANDROID_VENDOR__
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
                     auto port = trustyIpcPort(serverVersion);
-                    int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
-                    return tipcFd >= 0 ? android::base::unique_fd(tipcFd)
-                                       : android::base::unique_fd();
+                    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();
@@ -465,7 +473,9 @@
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
+
+    // b/272429574 - below 500ms, the test fails
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -478,7 +488,9 @@
     constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
     auto proc = createRpcTestSocketServerProcess(
             {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
+
+    // b/272429574 - below 500ms, the test fails
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadingStressTest) {
@@ -587,30 +599,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);
 }
@@ -659,6 +663,32 @@
     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());
+}
+
 TEST_P(BinderRpc, SingleDeathRecipient) {
     if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
@@ -676,7 +706,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));
@@ -689,6 +719,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;
 }
@@ -710,7 +744,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));
@@ -743,8 +777,7 @@
         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_EQ(INVALID_OPERATION, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -763,19 +796,13 @@
 
     // 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) {
@@ -1084,6 +1111,15 @@
     ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
 }
 
+#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
 static bool testSupportVsockLoopback() {
     // We don't need to enable TLS to know if vsock is supported.
     unsigned int vsockPort = allocateVsockPort();
@@ -1188,21 +1224,6 @@
     return ret;
 }
 
-static std::vector<SocketType> testTipcSocketTypes() {
-#ifdef __ANDROID_VENDOR__
-    auto port = trustyIpcPort(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-    int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
-    if (tipcFd >= 0) {
-        close(tipcFd);
-        return {SocketType::TIPC};
-    }
-#endif // __ANDROID_VENDOR__
-
-    // TIPC is not supported on this device, most likely
-    // because /dev/trusty-ipc-dev0 is missing
-    return {};
-}
-
 INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
                         ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
                                            ::testing::ValuesIn(RpcSecurityValues()),
@@ -1212,14 +1233,6 @@
                                            ::testing::Values(false, true)),
                         BinderRpc::PrintParamInfo);
 
-INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
-                        ::testing::Combine(::testing::ValuesIn(testTipcSocketTypes()),
-                                           ::testing::Values(RpcSecurity::RAW),
-                                           ::testing::ValuesIn(testVersions()),
-                                           ::testing::ValuesIn(testVersions()),
-                                           ::testing::Values(true), ::testing::Values(true)),
-                        BinderRpc::PrintParamInfo);
-
 class BinderRpcServerRootObject
       : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
 
@@ -1230,7 +1243,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);
@@ -1326,7 +1339,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));
     }
 };
@@ -1334,7 +1347,7 @@
 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())));
+    auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
     server->setProtocolVersion(std::get<1>(GetParam()));
     ASSERT_FALSE(server->hasServer());
     ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
@@ -1350,7 +1363,7 @@
     }
 
     auto addr = allocateSocketAddress();
-    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+    auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
     server->setProtocolVersion(std::get<1>(GetParam()));
     ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
     auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1399,7 +1412,7 @@
                 const Param& param,
                 std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
             auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
-            auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
+            auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
             rpcServer->setProtocolVersion(serverVersion);
             switch (socketType) {
                 case SocketType::PRECONNECTED: {
@@ -1478,7 +1491,7 @@
             }
             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();
@@ -1583,7 +1596,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();
         }
@@ -1655,7 +1668,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;
@@ -1988,6 +2001,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.h b/libs/binder/tests/binderRpcTestCommon.h
index a467ee3..c1364dd 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -126,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;
@@ -170,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) {
@@ -445,6 +449,12 @@
     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 c99d68a..6cde9f7 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -64,6 +64,21 @@
     // 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(BinderRpcTestProcessSession&&) = default;
     ~BinderRpcTestProcessSession() {
         if (!expectAlreadyShutdown) {
@@ -133,9 +148,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 a27bd2f..a9736d5 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -83,6 +83,18 @@
         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[]) {
@@ -104,7 +116,7 @@
     }
 
     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);
     server->setMaxThreads(serverConfig.numThreads);
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index 63b56a3..28be10d 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -39,31 +39,25 @@
     void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); }
 };
 
-std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
-    auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
-    auto ret = PrintToString(type) + "_clientV" + std::to_string(clientVersion) + "_serverV" +
-            std::to_string(serverVersion);
-    if (singleThreaded) {
-        ret += "_single_threaded";
-    } else {
-        ret += "_multi_threaded";
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+    switch (rpcSecurity) {
+        case RpcSecurity::RAW:
+            return RpcTransportCtxFactoryTipcTrusty::make();
+        default:
+            LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
     }
-    if (noKernel) {
-        ret += "_no_kernel";
-    } else {
-        ret += "_with_kernel";
-    }
-    return ret;
 }
 
 // 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(options.numIncomingConnections != 0,
-                        "Non-zero incoming connections %zu on Trusty",
-                        options.numIncomingConnections);
+    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());
 
@@ -71,8 +65,7 @@
 
     status_t status;
     for (size_t i = 0; i < options.numSessions; i++) {
-        auto factory = android::RpcTransportCtxFactoryTipcTrusty::make();
-        auto session = android::RpcSession::make(std::move(factory));
+        auto session = android::RpcSession::make(newFactory(rpcSecurity));
 
         EXPECT_TRUE(session->setProtocolVersion(clientVersion));
         session->setMaxOutgoingConnections(options.numOutgoingConnections);
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 11a22b0..1f46010 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -463,7 +463,7 @@
                 auto proc = createRpcTestSocketServerProcess(
                         {.numThreads = 1,
                          .numSessions = 1,
-                         .numIncomingConnections = numIncomingConnections});
+                         .numIncomingConnectionsBySession = {numIncomingConnections}});
                 auto cb = sp<MyBinderRpcCallback>::make();
 
                 if (callIsOneway) {
@@ -491,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/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 768fbe1..46d387c 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -367,13 +367,15 @@
         FUZZ_LOG() << "about to call readFromParcel() with status for 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";
         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 08eb27a..3a1471e 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -198,6 +198,8 @@
             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";
diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index f1079e9..dd08f72 100644
--- a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -16,10 +16,23 @@
 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/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
index 73790fa..e494366 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -26,7 +26,7 @@
     rewind(intermediateFile);
     int fileNumber = fileno(intermediateFile);
 
-    android::base::unique_fd fd(fileNumber);
+    android::base::unique_fd fd(dup(fileNumber));
 
     auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
 
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/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/ftl/Android.bp b/libs/ftl/Android.bp
index 8e57152..ea1b5e4 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: [
         "algorithm_test.cpp",
         "cast_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/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index 6b3b6c4..91bf7bc 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -164,6 +164,46 @@
                      }));
 }
 
+TEST(Optional, OrElse) {
+  // Non-empty.
+  {
+    const Optional opt = false;
+    EXPECT_EQ(false, opt.or_else([] { return Optional(true); }));
+    EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); }));
+  }
+
+  // Empty.
+  {
+    const Optional<int> opt;
+    EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; }));
+    EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); }));
+  }
+  {
+    bool empty = false;
+    EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> {
+      empty = true;
+      return std::nullopt;
+    }));
+    EXPECT_TRUE(empty);
+  }
+
+  // Chaining.
+  using StringVector = StaticVector<std::string, 3>;
+  EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s})
+                     .and_then([](StringVector&& v) -> Optional<StringVector> {
+                       if (v.push_back("0"s)) return v;
+                       return {};
+                     })
+                     .or_else([] {
+                       return Optional(StaticVector{"9"s, "9"s, "9"s});
+                     })
+                     .transform([](const StringVector& v) {
+                       return std::accumulate(v.begin(), v.end(), std::string());
+                     })
+                     .and_then(parse_int)
+                     .or_else([] { return Optional(-1); }));
+}
+
 // Comparison.
 namespace {
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index b391337..1969496 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -190,8 +190,8 @@
     }
     SAFE_PARCEL(output.writeParcelable, trustedPresentationThresholds);
     SAFE_PARCEL(output.writeParcelable, trustedPresentationListener);
-    SAFE_PARCEL(output.writeFloat, currentSdrHdrRatio);
-    SAFE_PARCEL(output.writeFloat, desiredSdrHdrRatio);
+    SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio);
+    SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio);
     SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint))
     return NO_ERROR;
 }
@@ -335,9 +335,9 @@
     SAFE_PARCEL(input.readParcelable, &trustedPresentationListener);
 
     SAFE_PARCEL(input.readFloat, &tmpFloat);
-    currentSdrHdrRatio = tmpFloat;
+    currentHdrSdrRatio = tmpFloat;
     SAFE_PARCEL(input.readFloat, &tmpFloat);
-    desiredSdrHdrRatio = tmpFloat;
+    desiredHdrSdrRatio = tmpFloat;
 
     int32_t tmpInt32;
     SAFE_PARCEL(input.readInt32, &tmpInt32);
@@ -592,8 +592,8 @@
     }
     if (other.what & eExtendedRangeBrightnessChanged) {
         what |= eExtendedRangeBrightnessChanged;
-        desiredSdrHdrRatio = other.desiredSdrHdrRatio;
-        currentSdrHdrRatio = other.currentSdrHdrRatio;
+        desiredHdrSdrRatio = other.desiredHdrSdrRatio;
+        currentHdrSdrRatio = other.currentHdrSdrRatio;
     }
     if (other.what & eCachingHintChanged) {
         what |= eCachingHintChanged;
@@ -747,8 +747,8 @@
     CHECK_DIFF(diff, eCropChanged, other, crop);
     if (other.what & eBufferChanged) diff |= eBufferChanged;
     CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
-    CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentSdrHdrRatio,
-                desiredSdrHdrRatio);
+    CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentHdrSdrRatio,
+                desiredHdrSdrRatio);
     CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint);
     CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
     if (other.what & eSurfaceDamageRegionChanged &&
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2f5830d..7700aa4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1723,8 +1723,8 @@
         return *this;
     }
     s->what |= layer_state_t::eExtendedRangeBrightnessChanged;
-    s->currentSdrHdrRatio = currentBufferRatio;
-    s->desiredSdrHdrRatio = desiredRatio;
+    s->currentHdrSdrRatio = currentBufferRatio;
+    s->desiredHdrSdrRatio = desiredRatio;
 
     registerSurfaceControlForCallback(sc);
     return *this;
diff --git a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
index fc809c4..e8c36ee 100644
--- a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
+++ b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
@@ -19,7 +19,9 @@
 /** @hide */
 oneway interface IHdrLayerInfoListener {
     // Callback with the total number of HDR layers, the dimensions of the largest layer,
-    // and a placeholder flags
+    // a placeholder flags, and the max desired HDR/SDR ratio. The max desired HDR/SDR
+    // ratio may be positive infinity to indicate an unbounded ratio.
     // TODO (b/182312559): Define the flags (likely need an indicator that a UDFPS layer is present)
-    void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags);
+    void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH,
+            int flags, float maxDesiredHdrSdrRatio);
 }
\ No newline at end of file
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6e3be5c..5c88a07 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -389,8 +389,8 @@
     gui::DropInputMode dropInputMode;
 
     bool dimmingEnabled;
-    float currentSdrHdrRatio = 1.f;
-    float desiredSdrHdrRatio = 1.f;
+    float currentHdrSdrRatio = 1.f;
+    float desiredHdrSdrRatio = 1.f;
 
     gui::CachingHint cachingHint = gui::CachingHint::Enabled;
 
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 183acc1..cd35d2f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -91,6 +91,9 @@
     compile_multilib: "both",
 
     header_libs: ["libsurfaceflinger_headers"],
+    data: [
+        ":libgui_test",
+    ],
 }
 
 cc_test {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 3344e0b..5f80c16 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -512,6 +512,22 @@
     bgSurface->expectTap(1, 1);
 }
 
+TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableRegionWithCrop) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    bgSurface->showAt(100, 100);
+
+    fgSurface->mInputInfo.surfaceInset = 5;
+    fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+    fgSurface->showAt(100, 100);
+
+    injectTap(106, 106);
+    fgSurface->expectTap(1, 1);
+
+    injectTap(101, 101);
+    bgSurface->expectTap(1, 1);
+}
+
 // Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
 TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 133b260..53b22cb 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -151,10 +151,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);
 }
 
 float transformAngle(const ui::Transform& transform, float angleRadians) {
@@ -606,12 +619,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) {
@@ -933,7 +946,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/Keyboard.cpp b/libs/input/Keyboard.cpp
index 3f8467d..0b5c7ff 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "Keyboard"
 
+#include <limits.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <limits.h>
+#include <optional>
 
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
@@ -49,23 +50,25 @@
         const PropertyMap* deviceConfiguration) {
     // Use the configured key layout if available.
     if (deviceConfiguration) {
-        std::string keyLayoutName;
-        if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) {
-            status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
+        std::optional<std::string> keyLayoutName =
+                deviceConfiguration->getString("keyboard.layout");
+        if (keyLayoutName.has_value()) {
+            status_t status = loadKeyLayout(deviceIdentifier, *keyLayoutName);
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
                       "it was not found.",
-                      deviceIdentifier.name.c_str(), keyLayoutName.c_str());
+                      deviceIdentifier.name.c_str(), keyLayoutName->c_str());
             }
         }
 
-        std::string keyCharacterMapName;
-        if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) {
-            status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
+        std::optional<std::string> keyCharacterMapName =
+                deviceConfiguration->getString("keyboard.characterMap");
+        if (keyCharacterMapName.has_value()) {
+            status_t status = loadKeyCharacterMap(deviceIdentifier, *keyCharacterMapName);
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard character "
                       "map '%s' but it was not found.",
-                      deviceIdentifier.name.c_str(), keyCharacterMapName.c_str());
+                      deviceIdentifier.name.c_str(), keyCharacterMapName->c_str());
             }
         }
 
@@ -162,9 +165,7 @@
     if (config == nullptr) {
         return false;
     }
-    bool isSpecialFunction = false;
-    config->tryGetProperty("keyboard.specialFunction", isSpecialFunction);
-    return isSpecialFunction;
+    return config->getBool("keyboard.specialFunction").value_or(false);
 }
 
 bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
@@ -177,8 +178,7 @@
     }
 
     if (deviceConfiguration) {
-        bool builtIn = false;
-        if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) {
+        if (deviceConfiguration->getBool("keyboard.builtIn").value_or(false)) {
             return true;
         }
     }
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 9a4f10b..548f894 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -60,75 +60,62 @@
     return mProperties.find(key) != mProperties.end();
 }
 
-bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const {
+std::optional<std::string> PropertyMap::getString(const std::string& key) const {
     auto it = mProperties.find(key);
-    if (it == mProperties.end()) {
-        return false;
-    }
-
-    outValue = it->second;
-    return true;
+    return it != mProperties.end() ? std::make_optional(it->second) : std::nullopt;
 }
 
-bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const {
-    int32_t intValue;
-    if (!tryGetProperty(key, intValue)) {
-        return false;
-    }
-
-    outValue = intValue;
-    return true;
+std::optional<bool> PropertyMap::getBool(const std::string& key) const {
+    std::optional<int32_t> intValue = getInt(key);
+    return intValue.has_value() ? std::make_optional(*intValue != 0) : std::nullopt;
 }
 
-bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const {
-    std::string stringValue;
-    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
-        return false;
+std::optional<int32_t> PropertyMap::getInt(const std::string& key) const {
+    std::optional<std::string> stringValue = getString(key);
+    if (!stringValue.has_value() || stringValue->length() == 0) {
+        return std::nullopt;
     }
 
     char* end;
-    int32_t value = static_cast<int32_t>(strtol(stringValue.c_str(), &end, 10));
+    int32_t value = static_cast<int32_t>(strtol(stringValue->c_str(), &end, 10));
     if (*end != '\0') {
         ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.c_str(),
-              stringValue.c_str());
-        return false;
+              stringValue->c_str());
+        return std::nullopt;
     }
-    outValue = value;
-    return true;
+    return value;
 }
 
-bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const {
-    std::string stringValue;
-    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
-        return false;
+std::optional<float> PropertyMap::getFloat(const std::string& key) const {
+    std::optional<std::string> stringValue = getString(key);
+    if (!stringValue.has_value() || stringValue->length() == 0) {
+        return std::nullopt;
     }
 
     char* end;
-    float value = strtof(stringValue.c_str(), &end);
+    float value = strtof(stringValue->c_str(), &end);
     if (*end != '\0') {
         ALOGW("Property key '%s' has invalid value '%s'.  Expected a float.", key.c_str(),
-              stringValue.c_str());
-        return false;
+              stringValue->c_str());
+        return std::nullopt;
     }
-    outValue = value;
-    return true;
+    return value;
 }
 
-bool PropertyMap::tryGetProperty(const std::string& key, double& outValue) const {
-    std::string stringValue;
-    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
-        return false;
+std::optional<double> PropertyMap::getDouble(const std::string& key) const {
+    std::optional<std::string> stringValue = getString(key);
+    if (!stringValue.has_value() || stringValue->length() == 0) {
+        return std::nullopt;
     }
 
     char* end;
-    double value = strtod(stringValue.c_str(), &end);
+    double value = strtod(stringValue->c_str(), &end);
     if (*end != '\0') {
         ALOGW("Property key '%s' has invalid value '%s'.  Expected a double.", key.c_str(),
-              stringValue.c_str());
-        return false;
+              stringValue->c_str());
+        return std::nullopt;
     }
-    outValue = value;
-    return true;
+    return value;
 }
 
 void PropertyMap::addAll(const PropertyMap* map) {
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
old mode 100755
new mode 100644
index d985dc1..6299ca8
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -29,8 +29,7 @@
                 },
                 [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
                     std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
-                    std::string out;
-                    propertyMap.tryGetProperty(key, out);
+                    propertyMap.getString(key);
                 },
                 [](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void {
                     TemporaryFile tf;
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 691e87c..3b061d1 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -61,8 +61,21 @@
 constexpr char OUTPUT_PHI[] = "phi";
 constexpr char OUTPUT_PRESSURE[] = "pressure";
 
+// Ideally, we would just use std::filesystem::exists here, but it requires libc++fs, which causes
+// build issues in other parts of the system.
+#if defined(__ANDROID__)
+bool fileExists(const char* filename) {
+    struct stat buffer;
+    return stat(filename, &buffer) == 0;
+}
+#endif
+
 std::string getModelPath() {
 #if defined(__ANDROID__)
+    static const char* oemModel = "/vendor/etc/motion_predictor_model.fb";
+    if (fileExists(oemModel)) {
+        return oemModel;
+    }
     return "/system/etc/motion_predictor_model.fb";
 #else
     return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
@@ -217,7 +230,7 @@
 
 std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() {
     const std::string modelPath = getModelPath();
-    const int fd = open(modelPath.c_str(), O_RDONLY);
+    android::base::unique_fd fd(open(modelPath.c_str(), O_RDONLY));
     if (fd == -1) {
         PLOG(FATAL) << "Could not read model from " << modelPath;
     }
@@ -232,9 +245,6 @@
     if (!modelBuffer) {
         PLOG(FATAL) << "Failed to mmap model";
     }
-    if (close(fd) == -1) {
-        PLOG(FATAL) << "Failed to close model fd";
-    }
 
     return std::unique_ptr<TfLiteMotionPredictorModel>(
             new TfLiteMotionPredictorModel(std::move(modelBuffer)));
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c9393f4..6d7d561 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -43,13 +43,13 @@
 void TouchVideoFrame::rotate(ui::Rotation orientation) {
     switch (orientation) {
         case ui::ROTATION_90:
-            rotateQuarterTurn(false /*clockwise*/);
+            rotateQuarterTurn(/*clockwise=*/false);
             break;
         case ui::ROTATION_180:
             rotate180();
             break;
         case ui::ROTATION_270:
-            rotateQuarterTurn(true /*clockwise*/);
+            rotateQuarterTurn(/*clockwise=*/true);
             break;
         case ui::ROTATION_0:
             // No need to rotate if there's no rotation.
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 3632914..8551e5f 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -157,10 +157,10 @@
 
     std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
     if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
-        createdStrategy = createStrategy(mOverrideStrategy, isDifferentialAxis /* deltaValues */);
+        createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis);
     } else {
         createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
-                                         isDifferentialAxis /* deltaValues */);
+                                         /*deltaValues=*/isDifferentialAxis);
     }
 
     LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
@@ -495,7 +495,7 @@
     }
 
     ALOGD_IF(DEBUG_STRATEGY, "  - a=%s",
-             matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+             matrixToString(&a[0][0], m, n, /*rowMajor=*/false).c_str());
 
     // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
     float q[n][m]; // orthonormal basis, column-major order
@@ -527,8 +527,8 @@
         }
     }
     if (DEBUG_STRATEGY) {
-        ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
-        ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+        ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, /*rowMajor=*/false).c_str());
+        ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, /*rowMajor=*/true).c_str());
 
         // calculate QR, if we factored A correctly then QR should equal A
         float qr[n][m];
@@ -540,7 +540,7 @@
                 }
             }
         }
-        ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
+        ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, /*rowMajor=*/false).c_str());
     }
 
     // Solve R B = Qt W Y to find B.  This is easy because R is upper triangular.
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 8a6e983..2132dc1 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,
@@ -235,102 +237,110 @@
     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[0].isResampled = true;
-    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[0].isResampled = true;
+    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[0].isResampled = true;
+    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[1].pointerCoords[1].isResampled = true;
+
+    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].clear();
-    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[0].isResampled = true;
-    pointerCoords[1].clear();
-    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);
-    pointerCoords[1].isResampled = true;
-    event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
-    pointerCoords[0].clear();
-    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].clear();
-    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) {
@@ -367,51 +377,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));
@@ -507,7 +531,7 @@
     initializeEventWithHistory(&event);
 
     MotionEvent copy;
-    copy.copyFrom(&event, true /*keepHistory*/);
+    copy.copyFrom(&event, /*keepHistory=*/true);
 
     ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
 }
@@ -517,7 +541,7 @@
     initializeEventWithHistory(&event);
 
     MotionEvent copy;
-    copy.copyFrom(&event, false /*keepHistory*/);
+    copy.copyFrom(&event, /*keepHistory=*/false);
 
     ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
     ASSERT_EQ(0U, copy.getHistorySize());
@@ -550,10 +574,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));
@@ -618,12 +642,12 @@
     }
     MotionEvent event;
     ui::Transform identityTransform;
-    event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
-                     INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
-                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                     MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
-                     0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
-                     identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+    event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+                     INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+                     MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
+                     /*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2,
+                     identityTransform, /*downTime=*/0, /*eventTime=*/0, pointerCount,
                      pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
@@ -791,18 +815,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) {
@@ -824,12 +848,12 @@
 
     ui::Transform identityTransform;
     for (MotionClassification classification : classifications) {
-        event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
+        event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN,
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
-                         0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
+                         /*eventTime=*/0, pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
 }
@@ -846,11 +870,11 @@
     }
 
     ui::Transform identityTransform;
-    event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
+    event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
                      AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
-                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+                     /*xCursorPosition=*/280, /*yCursorPosition=*/540, identityTransform,
+                     /*downTime=*/0, /*eventTime=*/0, pointerCount, pointerProperties,
                      pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
@@ -869,4 +893,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 70e4fda..5d8b970 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -25,6 +25,8 @@
 
 namespace android {
 
+constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
 class InputPublisherAndConsumerTest : public testing::Test {
 protected:
     std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
@@ -90,7 +92,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status)
             << "consumer consume should return OK";
 
@@ -199,7 +201,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status)
             << "consumer consume should return OK";
 
@@ -226,10 +228,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());
@@ -242,10 +244,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));
@@ -290,7 +294,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status) << "consumer consume should return OK";
 
     ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -331,7 +335,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status) << "consumer consume should return OK";
 
     ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -373,7 +377,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status) << "consumer consume should return OK";
 
     ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -415,7 +419,7 @@
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status) << "consumer consume should return OK";
 
     ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index d01258c..7cb9526 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -56,7 +56,7 @@
 
         mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel));
         mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel),
-                                                    true /* enableTouchResampling */);
+                                                    /*enableTouchResampling=*/true);
     }
 
     status_t publishSimpleMotionEventWithCoords(int32_t action, nsecs_t eventTime,
@@ -79,11 +79,11 @@
     if (action == AMOTION_EVENT_ACTION_DOWN && eventTime != 0) {
         ADD_FAILURE() << "Downtime should be equal to 0 (hardcoded for convenience)";
     }
-    return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), 1 /*deviceId*/,
-                                          AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, INVALID_HMAC,
-                                          action, 0 /*actionButton*/, 0 /*flags*/, 0 /*edgeFlags*/,
-                                          AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
-                                          identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/,
+    return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), /*deviceId=*/1,
+                                          AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, INVALID_HMAC,
+                                          action, /*actionButton=*/0, /*flags=*/0, /*edgeFlags=*/0,
+                                          AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+                                          identityTransform, /*xPrecision=*/0, /*yPrecision=*/0,
                                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
                                           downTime, eventTime, properties.size(), properties.data(),
@@ -161,7 +161,7 @@
     uint32_t consumeSeq;
     InputEvent* event;
 
-    status_t status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, frameTime.count(),
+    status_t status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, frameTime.count(),
                                          &consumeSeq, &event);
     ASSERT_EQ(OK, status);
     MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index c6ad3a2..0277579 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -159,13 +159,13 @@
 
         MotionEvent event;
         ui::Transform identityTransform;
-        event.initialize(InputEvent::nextId(), 5 /*deviceId*/, AINPUT_SOURCE_ROTARY_ENCODER,
+        event.initialize(InputEvent::nextId(), /*deviceId=*/5, AINPUT_SOURCE_ROTARY_ENCODER,
                          ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
-                         0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
-                         0 /*buttonState*/, MotionClassification::NONE, identityTransform,
-                         0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
-                         timeStamp.count(), 1 /*pointerCount*/, properties, coords);
+                         /*actionButton=*/0, /*flags=*/0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+                         /*buttonState=*/0, MotionClassification::NONE, identityTransform,
+                         /*xPrecision=*/0, /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
+                         timeStamp.count(), /*pointerCount=*/1, properties, coords);
 
         events.emplace_back(event);
     }
@@ -219,12 +219,12 @@
 
         MotionEvent event;
         ui::Transform identityTransform;
-        event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
-                         DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
-                         AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                         MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
-                         0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
+        event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN,
+                         DISPLAY_ID, INVALID_HMAC, action, /*actionButton=*/0, /*flags=*/0,
+                         AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+                         MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
+                         /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
         events.emplace_back(event);
@@ -341,23 +341,23 @@
 TEST_F(VelocityTrackerTest, TestComputedVelocity) {
     VelocityTracker::ComputedVelocity computedVelocity;
 
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/0, /*velocity=*/200);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/26U, /*velocity=*/400);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/27U, /*velocity=*/650);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, /*velocity=*/750);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/0, /*velocity=*/1000);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/26U, /*velocity=*/2000);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/27U, /*velocity=*/3000);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, /*velocity=*/4000);
 
     // Check the axes/indices with velocity.
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/0U)), 200);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/26U)), 400);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/27U)), 650);
     EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/0U)), 1000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/26U)), 2000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/27U)), 3000);
     EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000);
     for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
         // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id.
@@ -436,17 +436,17 @@
 
     float maxFloat = std::numeric_limits<float>::max();
     VelocityTracker::ComputedVelocity computedVelocity;
-    computedVelocity = vt.getComputedVelocity(1000 /* units */, maxFloat);
+    computedVelocity = vt.getComputedVelocity(/*units=*/1000, maxFloat);
     checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
                   764.345703);
 
     // Expect X velocity to be scaled with respective to provided units.
-    computedVelocity = vt.getComputedVelocity(1000000 /* units */, maxFloat);
+    computedVelocity = vt.getComputedVelocity(/*units=*/1000000, maxFloat);
     checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
                   764345.703);
 
     // Expect X velocity to be clamped by provided max velocity.
-    computedVelocity = vt.getComputedVelocity(1000000 /* units */, 1000);
+    computedVelocity = vt.getComputedVelocity(/*units=*/1000000, 1000);
     checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000);
 
     // All 0 data for Y; expect 0 velocity.
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index f2b59ea..277d74d 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -23,10 +23,10 @@
 
 static KeyEvent getKeyEventWithFlags(int32_t flags) {
     KeyEvent event;
-    event.initialize(InputEvent::nextId(), 2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD,
+    event.initialize(InputEvent::nextId(), /*deviceId=*/2, AINPUT_SOURCE_GAMEPAD,
                      ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags,
-                     AKEYCODE_BUTTON_X, 121 /*scanCode*/, AMETA_ALT_ON, 1 /*repeatCount*/,
-                     1000 /*downTime*/, 2000 /*eventTime*/);
+                     AKEYCODE_BUTTON_X, /*scanCode=*/121, AMETA_ALT_ON, /*repeatCount=*/1,
+                     /*downTime=*/1000, /*eventTime=*/2000);
     return event;
 }
 
@@ -44,12 +44,12 @@
     ui::Transform transform;
     transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
     ui::Transform identity;
-    event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
-                     INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
-                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
-                     MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
-                     200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+    event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
+                     INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, flags,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+                     MotionClassification::NONE, transform, /*xPrecision=*/0.1, /*yPrecision=*/0.2,
+                     /*xCursorPosition=*/280, /*yCursorPosition=*/540, identity, /*downTime=*/100,
+                     /*eventTime=*/200, pointerCount, pointerProperties, pointerCoords);
     return event;
 }
 
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 0c03ede..b470f35 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -29,6 +29,7 @@
     local_include_dirs: ["include"],
 
     srcs: [
+        "icc.cpp",
         "jpegr.cpp",
         "recoverymapmath.cpp",
         "jpegrutils.cpp",
@@ -43,8 +44,6 @@
         "liblog",
         "libutils",
     ],
-
-    static_libs: ["libskia"],
 }
 
 cc_library {
diff --git a/libs/jpegrecoverymap/icc.cpp b/libs/jpegrecoverymap/icc.cpp
new file mode 100644
index 0000000..5412cb1
--- /dev/null
+++ b/libs/jpegrecoverymap/icc.cpp
@@ -0,0 +1,584 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/icc.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+#include <vector>
+#include <utils/Log.h>
+
+#ifndef FLT_MAX
+#define FLT_MAX 0x1.fffffep127f
+#endif
+
+namespace android::jpegrecoverymap {
+static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
+    float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
+    float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
+    float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2];
+    x[0] = y0;
+    x[1] = y1;
+    x[2] = y2;
+}
+
+bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) {
+    double a00 = src->vals[0][0],
+           a01 = src->vals[1][0],
+           a02 = src->vals[2][0],
+           a10 = src->vals[0][1],
+           a11 = src->vals[1][1],
+           a12 = src->vals[2][1],
+           a20 = src->vals[0][2],
+           a21 = src->vals[1][2],
+           a22 = src->vals[2][2];
+
+    double b0 = a00*a11 - a01*a10,
+           b1 = a00*a12 - a02*a10,
+           b2 = a01*a12 - a02*a11,
+           b3 = a20,
+           b4 = a21,
+           b5 = a22;
+
+    double determinant = b0*b5
+                       - b1*b4
+                       + b2*b3;
+
+    if (determinant == 0) {
+        return false;
+    }
+
+    double invdet = 1.0 / determinant;
+    if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
+        return false;
+    }
+
+    b0 *= invdet;
+    b1 *= invdet;
+    b2 *= invdet;
+    b3 *= invdet;
+    b4 *= invdet;
+    b5 *= invdet;
+
+    dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
+    dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
+    dst->vals[2][0] = (float)(        +     b2 );
+    dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
+    dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
+    dst->vals[2][1] = (float)(        -     b1 );
+    dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
+    dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
+    dst->vals[2][2] = (float)(        +     b0 );
+
+    for (int r = 0; r < 3; ++r)
+    for (int c = 0; c < 3; ++c) {
+        if (!isfinitef_(dst->vals[r][c])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) {
+    Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
+    for (int r = 0; r < 3; r++)
+        for (int c = 0; c < 3; c++) {
+            m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
+                         + A->vals[r][1] * B->vals[1][c]
+                         + A->vals[r][2] * B->vals[2][c];
+        }
+    return m;
+}
+
+static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) {
+    float v[3] = {
+            xyz_float[0] / kD50_x,
+            xyz_float[1] / kD50_y,
+            xyz_float[2] / kD50_z,
+    };
+    for (size_t i = 0; i < 3; ++i) {
+        v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f);
+    }
+    const float L = v[1] * 116.0f - 16.0f;
+    const float a = (v[0] - v[1]) * 500.0f;
+    const float b = (v[1] - v[2]) * 200.0f;
+    const float Lab_unorm[3] = {
+            L * (1 / 100.f),
+            (a + 128.0f) * (1 / 255.0f),
+            (b + 128.0f) * (1 / 255.0f),
+    };
+    // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the
+    // table, but the spec appears to indicate that the value should be 0xFF00.
+    // https://crbug.com/skia/13807
+    for (size_t i = 0; i < 3; ++i) {
+        reinterpret_cast<uint16_t*>(grid16_lab)[i] =
+                Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i]));
+    }
+}
+
+std::string IccHelper::get_desc_string(const jpegr_transfer_function tf,
+                                       const jpegr_color_gamut gamut) {
+    std::string result;
+    switch (gamut) {
+        case JPEGR_COLORGAMUT_BT709:
+            result += "sRGB";
+            break;
+        case JPEGR_COLORGAMUT_P3:
+            result += "Display P3";
+            break;
+        case JPEGR_COLORGAMUT_BT2100:
+            result += "Rec2020";
+            break;
+        default:
+            result += "Unknown";
+            break;
+    }
+    result += " Gamut with ";
+    switch (tf) {
+        case JPEGR_TF_SRGB:
+            result += "sRGB";
+            break;
+        case JPEGR_TF_LINEAR:
+            result += "Linear";
+            break;
+        case JPEGR_TF_PQ:
+            result += "PQ";
+            break;
+        case JPEGR_TF_HLG:
+            result += "HLG";
+            break;
+        default:
+            result += "Unknown";
+            break;
+    }
+    result += " Transfer";
+    return result;
+}
+
+sp<DataStruct> IccHelper::write_text_tag(const char* text) {
+    uint32_t text_length = strlen(text);
+    uint32_t header[] = {
+            Endian_SwapBE32(kTAG_TextType),                         // Type signature
+            0,                                                      // Reserved
+            Endian_SwapBE32(1),                                     // Number of records
+            Endian_SwapBE32(12),                                    // Record size (must be 12)
+            Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')),    // English USA
+            Endian_SwapBE32(2 * text_length),                       // Length of string in bytes
+            Endian_SwapBE32(28),                                    // Offset of string
+    };
+
+    uint32_t total_length = text_length * 2 + sizeof(header);
+    total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+
+    if (!dataStruct->write(header, sizeof(header))) {
+        ALOGE("write_text_tag(): error in writing data");
+        return dataStruct;
+    }
+
+    for (size_t i = 0; i < text_length; i++) {
+        // Convert ASCII to big-endian UTF-16.
+        dataStruct->write8(0);
+        dataStruct->write8(text[i]);
+    }
+
+    return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) {
+    uint32_t data[] = {
+            Endian_SwapBE32(kXYZ_PCSSpace),
+            0,
+            static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))),
+            static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
+            static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
+    };
+    sp<DataStruct> dataStruct = new DataStruct(sizeof(data));
+    dataStruct->write(&data, sizeof(data));
+    return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* table_16) {
+    int total_length = 4 + 4 + 4 + table_entries * 2;
+    total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(kTAG_CurveType));     // Type
+    dataStruct->write32(0);                                     // Reserved
+    dataStruct->write32(Endian_SwapBE32(table_entries));  // Value count
+    for (size_t i = 0; i < table_entries; ++i) {
+        uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
+        dataStruct->write16(value);
+    }
+    return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
+    int total_length = 16;
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
+    dataStruct->write32(0);                                      // Reserved
+    dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0)));
+
+    return dataStruct;
+}
+
+float IccHelper::compute_tone_map_gain(const jpegr_transfer_function tf, float L) {
+    if (L <= 0.f) {
+        return 1.f;
+    }
+    if (tf == JPEGR_TF_PQ) {
+        // The PQ transfer function will map to the range [0, 1]. Linearly scale
+        // it up to the range [0, 10,000/203]. We will then tone map that back
+        // down to [0, 1].
+        constexpr float kInputMaxLuminance = 10000 / 203.f;
+        constexpr float kOutputMaxLuminance = 1.0;
+        L *= kInputMaxLuminance;
+
+        // Compute the tone map gain which will tone map from 10,000/203 to 1.0.
+        constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance);
+        constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
+        return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
+    }
+    if (tf == JPEGR_TF_HLG) {
+        // Let Lw be the brightness of the display in nits.
+        constexpr float Lw = 203.f;
+        const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
+        return std::pow(L, gamma - 1.f);
+    }
+    return 1.f;
+}
+
+sp<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
+                                         uint32_t transfer_characteristics) {
+    int total_length = 12;  // 4 + 4 + 1 + 1 + 1 + 1
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(kTAG_cicp));    // Type signature
+    dataStruct->write32(0);                             // Reserved
+    dataStruct->write8(color_primaries);                // Color primaries
+    dataStruct->write8(transfer_characteristics);       // Transfer characteristics
+    dataStruct->write8(0);                              // RGB matrix
+    dataStruct->write8(1);                              // Full range
+    return dataStruct;
+}
+
+void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) {
+    // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50.
+    Matrix3x3 src_to_rec2020;
+    const Matrix3x3 rec2020_to_XYZD50 = kRec2020;
+    {
+        Matrix3x3 XYZD50_to_rec2020;
+        Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020);
+        src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50);
+    }
+
+    // Convert the source signal to linear.
+    for (size_t i = 0; i < kNumChannels; ++i) {
+        rgb[i] = pqOetf(rgb[i]);
+    }
+
+    // Convert source gamut to Rec2020.
+    Matrix3x3_apply(&src_to_rec2020, rgb);
+
+    // Compute the luminance of the signal.
+    float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
+
+    // Compute the tone map gain based on the luminance.
+    float tone_map_gain = compute_tone_map_gain(JPEGR_TF_PQ, L);
+
+    // Apply the tone map gain.
+    for (size_t i = 0; i < kNumChannels; ++i) {
+        rgb[i] *= tone_map_gain;
+    }
+
+    // Convert from Rec2020-linear to XYZD50.
+    Matrix3x3_apply(&rec2020_to_XYZD50, rgb);
+}
+
+sp<DataStruct> IccHelper::write_clut(const uint8_t* grid_points, const uint8_t* grid_16) {
+    uint32_t value_count = kNumChannels;
+    for (uint32_t i = 0; i < kNumChannels; ++i) {
+        value_count *= grid_points[i];
+    }
+
+    int total_length = 20 + 2 * value_count;
+    total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+
+    for (size_t i = 0; i < 16; ++i) {
+        dataStruct->write8(i < kNumChannels ? grid_points[i] : 0);  // Grid size
+    }
+    dataStruct->write8(2);  // Grid byte width (always 16-bit)
+    dataStruct->write8(0);  // Reserved
+    dataStruct->write8(0);  // Reserved
+    dataStruct->write8(0);  // Reserved
+
+    for (uint32_t i = 0; i < value_count; ++i) {
+        uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i];
+        dataStruct->write16(value);
+    }
+
+    return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type,
+                                               bool has_a_curves,
+                                               const uint8_t* grid_points,
+                                               const uint8_t* grid_16) {
+    const size_t b_curves_offset = 32;
+    sp<DataStruct> b_curves_data[kNumChannels];
+    sp<DataStruct> a_curves_data[kNumChannels];
+    size_t clut_offset = 0;
+    sp<DataStruct> clut;
+    size_t a_curves_offset = 0;
+
+    // The "B" curve is required.
+    for (size_t i = 0; i < kNumChannels; ++i) {
+        b_curves_data[i] = write_trc_tag_for_linear();
+    }
+
+    // The "A" curve and CLUT are optional.
+    if (has_a_curves) {
+        clut_offset = b_curves_offset;
+        for (size_t i = 0; i < kNumChannels; ++i) {
+            clut_offset += b_curves_data[i]->getLength();
+        }
+        clut = write_clut(grid_points, grid_16);
+
+        a_curves_offset = clut_offset + clut->getLength();
+        for (size_t i = 0; i < kNumChannels; ++i) {
+            a_curves_data[i] = write_trc_tag_for_linear();
+        }
+    }
+
+    int total_length = b_curves_offset;
+    for (size_t i = 0; i < kNumChannels; ++i) {
+        total_length += b_curves_data[i]->getLength();
+    }
+    if (has_a_curves) {
+        total_length += clut->getLength();
+        for (size_t i = 0; i < kNumChannels; ++i) {
+            total_length += a_curves_data[i]->getLength();
+        }
+    }
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(type));             // Type signature
+    dataStruct->write32(0);                                 // Reserved
+    dataStruct->write8(kNumChannels);                       // Input channels
+    dataStruct->write8(kNumChannels);                       // Output channels
+    dataStruct->write16(0);                                 // Reserved
+    dataStruct->write32(Endian_SwapBE32(b_curves_offset));  // B curve offset
+    dataStruct->write32(Endian_SwapBE32(0));                // Matrix offset (ignored)
+    dataStruct->write32(Endian_SwapBE32(0));                // M curve offset (ignored)
+    dataStruct->write32(Endian_SwapBE32(clut_offset));      // CLUT offset
+    dataStruct->write32(Endian_SwapBE32(a_curves_offset));  // A curve offset
+    for (size_t i = 0; i < kNumChannels; ++i) {
+        if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) {
+            return dataStruct;
+        }
+    }
+    if (has_a_curves) {
+        dataStruct->write(clut->getData(), clut->getLength());
+        for (size_t i = 0; i < kNumChannels; ++i) {
+            dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength());
+        }
+    }
+    return dataStruct;
+}
+
+sp<DataStruct> IccHelper::writeIccProfile(jpegr_transfer_function tf, jpegr_color_gamut gamut) {
+    ICCHeader header;
+
+    std::vector<std::pair<uint32_t, sp<DataStruct>>> tags;
+
+    // Compute profile description tag
+    std::string desc = get_desc_string(tf, gamut);
+
+    tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));
+
+    Matrix3x3 toXYZD50;
+    switch (gamut) {
+        case JPEGR_COLORGAMUT_BT709:
+            toXYZD50 = kSRGB;
+            break;
+        case JPEGR_COLORGAMUT_P3:
+            toXYZD50 = kDisplayP3;
+            break;
+        case JPEGR_COLORGAMUT_BT2100:
+            toXYZD50 = kRec2020;
+            break;
+        default:
+            // Should not fall here.
+            return new DataStruct(0);
+    }
+
+    // Compute primaries.
+    {
+        tags.emplace_back(kTAG_rXYZ,
+                write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0]));
+        tags.emplace_back(kTAG_gXYZ,
+                write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1]));
+        tags.emplace_back(kTAG_bXYZ,
+                write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2]));
+    }
+
+    // Compute white point tag (must be D50)
+    tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
+
+    // Compute transfer curves.
+    if (tf != JPEGR_TF_PQ) {
+        if (tf == JPEGR_TF_HLG) {
+            std::vector<uint8_t> trc_table;
+            trc_table.resize(kTrcTableSize * 2);
+            for (uint32_t i = 0; i < kTrcTableSize; ++i) {
+                float x = i / (kTrcTableSize - 1.f);
+                float y = hlgOetf(x);
+                y *= compute_tone_map_gain(tf, y);
+                float_to_table16(y, &trc_table[2 * i]);
+            }
+
+            tags.emplace_back(kTAG_rTRC,
+                    write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+            tags.emplace_back(kTAG_gTRC,
+                    write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+            tags.emplace_back(kTAG_bTRC,
+                    write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+        } else {
+            tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear());
+            tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear());
+            tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear());
+        }
+    }
+
+    // Compute CICP.
+    if (tf == JPEGR_TF_HLG || tf == JPEGR_TF_PQ) {
+        // The CICP tag is present in ICC 4.4, so update the header's version.
+        header.version = Endian_SwapBE32(0x04400000);
+
+        uint32_t color_primaries = 0;
+        if (gamut == JPEGR_COLORGAMUT_BT709) {
+            color_primaries = kCICPPrimariesSRGB;
+        } else if (gamut == JPEGR_COLORGAMUT_P3) {
+            color_primaries = kCICPPrimariesP3;
+        }
+
+        uint32_t transfer_characteristics = 0;
+        if (tf == JPEGR_TF_SRGB) {
+            transfer_characteristics = kCICPTrfnSRGB;
+        } else if (tf == JPEGR_TF_LINEAR) {
+            transfer_characteristics = kCICPTrfnLinear;
+        } else if (tf == JPEGR_TF_PQ) {
+            transfer_characteristics = kCICPTrfnPQ;
+        } else if (tf == JPEGR_TF_HLG) {
+            transfer_characteristics = kCICPTrfnHLG;
+        }
+        tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
+    }
+
+    // Compute A2B0.
+    if (tf == JPEGR_TF_PQ) {
+        std::vector<uint8_t> a2b_grid;
+        a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
+        size_t a2b_grid_index = 0;
+        for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
+            for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
+                for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
+                    float rgb[3] = {
+                            r_index / (kGridSize - 1.f),
+                            g_index / (kGridSize - 1.f),
+                            b_index / (kGridSize - 1.f),
+                    };
+                    compute_lut_entry(toXYZD50, rgb);
+                    float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]);
+                    a2b_grid_index += 6;
+                }
+            }
+        }
+        const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data());
+
+        uint8_t grid_points[kNumChannels];
+        for (size_t i = 0; i < kNumChannels; ++i) {
+            grid_points[i] = kGridSize;
+        }
+
+        auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType,
+                                             /* has_a_curves */ true,
+                                             grid_points,
+                                             grid_16);
+        tags.emplace_back(kTAG_A2B0, std::move(a2b_data));
+    }
+
+    // Compute B2A0.
+    if (tf == JPEGR_TF_PQ) {
+        auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
+                                             /* has_a_curves */ false,
+                                             /* grid_points */ nullptr,
+                                             /* grid_16 */ nullptr);
+        tags.emplace_back(kTAG_B2A0, std::move(b2a_data));
+    }
+
+    // Compute copyright tag
+    tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022"));
+
+    // Compute the size of the profile.
+    size_t tag_data_size = 0;
+    for (const auto& tag : tags) {
+        tag_data_size += tag.second->getLength();
+    }
+    size_t tag_table_size = kICCTagTableEntrySize * tags.size();
+    size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
+
+    // Write the header.
+    header.data_color_space = Endian_SwapBE32(Signature_RGB);
+    header.pcs = Endian_SwapBE32(tf == JPEGR_TF_PQ ? Signature_Lab : Signature_XYZ);
+    header.size = Endian_SwapBE32(profile_size);
+    header.tag_count = Endian_SwapBE32(tags.size());
+
+    sp<DataStruct> dataStruct = new DataStruct(profile_size);
+    if (!dataStruct->write(&header, sizeof(header))) {
+        ALOGE("writeIccProfile(): error in header");
+        return dataStruct;
+    }
+
+    // Write the tag table. Track the offset and size of the previous tag to
+    // compute each tag's offset. An empty SkData indicates that the previous
+    // tag is to be reused.
+    uint32_t last_tag_offset = sizeof(header) + tag_table_size;
+    uint32_t last_tag_size = 0;
+    for (const auto& tag : tags) {
+        last_tag_offset = last_tag_offset + last_tag_size;
+        last_tag_size = tag.second->getLength();
+        uint32_t tag_table_entry[3] = {
+                Endian_SwapBE32(tag.first),
+                Endian_SwapBE32(last_tag_offset),
+                Endian_SwapBE32(last_tag_size),
+        };
+        if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) {
+            ALOGE("writeIccProfile(): error in writing tag table");
+            return dataStruct;
+        }
+    }
+
+    // Write the tags.
+    for (const auto& tag : tags) {
+        if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) {
+            ALOGE("writeIccProfile(): error in writing tags");
+            return dataStruct;
+        }
+    }
+
+    return dataStruct;
+}
+
+} // namespace android::jpegrecoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
new file mode 100644
index 0000000..a81aa62
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_ICC_H
+#define ANDROID_JPEGRECOVERYMAP_ICC_H
+
+#include <jpegrecoverymap/jpegr.h>
+#include <jpegrecoverymap/jpegrutils.h>
+#include <utils/RefBase.h>
+#include <cmath>
+#include <string>
+
+#ifdef USE_BIG_ENDIAN
+#undef USE_BIG_ENDIAN
+#define USE_BIG_ENDIAN true
+#endif
+
+namespace android::jpegrecoverymap {
+
+typedef int32_t              Fixed;
+#define Fixed1               (1 << 16)
+#define MaxS32FitsInFloat    2147483520
+#define MinS32FitsInFloat    (-MaxS32FitsInFloat)
+#define FixedToFloat(x)      ((x) * 1.52587890625e-5f)
+
+typedef struct Matrix3x3 {
+    float vals[3][3];
+} Matrix3x3;
+
+// The D50 illuminant.
+constexpr float kD50_x = 0.9642f;
+constexpr float kD50_y = 1.0000f;
+constexpr float kD50_z = 0.8249f;
+
+enum {
+    // data_color_space
+    Signature_CMYK = 0x434D594B,
+    Signature_Gray = 0x47524159,
+    Signature_RGB  = 0x52474220,
+
+    // pcs
+    Signature_Lab  = 0x4C616220,
+    Signature_XYZ  = 0x58595A20,
+};
+
+
+typedef uint32_t FourByteTag;
+static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
+    return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
+}
+
+// This is equal to the header size according to the ICC specification (128)
+// plus the size of the tag count (4).  We include the tag count since we
+// always require it to be present anyway.
+static constexpr size_t kICCHeaderSize = 132;
+
+// Contains a signature (4), offset (4), and size (4).
+static constexpr size_t kICCTagTableEntrySize = 12;
+
+static constexpr uint32_t kDisplay_Profile    = SetFourByteTag('m', 'n', 't', 'r');
+static constexpr uint32_t kRGB_ColorSpace     = SetFourByteTag('R', 'G', 'B', ' ');
+static constexpr uint32_t kXYZ_PCSSpace       = SetFourByteTag('X', 'Y', 'Z', ' ');
+static constexpr uint32_t kACSP_Signature     = SetFourByteTag('a', 'c', 's', 'p');
+
+static constexpr uint32_t kTAG_desc           = SetFourByteTag('d', 'e', 's', 'c');
+static constexpr uint32_t kTAG_TextType       = SetFourByteTag('m', 'l', 'u', 'c');
+static constexpr uint32_t kTAG_rXYZ           = SetFourByteTag('r', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_gXYZ           = SetFourByteTag('g', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_bXYZ           = SetFourByteTag('b', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_wtpt           = SetFourByteTag('w', 't', 'p', 't');
+static constexpr uint32_t kTAG_rTRC           = SetFourByteTag('r', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_gTRC           = SetFourByteTag('g', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_bTRC           = SetFourByteTag('b', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_cicp           = SetFourByteTag('c', 'i', 'c', 'p');
+static constexpr uint32_t kTAG_cprt           = SetFourByteTag('c', 'p', 'r', 't');
+static constexpr uint32_t kTAG_A2B0           = SetFourByteTag('A', '2', 'B', '0');
+static constexpr uint32_t kTAG_B2A0           = SetFourByteTag('B', '2', 'A', '0');
+
+static constexpr uint32_t kTAG_CurveType      = SetFourByteTag('c', 'u', 'r', 'v');
+static constexpr uint32_t kTAG_mABType        = SetFourByteTag('m', 'A', 'B', ' ');
+static constexpr uint32_t kTAG_mBAType        = SetFourByteTag('m', 'B', 'A', ' ');
+static constexpr uint32_t kTAG_ParaCurveType  = SetFourByteTag('p', 'a', 'r', 'a');
+
+
+static constexpr Matrix3x3 kSRGB = {{
+    // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
+    // 0.436065674f, 0.385147095f, 0.143066406f,
+    // 0.222488403f, 0.716873169f, 0.060607910f,
+    // 0.013916016f, 0.097076416f, 0.714096069f,
+    { FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0) },
+    { FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84) },
+    { FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF) },
+}};
+
+static constexpr Matrix3x3 kDisplayP3 = {{
+    {  0.515102f,   0.291965f,  0.157153f  },
+    {  0.241182f,   0.692236f,  0.0665819f },
+    { -0.00104941f, 0.0418818f, 0.784378f  },
+}};
+
+static constexpr Matrix3x3 kRec2020 = {{
+    {  0.673459f,   0.165661f,  0.125100f  },
+    {  0.279033f,   0.675338f,  0.0456288f },
+    { -0.00193139f, 0.0299794f, 0.797162f  },
+}};
+
+static constexpr uint32_t kCICPPrimariesSRGB = 1;
+static constexpr uint32_t kCICPPrimariesP3 = 12;
+static constexpr uint32_t kCICPPrimariesRec2020 = 9;
+
+static constexpr uint32_t kCICPTrfnSRGB = 1;
+static constexpr uint32_t kCICPTrfnLinear = 8;
+static constexpr uint32_t kCICPTrfnPQ = 16;
+static constexpr uint32_t kCICPTrfnHLG = 18;
+
+enum ParaCurveType {
+    kExponential_ParaCurveType = 0,
+    kGAB_ParaCurveType         = 1,
+    kGABC_ParaCurveType        = 2,
+    kGABDE_ParaCurveType       = 3,
+    kGABCDEF_ParaCurveType     = 4,
+};
+
+/**
+ *  Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
+ */
+static inline int float_saturate2int(float x) {
+    x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
+    x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
+    return (int)x;
+}
+
+static Fixed float_round_to_fixed(float x) {
+    return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
+}
+
+static uint16_t float_round_to_unorm16(float x) {
+    x = x * 65535.f + 0.5;
+    if (x > 65535) return 65535;
+    if (x < 0) return 0;
+    return static_cast<uint16_t>(x);
+}
+
+static void float_to_table16(const float f, uint8_t* table_16) {
+    *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
+}
+
+static bool isfinitef_(float x) { return 0 == x*0; }
+
+struct ICCHeader {
+    // Size of the profile (computed)
+    uint32_t size;
+    // Preferred CMM type (ignored)
+    uint32_t cmm_type = 0;
+    // Version 4.3 or 4.4 if CICP is included.
+    uint32_t version = Endian_SwapBE32(0x04300000);
+    // Display device profile
+    uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
+    // RGB input color space;
+    uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
+    // Profile connection space.
+    uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
+    // Date and time (ignored)
+    uint8_t creation_date_time[12] = {0};
+    // Profile signature
+    uint32_t signature = Endian_SwapBE32(kACSP_Signature);
+    // Platform target (ignored)
+    uint32_t platform = 0;
+    // Flags: not embedded, can be used independently
+    uint32_t flags = 0x00000000;
+    // Device manufacturer (ignored)
+    uint32_t device_manufacturer = 0;
+    // Device model (ignored)
+    uint32_t device_model = 0;
+    // Device attributes (ignored)
+    uint8_t device_attributes[8] = {0};
+    // Relative colorimetric rendering intent
+    uint32_t rendering_intent = Endian_SwapBE32(1);
+    // D50 standard illuminant (X, Y, Z)
+    uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
+    uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
+    uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
+    // Profile creator (ignored)
+    uint32_t creator = 0;
+    // Profile id checksum (ignored)
+    uint8_t profile_id[16] = {0};
+    // Reserved (ignored)
+    uint8_t reserved[28] = {0};
+    // Technically not part of header, but required
+    uint32_t tag_count = 0;
+};
+
+class IccHelper {
+private:
+    static constexpr uint32_t kTrcTableSize = 65;
+    static constexpr uint32_t kGridSize = 17;
+    static constexpr size_t kNumChannels = 3;
+
+    static sp<DataStruct> write_text_tag(const char* text);
+    static std::string get_desc_string(const jpegr_transfer_function tf,
+                                       const jpegr_color_gamut gamut);
+    static sp<DataStruct> write_xyz_tag(float x, float y, float z);
+    static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
+    static sp<DataStruct> write_trc_tag_for_linear();
+    static float compute_tone_map_gain(const jpegr_transfer_function tf, float L);
+    static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
+                                         uint32_t transfer_characteristics);
+    static sp<DataStruct> write_mAB_or_mBA_tag(uint32_t type,
+                                               bool has_a_curves,
+                                               const uint8_t* grid_points,
+                                               const uint8_t* grid_16);
+    static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
+    static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
+
+public:
+    static sp<DataStruct> writeIccProfile(const jpegr_transfer_function tf,
+                                          const jpegr_color_gamut gamut);
+};
+}  // namespace android::jpegrecoverymap
+
+#endif //ANDROID_JPEGRECOVERYMAP_ICC_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index a433e8a..1ab1dd7 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -91,7 +91,10 @@
     int length;
 };
 
-struct jpegr_metadata {
+/*
+ * Holds information for recovery map related metadata.
+ */
+struct jpegr_metadata_struct {
   // JPEG/R version
   uint32_t version;
   // Max Content Boost for the map
@@ -103,12 +106,14 @@
 typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
 typedef struct jpegr_compressed_struct* jr_compressed_ptr;
 typedef struct jpegr_exif_struct* jr_exif_ptr;
-typedef struct jpegr_metadata* jr_metadata_ptr;
+typedef struct jpegr_metadata_struct* jr_metadata_ptr;
 typedef struct jpegr_info_struct* jr_info_ptr;
 
 class JpegR {
 public:
     /*
+     * Experimental only
+     *
      * Encode API-0
      * Compress JPEGR image from 10-bit HDR YUV.
      *
@@ -199,19 +204,42 @@
      * Decode API
      * Decompress JPEGR image.
      *
-     * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
-     * @param compressed_jpegr_image compressed JPEGR image
-     * @param dest destination of the uncompressed JPEGR image
-     * @param exif destination of the decoded EXIF metadata.
-     * @param output_format flag for setting output color format. if set to
-     *                      {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
-     *                      which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
+     * @param compressed_jpegr_image compressed JPEGR image.
+     * @param dest destination of the uncompressed JPEGR image.
+     * @param max_display_boost (optional) the maximum available boost supported by a display
+     * @param exif destination of the decoded EXIF metadata. The default value is NULL where the
+                   decoder will do nothing about it. If configured not NULL the decoder will write
+                   EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
+     * @param output_format flag for setting output color format. Its value configures the output
+                            color format. The default value is {@code JPEGR_OUTPUT_HDR_LINEAR}.
+                            ----------------------------------------------------------------------
+                            |      output_format       |    decoded color format to be written   |
+                            ----------------------------------------------------------------------
+                            |     JPEGR_OUTPUT_SDR     |                RGBA_8888                |
+                            ----------------------------------------------------------------------
+                            | JPEGR_OUTPUT_HDR_LINEAR  |        (default)RGBA_F16 linear         |
+                            ----------------------------------------------------------------------
+                            |   JPEGR_OUTPUT_HDR_PQ    |             RGBA_1010102 PQ             |
+                            ----------------------------------------------------------------------
+                            |   JPEGR_OUTPUT_HDR_HLG   |            RGBA_1010102 HLG             |
+                            ----------------------------------------------------------------------
+     * @param recovery_map destination of the decoded recovery map. The default value is NULL where
+                           the decoder will do nothing about it. If configured not NULL the decoder
+                           will write the decoded recovery_map data into this structure. The format
+                           is defined in {@code jpegr_uncompressed_struct}.
+     * @param metadata destination of the decoded metadata. The default value is NULL where the
+                       decoder will do nothing about it. If configured not NULL the decoder will
+                       write metadata into this structure. the format of metadata is defined in
+                       {@code jpegr_metadata}.
      * @return NO_ERROR if decoding succeeds, error code if error occurs.
      */
     status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                          jr_uncompressed_ptr dest,
+                         float max_display_boost = -1.0f,
                          jr_exif_ptr exif = nullptr,
-                         jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR);
+                         jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR,
+                         jr_uncompressed_ptr recovery_map = nullptr,
+                         jr_metadata_ptr metadata = nullptr);
 
     /*
     * Gets Info from JPEGR file without decoding it.
@@ -255,6 +283,7 @@
      * @param output_format flag for setting output color format. if set to
      *                      {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
      *                      which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
+     * @param max_display_boost the maximum available boost supported by a display
      * @param dest reconstructed HDR image
      * @return NO_ERROR if calculation succeeds, error code if error occurs.
      */
@@ -262,6 +291,7 @@
                               jr_uncompressed_ptr uncompressed_recovery_map,
                               jr_metadata_ptr metadata,
                               jpegr_output_format output_format,
+                              float max_display_boost,
                               jr_uncompressed_ptr dest);
 
 private:
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
index 4145853..dd06fa2 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
@@ -27,7 +27,25 @@
 
 namespace android::jpegrecoverymap {
 
-struct jpegr_metadata;
+static constexpr uint32_t EndianSwap32(uint32_t value) {
+    return ((value & 0xFF) << 24) |
+           ((value & 0xFF00) << 8) |
+           ((value & 0xFF0000) >> 8) |
+           (value >> 24);
+}
+static inline uint16_t EndianSwap16(uint16_t value) {
+    return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
+}
+
+#if USE_BIG_ENDIAN
+    #define Endian_SwapBE32(n) EndianSwap32(n)
+    #define Endian_SwapBE16(n) EndianSwap16(n)
+#else
+    #define Endian_SwapBE32(n) (n)
+    #define Endian_SwapBE16(n) (n)
+#endif
+
+struct jpegr_metadata_struct;
 /*
  * Mutable data structure. Holds information for metadata.
  */
@@ -69,7 +87,7 @@
  * @param metadata place to store HDR metadata values
  * @return true if metadata is successfully retrieved, false otherwise
 */
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata);
 
 /*
  * This method generates XMP metadata for the primary image.
@@ -138,7 +156,7 @@
  * @param metadata JPEG/R metadata to encode as XMP
  * @return XMP metadata in type of string
  */
- std::string generateXmpForSecondaryImage(jpegr_metadata& metadata);
+ std::string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata);
 }  // namespace android::jpegrecoverymap
 
 #endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
index 7dca916..cf3387d 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
@@ -19,25 +19,13 @@
 
 #include <jpegrecoverymap/jpegrutils.h>
 
-namespace android::jpegrecoverymap {
-static constexpr uint32_t EndianSwap32(uint32_t value) {
-    return ((value & 0xFF) << 24) |
-           ((value & 0xFF00) << 8) |
-           ((value & 0xFF0000) >> 8) |
-           (value >> 24);
-}
-static inline uint16_t EndianSwap16(uint16_t value) {
-    return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
-}
+#ifdef USE_BIG_ENDIAN
+#undef USE_BIG_ENDIAN
 #define USE_BIG_ENDIAN true
-#if USE_BIG_ENDIAN
-    #define Endian_SwapBE32(n) EndianSwap32(n)
-    #define Endian_SwapBE16(n) EndianSwap16(n)
-#else
-    #define Endian_SwapBE32(n) (n)
-    #define Endian_SwapBE16(n) (n)
 #endif
 
+namespace android::jpegrecoverymap {
+
 constexpr size_t kNumPictures = 2;
 constexpr size_t kMpEndianSize = 4;
 constexpr uint16_t kTagSerializedCount = 3;
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index 8b5318f..67d2a6a 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -135,6 +135,16 @@
     }
   }
 
+  RecoveryLUT(jr_metadata_ptr metadata, float displayBoost) {
+    float boostFactor = displayBoost > 0 ? displayBoost / metadata->maxContentBoost : 1.0f;
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
+      float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
+                     + log2(metadata->maxContentBoost) * value;
+      mRecoveryTable[idx] = exp2(logBoost * boostFactor);
+    }
+  }
+
   ~RecoveryLUT() {
   }
 
@@ -357,6 +367,7 @@
  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
  */
 Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata);
+Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata, float displayBoost);
 Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);
 
 /*
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index 79b1ae3..e395d51 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -20,6 +20,7 @@
 #include <jpegrecoverymap/recoverymapmath.h>
 #include <jpegrecoverymap/jpegrutils.h>
 #include <jpegrecoverymap/multipictureformat.h>
+#include <jpegrecoverymap/icc.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/jpeg/jpeg_info.h>
@@ -27,10 +28,6 @@
 #include <image_io/jpeg/jpeg_info_builder.h>
 #include <image_io/base/data_segment_data_source.h>
 #include <utils/Log.h>
-#include "SkColorSpace.h"
-#include "SkData.h"
-#include "SkICC.h"
-#include "SkRefCnt.h"
 
 #include <map>
 #include <memory>
@@ -89,21 +86,6 @@
   return cpuCoreCount;
 }
 
-static const map<jpegrecoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut {
-    {JPEGR_COLORGAMUT_BT709,     SkNamedGamut::kSRGB},
-    {JPEGR_COLORGAMUT_P3,        SkNamedGamut::kDisplayP3},
-    {JPEGR_COLORGAMUT_BT2100,    SkNamedGamut::kRec2020},
-};
-
-static const map<
-        jpegrecoverymap::jpegr_transfer_function,
-        skcms_TransferFunction> jrTransFunc_to_skTransFunc {
-    {JPEGR_TF_SRGB,        SkNamedTransferFn::kSRGB},
-    {JPEGR_TF_LINEAR,      SkNamedTransferFn::kLinear},
-    {JPEGR_TF_HLG,         SkNamedTransferFn::kHLG},
-    {JPEGR_TF_PQ,          SkNamedTransferFn::kPQ},
-};
-
 /* Encode API-0 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                             jpegr_transfer_function hdr_tf,
@@ -125,7 +107,7 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct uncompressed_yuv_420_image;
@@ -146,15 +128,14 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
-  sk_sp<SkData> icc = SkWriteICCProfile(
-          jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
-          jrGamut_to_skGamut.at(uncompressed_yuv_420_image.colorGamut));
+  sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+                                                  uncompressed_yuv_420_image.colorGamut);
 
   JpegEncoderHelper jpeg_encoder;
   if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
                                   uncompressed_yuv_420_image.width,
                                   uncompressed_yuv_420_image.height, quality,
-                                  icc.get()->data(), icc.get()->size())) {
+                                  icc->getData(), icc->getLength())) {
     return ERROR_JPEGR_ENCODE_ERROR;
   }
   jpegr_compressed_struct jpeg;
@@ -195,7 +176,7 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -210,15 +191,14 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
-  sk_sp<SkData> icc = SkWriteICCProfile(
-          jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
-          jrGamut_to_skGamut.at(uncompressed_yuv_420_image->colorGamut));
+  sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+                                                  uncompressed_yuv_420_image->colorGamut);
 
   JpegEncoderHelper jpeg_encoder;
   if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
                                   uncompressed_yuv_420_image->width,
                                   uncompressed_yuv_420_image->height, quality,
-                                  icc.get()->data(), icc.get()->size())) {
+                                  icc->getData(), icc->getLength())) {
     return ERROR_JPEGR_ENCODE_ERROR;
   }
   jpegr_compressed_struct jpeg;
@@ -255,7 +235,7 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -308,7 +288,7 @@
     return ERROR_JPEGR_RESOLUTION_MISMATCH;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -350,13 +330,14 @@
 /* Decode API */
 status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                             jr_uncompressed_ptr dest,
+                            float max_display_boost,
                             jr_exif_ptr exif,
-                            jpegr_output_format output_format) {
+                            jpegr_output_format output_format,
+                            jr_uncompressed_ptr recovery_map,
+                            jr_metadata_ptr metadata) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
-  // TODO: fill EXIF data
-  (void) exif;
 
   if (output_format == JPEGR_OUTPUT_SDR) {
     JpegDecoderHelper jpeg_decoder;
@@ -372,21 +353,72 @@
            uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
     dest->width = uncompressed_rgba_image.width;
     dest->height = uncompressed_rgba_image.height;
-    return NO_ERROR;
+
+    if (recovery_map == nullptr && exif == nullptr) {
+      return NO_ERROR;
+    }
+
+    if (exif != nullptr) {
+      if (exif->data == nullptr) {
+        return ERROR_JPEGR_INVALID_NULL_PTR;
+      }
+      if (exif->length < jpeg_decoder.getEXIFSize()) {
+        return ERROR_JPEGR_BUFFER_TOO_SMALL;
+      }
+      memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
+      exif->length = jpeg_decoder.getEXIFSize();
+    }
+    if (recovery_map == nullptr) {
+      return NO_ERROR;
+    }
   }
 
   jpegr_compressed_struct compressed_map;
-  jpegr_metadata metadata;
   JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
 
+  JpegDecoderHelper recovery_map_decoder;
+  if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  if (recovery_map != nullptr) {
+    recovery_map->width = recovery_map_decoder.getDecompressedImageWidth();
+    recovery_map->height = recovery_map_decoder.getDecompressedImageHeight();
+    int size = recovery_map->width * recovery_map->height;
+    recovery_map->data = malloc(size);
+    memcpy(recovery_map->data, recovery_map_decoder.getDecompressedImagePtr(), size);
+  }
+
+  jpegr_metadata_struct jr_metadata;
+  if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()),
+                          recovery_map_decoder.getXMPSize(), &jr_metadata)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  if (metadata != nullptr) {
+      metadata->version = jr_metadata.version;
+      metadata->minContentBoost = jr_metadata.minContentBoost;
+      metadata->maxContentBoost = jr_metadata.maxContentBoost;
+  }
+
+  if (output_format == JPEGR_OUTPUT_SDR) {
+    return NO_ERROR;
+  }
+
   JpegDecoderHelper jpeg_decoder;
   if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
-  JpegDecoderHelper recovery_map_decoder;
-  if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
-    return ERROR_JPEGR_DECODE_ERROR;
+  if (exif != nullptr) {
+    if (exif->data == nullptr) {
+      return ERROR_JPEGR_INVALID_NULL_PTR;
+    }
+    if (exif->length < jpeg_decoder.getEXIFSize()) {
+      return ERROR_JPEGR_BUFFER_TOO_SMALL;
+    }
+    memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
+    exif->length = jpeg_decoder.getEXIFSize();
   }
 
   jpegr_uncompressed_struct map;
@@ -399,12 +431,8 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()),
-                          recovery_map_decoder.getXMPSize(), &metadata)) {
-    return ERROR_JPEGR_DECODE_ERROR;
-  }
-
-  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, output_format, dest));
+  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &jr_metadata, output_format,
+                               max_display_boost, dest));
   return NO_ERROR;
 }
 
@@ -640,6 +668,7 @@
                                  jr_uncompressed_ptr uncompressed_recovery_map,
                                  jr_metadata_ptr metadata,
                                  jpegr_output_format output_format,
+                                 float max_display_boost,
                                  jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_recovery_map == nullptr
@@ -651,13 +680,15 @@
   dest->width = uncompressed_yuv_420_image->width;
   dest->height = uncompressed_yuv_420_image->height;
   ShepardsIDW idwTable(kMapDimensionScaleFactor);
-  RecoveryLUT recoveryLUT(metadata);
+  float display_boost = max_display_boost > 0 ?
+          std::min(max_display_boost, metadata->maxContentBoost)
+          : metadata->maxContentBoost;
+  RecoveryLUT recoveryLUT(metadata, display_boost);
 
   JobQueue jobQueue;
   std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
                                        metadata, dest, &jobQueue, &idwTable, output_format,
-                                       &recoveryLUT]() -> void {
-    const float hdr_ratio = metadata->maxContentBoost;
+                                       &recoveryLUT, display_boost]() -> void {
     size_t width = uncompressed_yuv_420_image->width;
     size_t height = uncompressed_yuv_420_image->height;
 
@@ -683,12 +714,13 @@
           } else {
             recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, idwTable);
           }
+
 #if USE_APPLY_RECOVERY_LUT
           Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT);
 #else
-          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
+          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata, display_boost);
 #endif
-          rgb_hdr = rgb_hdr / metadata->maxContentBoost;
+          rgb_hdr = rgb_hdr / display_boost;
           size_t pixel_idx = x + y * width;
 
           switch (output_format) {
diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp
index 38b78ad..ff96447 100644
--- a/libs/jpegrecoverymap/jpegrutils.cpp
+++ b/libs/jpegrecoverymap/jpegrutils.cpp
@@ -253,7 +253,7 @@
 const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
 const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
 
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata) {
     string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
 
     if (xmp_size < nameSpace.size()+2) {
@@ -327,7 +327,7 @@
   return ss.str();
 }
 
-string generateXmpForSecondaryImage(jpegr_metadata& metadata) {
+string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata) {
   const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
   const vector<string> kLiItem({string("rdf:li"), kConItem});
 
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 20c32ed..2cffde3 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -463,6 +463,13 @@
   return e * recoveryFactor;
 }
 
+Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata, float displayBoost) {
+  float logBoost = log2(metadata->minContentBoost) * (1.0f - recovery)
+                 + log2(metadata->maxContentBoost) * recovery;
+  float recoveryFactor = exp2(logBoost * displayBoost / metadata->maxContentBoost);
+  return e * recoveryFactor;
+}
+
 Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT) {
   float recoveryFactor = recoveryLUT.getRecoveryFactor(recovery);
   return e * recoveryFactor;
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 61b3db9..d5da7fb 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -39,7 +39,6 @@
         "libjpegdecoder",
         "libjpegencoder",
         "libjpegrecoverymap",
-        "libskia",
         "libutils",
     ],
 }
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index 0a7d20a..df90f53 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -152,7 +152,8 @@
 
   timerStart(&applyRecMapTime);
   for (auto i = 0; i < kProfileCount; i++) {
-      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest));
+      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG,
+                                     metadata->maxContentBoost /* displayBoost */, dest));
   }
   timerStop(&applyRecMapTime);
 
@@ -170,11 +171,11 @@
   jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
                          nullptr);
   jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
-  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr);
+  jpegRCodec.decodeJPEGR(nullptr, nullptr);
 }
 
 TEST_F(JpegRTest, writeXmpThenRead) {
-  jpegr_metadata metadata_expected;
+  jpegr_metadata_struct metadata_expected;
   metadata_expected.maxContentBoost = 1.25;
   metadata_expected.minContentBoost = 0.75;
   const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -189,7 +190,7 @@
   xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
                   reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
 
-  jpegr_metadata metadata_read;
+  jpegr_metadata_struct metadata_read;
   EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
   ASSERT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
   ASSERT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
@@ -476,7 +477,7 @@
 
   JpegRBenchmark benchmark;
 
-  jpegr_metadata metadata = { .version = 1,
+  jpegr_metadata_struct metadata = { .version = 1,
                               .maxContentBoost = 8.0f,
                               .minContentBoost = 1.0f / 8.0f };
 
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 6c61ff1..5ef79e9 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -554,9 +554,10 @@
 
 TEST_F(RecoveryMapMathTest, applyRecoveryLUT) {
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
-                                .minContentBoost = 1.0f / static_cast<float>(boost) };
+    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+                                       .minContentBoost = 1.0f / static_cast<float>(boost) };
     RecoveryLUT recoveryLUT(&metadata);
+    RecoveryLUT recoveryLUTWithBoost(&metadata, metadata.maxContentBoost);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
       EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
@@ -569,13 +570,24 @@
                       applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
       EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
                       applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlack(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlack(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbWhite(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbWhite(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbRed(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbRed(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbGreen(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbGreen(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlue(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlue(), value, recoveryLUTWithBoost));
     }
   }
 
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
-                                .minContentBoost = 1.0f };
+    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+                                       .minContentBoost = 1.0f };
     RecoveryLUT recoveryLUT(&metadata);
+    RecoveryLUT recoveryLUTWithBoost(&metadata, metadata.maxContentBoost);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
       EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
@@ -588,14 +600,25 @@
                       applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
       EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
                       applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlack(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlack(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbWhite(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbWhite(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbRed(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbRed(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbGreen(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbGreen(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlue(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlue(), value, recoveryLUTWithBoost));
     }
   }
 
   for (int boost = 1; boost <= 10; boost++) {
-    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
-                                .minContentBoost = 1.0f / pow(static_cast<float>(boost),
+    jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+                                       .minContentBoost = 1.0f / pow(static_cast<float>(boost),
                                                               1.0f / 3.0f) };
     RecoveryLUT recoveryLUT(&metadata);
+    RecoveryLUT recoveryLUTWithBoost(&metadata, metadata.maxContentBoost);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
       EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
@@ -608,6 +631,16 @@
                       applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
       EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
                       applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlack(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlack(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbWhite(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbWhite(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbRed(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbRed(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbGreen(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbGreen(), value, recoveryLUTWithBoost));
+      EXPECT_RGB_EQ(applyRecoveryLUT(RgbBlue(), value, recoveryLUT),
+                    applyRecoveryLUT(RgbBlue(), value, recoveryLUTWithBoost));
     }
   }
 }
@@ -659,8 +692,8 @@
 }
 
 TEST_F(RecoveryMapMathTest, EncodeRecovery) {
-  jpegr_metadata metadata = { .maxContentBoost = 4.0f,
-                              .minContentBoost = 1.0f / 4.0f };
+  jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+                                     .minContentBoost = 1.0f / 4.0f };
 
   EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127);
   EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127);
@@ -717,8 +750,9 @@
 }
 
 TEST_F(RecoveryMapMathTest, ApplyRecovery) {
-  jpegr_metadata metadata = { .maxContentBoost = 4.0f,
-                              .minContentBoost = 1.0f / 4.0f };
+  jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+                                     .minContentBoost = 1.0f / 4.0f };
+  float displayBoost = metadata.maxContentBoost;
 
   EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack());
   EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack());
@@ -774,6 +808,19 @@
   EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, &metadata), e);
   EXPECT_RGB_NEAR(applyRecovery(e, 0.75f, &metadata), e * 2.0f);
   EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, &metadata), e * 4.0f);
+
+  EXPECT_RGB_EQ(applyRecovery(RgbBlack(), 1.0f, &metadata),
+                applyRecovery(RgbBlack(), 1.0f, &metadata, displayBoost));
+  EXPECT_RGB_EQ(applyRecovery(RgbWhite(), 1.0f, &metadata),
+                applyRecovery(RgbWhite(), 1.0f, &metadata, displayBoost));
+  EXPECT_RGB_EQ(applyRecovery(RgbRed(), 1.0f, &metadata),
+                applyRecovery(RgbRed(), 1.0f, &metadata, displayBoost));
+  EXPECT_RGB_EQ(applyRecovery(RgbGreen(), 1.0f, &metadata),
+                applyRecovery(RgbGreen(), 1.0f, &metadata, displayBoost));
+  EXPECT_RGB_EQ(applyRecovery(RgbBlue(), 1.0f, &metadata),
+                applyRecovery(RgbBlue(), 1.0f, &metadata, displayBoost));
+  EXPECT_RGB_EQ(applyRecovery(e, 1.0f, &metadata),
+                applyRecovery(e, 1.0f, &metadata, displayBoost));
 }
 
 TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
@@ -981,8 +1028,8 @@
 }
 
 TEST_F(RecoveryMapMathTest, ApplyMap) {
-  jpegr_metadata metadata = { .maxContentBoost = 8.0f,
-                              .minContentBoost = 1.0f / 8.0f };
+  jpegr_metadata_struct metadata = { .maxContentBoost = 8.0f,
+                                     .minContentBoost = 1.0f / 8.0f };
 
   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
                 RgbWhite() * 8.0f);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index cf927db..8060705 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -18,6 +18,7 @@
 
 #include <android/hardware_buffer.h>
 #include <android/hardware_buffer_aidl.h>
+#include <android/binder_libbinder.h>
 #include <vndk/hardware_buffer.h>
 
 #include <errno.h>
@@ -34,9 +35,6 @@
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
 
-// TODO: Better way to handle this
-#include "../binder/ndk/parcel_internal.h"
-
 static constexpr int kFdBufferSize = 128 * sizeof(int);  // 128 ints
 
 using namespace android;
@@ -421,7 +419,7 @@
         AHardwareBuffer* _Nullable* _Nonnull outBuffer) {
     if (!parcel || !outBuffer) return STATUS_BAD_VALUE;
     auto buffer = sp<GraphicBuffer>::make();
-    status_t status = parcel->get()->read(*buffer);
+    status_t status = AParcel_viewPlatformParcel(parcel)->read(*buffer);
     if (status != STATUS_OK) return status;
     *outBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get());
     AHardwareBuffer_acquire(*outBuffer);
@@ -433,7 +431,7 @@
     const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
     if (!gb) return STATUS_BAD_VALUE;
     if (!parcel) return STATUS_BAD_VALUE;
-    return parcel->get()->write(*gb);
+    return AParcel_viewPlatformParcel(parcel)->write(*gb);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 85a5249..21798d0 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -177,14 +177,14 @@
     /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R16_UINT
-     *   OpenGL ES: GR_GL_R16UI
+     *   OpenGL ES: GL_R16UI
      */
     AHARDWAREBUFFER_FORMAT_R16_UINT                 = 0x39,
 
     /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R16G16_UINT
-     *   OpenGL ES: GR_GL_RG16UI
+     *   OpenGL ES: GL_RG16UI
      */
     AHARDWAREBUFFER_FORMAT_R16G16_UINT              = 0x3a,
 
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 5965d41..e393fb2 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -395,10 +395,14 @@
         mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) {
         return;
     }
-    // We currently don't attempt to map a buffer if the buffer contains protected content
-    // because GPU resources for protected buffers is much more limited.
+    // We don't attempt to map a buffer if the buffer contains protected content. In GL this is
+    // important because GPU resources for protected buffers are much more limited. (In Vk we
+    // simply match the existing behavior for protected buffers.)  In Vk, we never cache any
+    // buffers while in a protected context, since Vk cannot share across contexts, and protected
+    // is less common.
     const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
-    if (isProtectedBuffer) {
+    if (isProtectedBuffer ||
+        (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && isProtected())) {
         return;
     }
     ATRACE_CALL();
@@ -461,6 +465,20 @@
     }
 }
 
+std::shared_ptr<AutoBackendTexture::LocalRef> SkiaRenderEngine::getOrCreateBackendTexture(
+        const sp<GraphicBuffer>& buffer, bool isOutputBuffer) {
+    // Do not lookup the buffer in the cache for protected contexts with the SkiaVk back-end
+    if (mRenderEngineType == RenderEngineType::SKIA_GL_THREADED ||
+        (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && !isProtected())) {
+        if (const auto& it = mTextureCache.find(buffer->getId()); it != mTextureCache.end()) {
+            return it->second;
+        }
+    }
+    return std::make_shared<AutoBackendTexture::LocalRef>(getActiveGrContext(),
+                                                          buffer->toAHardwareBuffer(),
+                                                          isOutputBuffer, mTextureCleanupMgr);
+}
+
 bool SkiaRenderEngine::canSkipPostRenderCleanup() const {
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     return mTextureCleanupMgr.isEmpty();
@@ -651,21 +669,11 @@
     validateOutputBufferUsage(buffer->getBuffer());
 
     auto grContext = getActiveGrContext();
-    auto& cache = mTextureCache;
 
     // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
     DeferTextureCleanup dtc(mTextureCleanupMgr);
 
-    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
-    if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
-        surfaceTextureRef = it->second;
-    } else {
-        surfaceTextureRef =
-                std::make_shared<AutoBackendTexture::LocalRef>(grContext,
-                                                               buffer->getBuffer()
-                                                                       ->toAHardwareBuffer(),
-                                                               true, mTextureCleanupMgr);
-    }
+    auto surfaceTextureRef = getOrCreateBackendTexture(buffer->getBuffer(), true);
 
     // wait on the buffer to be ready to use prior to using it
     waitFence(grContext, bufferFence);
@@ -904,21 +912,7 @@
             ATRACE_NAME("DrawImage");
             validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
             const auto& item = layer.source.buffer;
-            std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
-
-            if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
-                iter != cache.end()) {
-                imageTextureRef = iter->second;
-            } else {
-                // If we didn't find the image in the cache, then create a local ref but don't cache
-                // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
-                // we didn't find anything in the cache then we intentionally did not cache this
-                // buffer's resources.
-                imageTextureRef = std::make_shared<
-                        AutoBackendTexture::LocalRef>(grContext,
-                                                      item.buffer->getBuffer()->toAHardwareBuffer(),
-                                                      false, mTextureCleanupMgr);
-            }
+            auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false);
 
             // if the layer's buffer has a fence, then we must must respect the fence prior to using
             // the buffer.
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index dd6646b..e4406b4 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -133,6 +133,8 @@
     void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override final;
     bool canSkipPostRenderCleanup() const override final;
 
+    std::shared_ptr<AutoBackendTexture::LocalRef> getOrCreateBackendTexture(
+            const sp<GraphicBuffer>& buffer, bool isOutputBuffer) REQUIRES(mRenderingMutex);
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
@@ -167,7 +169,9 @@
     // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
     std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
             GUARDED_BY(mRenderingMutex);
-    // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
+    // For GL, this cache is shared between protected and unprotected contexts. For Vulkan, it is
+    // only used for the unprotected context, because Vulkan does not allow sharing between
+    // contexts, and protected is less common.
     std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
             GUARDED_BY(mRenderingMutex);
     std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index c3af996..b6274ab 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -766,162 +766,6 @@
                gralloc4::encodeSmpte2094_10);
 }
 
-template <class T>
-status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    const MetadataType& metadataType,
-                                    DecodeFunction<T> decodeFunction, T* outMetadata) const {
-    if (!outMetadata) {
-        return BAD_VALUE;
-    }
-
-    IMapper::BufferDescriptorInfo descriptorInfo;
-    if (auto error = sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage,
-                                           &descriptorInfo) != OK) {
-        return error;
-    }
-
-    hidl_vec<uint8_t> vec;
-    Error error;
-    auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
-                                                    [&](const auto& tmpError,
-                                                        const hidl_vec<uint8_t>& tmpVec) {
-                                                        error = tmpError;
-                                                        vec = tmpVec;
-                                                    });
-
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-
-    if (error != Error::NONE) {
-        ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
-              metadataType.value, error);
-        return static_cast<status_t>(error);
-    }
-
-    return decodeFunction(vec, outMetadata);
-}
-
-status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
-                                                     PixelFormat format, uint32_t layerCount,
-                                                     uint64_t usage,
-                                                     uint32_t* outPixelFormatFourCC) const {
-    return getDefault(width, height, format, layerCount, usage,
-                      gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
-                      outPixelFormatFourCC);
-}
-
-status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
-                                                       PixelFormat format, uint32_t layerCount,
-                                                       uint64_t usage,
-                                                       uint64_t* outPixelFormatModifier) const {
-    return getDefault(width, height, format, layerCount, usage,
-                      gralloc4::MetadataType_PixelFormatModifier,
-                      gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
-}
-
-status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
-                                                  PixelFormat format, uint32_t layerCount,
-                                                  uint64_t usage,
-                                                  uint64_t* outAllocationSize) const {
-    return getDefault(width, height, format, layerCount, usage,
-                      gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
-                      outAllocationSize);
-}
-
-status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
-                                                    PixelFormat format, uint32_t layerCount,
-                                                    uint64_t usage,
-                                                    uint64_t* outProtectedContent) const {
-    return getDefault(width, height, format, layerCount, usage,
-                      gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
-                      outProtectedContent);
-}
-
-status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
-                                               uint32_t layerCount, uint64_t usage,
-                                               ExtendableType* outCompression) const {
-    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
-                      gralloc4::decodeCompression, outCompression);
-}
-
-status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
-                                               uint32_t layerCount, uint64_t usage,
-                                               ui::Compression* outCompression) const {
-    if (!outCompression) {
-        return BAD_VALUE;
-    }
-    ExtendableType compression;
-    status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
-    if (error) {
-        return error;
-    }
-    if (!gralloc4::isStandardCompression(compression)) {
-        return BAD_TYPE;
-    }
-    *outCompression = gralloc4::getStandardCompressionValue(compression);
-    return NO_ERROR;
-}
-
-status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                              uint32_t layerCount, uint64_t usage,
-                                              ExtendableType* outInterlaced) const {
-    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
-                      gralloc4::decodeInterlaced, outInterlaced);
-}
-
-status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                              uint32_t layerCount, uint64_t usage,
-                                              ui::Interlaced* outInterlaced) const {
-    if (!outInterlaced) {
-        return BAD_VALUE;
-    }
-    ExtendableType interlaced;
-    status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
-    if (error) {
-        return error;
-    }
-    if (!gralloc4::isStandardInterlaced(interlaced)) {
-        return BAD_TYPE;
-    }
-    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
-    return NO_ERROR;
-}
-
-status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
-                                                uint32_t layerCount, uint64_t usage,
-                                                ExtendableType* outChromaSiting) const {
-    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
-                      gralloc4::decodeChromaSiting, outChromaSiting);
-}
-
-status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
-                                                uint32_t layerCount, uint64_t usage,
-                                                ui::ChromaSiting* outChromaSiting) const {
-    if (!outChromaSiting) {
-        return BAD_VALUE;
-    }
-    ExtendableType chromaSiting;
-    status_t error =
-            getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
-    if (error) {
-        return error;
-    }
-    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
-        return BAD_TYPE;
-    }
-    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
-    return NO_ERROR;
-}
-
-status_t Gralloc4Mapper::getDefaultPlaneLayouts(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
-        std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
-    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
-                      gralloc4::decodePlaneLayouts, outPlaneLayouts);
-}
-
 std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
     hidl_vec<MetadataTypeDescription> descriptions;
     Error error;
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index 6f196b8..2106839 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -352,14 +352,12 @@
         }
     }
     {
-        (void)stride;
-        // TODO(b/261856851): Add StandardMetadataType::STRIDE && enable this
-        //        auto value = getStandardMetadata<StandardMetadataType::STRIDE>(mMapper,
-        //        bufferHandle); if (static_cast<BufferUsage>(usage) != value) {
-        //            ALOGW("Layer count didn't match, expected %" PRIu64 " got %" PRId64, usage,
-        //                  static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER)));
-        //            return BAD_VALUE;
-        //        }
+        auto value = getStandardMetadata<StandardMetadataType::STRIDE>(mMapper, bufferHandle);
+        if (stride != value) {
+            ALOGW("Stride didn't match, expected %" PRIu32 " got %" PRId32, stride,
+                  value.value_or(-1));
+            return BAD_VALUE;
+        }
     }
     return OK;
 }
@@ -831,73 +829,4 @@
                                                                    smpte2094_10);
 }
 
-status_t Gralloc5Mapper::getDefaultPixelFormatFourCC(uint32_t, uint32_t, PixelFormat, uint32_t,
-                                                     uint64_t, uint32_t *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultPixelFormatModifier(uint32_t, uint32_t, PixelFormat, uint32_t,
-                                                       uint64_t, uint64_t *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultAllocationSize(uint32_t, uint32_t, PixelFormat, uint32_t,
-                                                  uint64_t, uint64_t *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultProtectedContent(uint32_t, uint32_t, PixelFormat, uint32_t,
-                                                    uint64_t, uint64_t *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultCompression(
-        uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-        aidl::android::hardware::graphics::common::ExtendableType *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultCompression(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-                                               ui::Compression *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultInterlaced(
-        uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-        aidl::android::hardware::graphics::common::ExtendableType *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultInterlaced(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-                                              ui::Interlaced *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultChromaSiting(
-        uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-        aidl::android::hardware::graphics::common::ExtendableType *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultChromaSiting(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-                                                ui::ChromaSiting *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
-status_t Gralloc5Mapper::getDefaultPlaneLayouts(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
-                                                std::vector<ui::PlaneLayout> *) const {
-    // TODO(b/261857910): Remove
-    return UNKNOWN_TRANSACTION;
-}
-
 } // namespace android
\ No newline at end of file
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 6002a6d..7086e04 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -341,84 +341,5 @@
     return mMapper->setSmpte2094_10(bufferHandle, smpte2094_10);
 }
 
-status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
-                                                          PixelFormat format, uint32_t layerCount,
-                                                          uint64_t usage,
-                                                          uint32_t* outPixelFormatFourCC) {
-    return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
-                                                outPixelFormatFourCC);
-}
-
-status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
-                                                            PixelFormat format, uint32_t layerCount,
-                                                            uint64_t usage,
-                                                            uint64_t* outPixelFormatModifier) {
-    return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
-                                                  outPixelFormatModifier);
-}
-
-status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
-                                                       PixelFormat format, uint32_t layerCount,
-                                                       uint64_t usage,
-                                                       uint64_t* outAllocationSize) {
-    return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
-                                             outAllocationSize);
-}
-
-status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
-                                                         PixelFormat format, uint32_t layerCount,
-                                                         uint64_t usage,
-                                                         uint64_t* outProtectedContent) {
-    return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
-                                               outProtectedContent);
-}
-
-status_t GraphicBufferMapper::getDefaultCompression(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
-        aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
-    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
-}
-
-status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
-                                                    PixelFormat format, uint32_t layerCount,
-                                                    uint64_t usage,
-                                                    ui::Compression* outCompression) {
-    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
-}
-
-status_t GraphicBufferMapper::getDefaultInterlaced(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
-        aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
-    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
-}
-
-status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
-                                                   PixelFormat format, uint32_t layerCount,
-                                                   uint64_t usage, ui::Interlaced* outInterlaced) {
-    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
-}
-
-status_t GraphicBufferMapper::getDefaultChromaSiting(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
-        aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
-    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
-                                           outChromaSiting);
-}
-
-status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
-                                                     PixelFormat format, uint32_t layerCount,
-                                                     uint64_t usage,
-                                                     ui::ChromaSiting* outChromaSiting) {
-    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
-                                           outChromaSiting);
-}
-
-status_t GraphicBufferMapper::getDefaultPlaneLayouts(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
-        std::vector<ui::PlaneLayout>* outPlaneLayouts) {
-    return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
-                                           outPlaneLayouts);
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index b494cbe..496ba57 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -200,72 +200,6 @@
                                      std::optional<std::vector<uint8_t>> /*smpte2094_10*/) const {
         return INVALID_OPERATION;
     }
-    virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
-                                                 PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                                 uint64_t /*usage*/,
-                                                 uint32_t* /*outPixelFormatFourCC*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
-                                                   PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                                   uint64_t /*usage*/,
-                                                   uint64_t* /*outPixelFormatModifier*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
-                                              PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                              uint64_t /*usage*/,
-                                              uint64_t* /*outAllocationSize*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
-                                                PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                                uint64_t /*usage*/,
-                                                uint64_t* /*outProtectedContent*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultCompression(
-            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
-            uint32_t /*layerCount*/, uint64_t /*usage*/,
-            aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
-                                           PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                           uint64_t /*usage*/,
-                                           ui::Compression* /*outCompression*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultInterlaced(
-            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
-            uint32_t /*layerCount*/, uint64_t /*usage*/,
-            aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
-                                          PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                          uint64_t /*usage*/,
-                                          ui::Interlaced* /*outInterlaced*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultChromaSiting(
-            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
-            uint32_t /*layerCount*/, uint64_t /*usage*/,
-            aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
-                                            PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                            uint64_t /*usage*/,
-                                            ui::ChromaSiting* /*outChromaSiting*/) const {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDefaultPlaneLayouts(
-            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
-            uint32_t /*layerCount*/, uint64_t /*usage*/,
-            std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
-        return INVALID_OPERATION;
-    }
 };
 
 // A wrapper to IAllocator
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 6bc5ce5..df43be8 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -120,42 +120,6 @@
                              std::optional<std::vector<uint8_t>>* outSmpte2094_10) const override;
     status_t setSmpte2094_10(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>> smpte2094_10) const override;
-    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
-                                         uint32_t layerCount, uint64_t usage,
-                                         uint32_t* outPixelFormatFourCC) const override;
-    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
-                                           uint32_t layerCount, uint64_t usage,
-                                           uint64_t* outPixelFormatModifier) const override;
-    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
-                                      uint32_t layerCount, uint64_t usage,
-                                      uint64_t* outAllocationSize) const override;
-    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
-                                        uint32_t layerCount, uint64_t usage,
-                                        uint64_t* outProtectedContent) const override;
-    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
-                                   uint32_t layerCount, uint64_t usage,
-                                   aidl::android::hardware::graphics::common::ExtendableType*
-                                           outCompression) const override;
-    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
-                                   uint32_t layerCount, uint64_t usage,
-                                   ui::Compression* outCompression) const override;
-    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                  uint32_t layerCount, uint64_t usage,
-                                  aidl::android::hardware::graphics::common::ExtendableType*
-                                          outInterlaced) const override;
-    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                  uint32_t layerCount, uint64_t usage,
-                                  ui::Interlaced* outInterlaced) const override;
-    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    aidl::android::hardware::graphics::common::ExtendableType*
-                                            outChromaSiting) const override;
-    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    ui::ChromaSiting* outChromaSiting) const override;
-    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
 
     std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
     listSupportedMetadataTypes() const;
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
index bc10169..44b97d1 100644
--- a/libs/ui/include/ui/Gralloc5.h
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -156,60 +156,6 @@
             buffer_handle_t bufferHandle,
             std::optional<std::vector<uint8_t>> smpte2094_10) const override;
 
-    [[nodiscard]] status_t getDefaultPixelFormatFourCC(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint32_t *outPixelFormatFourCC) const override;
-
-    [[nodiscard]] status_t getDefaultPixelFormatModifier(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint64_t *outPixelFormatModifier) const override;
-
-    [[nodiscard]] status_t getDefaultAllocationSize(uint32_t width, uint32_t height,
-                                                    PixelFormat format, uint32_t layerCount,
-                                                    uint64_t usage,
-                                                    uint64_t *outAllocationSize) const override;
-
-    [[nodiscard]] status_t getDefaultProtectedContent(uint32_t width, uint32_t height,
-                                                      PixelFormat format, uint32_t layerCount,
-                                                      uint64_t usage,
-                                                      uint64_t *outProtectedContent) const override;
-
-    [[nodiscard]] status_t getDefaultCompression(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType *outCompression)
-            const override;
-
-    [[nodiscard]] status_t getDefaultCompression(uint32_t width, uint32_t height,
-                                                 PixelFormat format, uint32_t layerCount,
-                                                 uint64_t usage,
-                                                 ui::Compression *outCompression) const override;
-
-    [[nodiscard]] status_t getDefaultInterlaced(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType *outInterlaced)
-            const override;
-
-    [[nodiscard]] status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                                uint32_t layerCount, uint64_t usage,
-                                                ui::Interlaced *outInterlaced) const override;
-
-    [[nodiscard]] status_t getDefaultChromaSiting(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting)
-            const override;
-
-    [[nodiscard]] status_t getDefaultChromaSiting(uint32_t width, uint32_t height,
-                                                  PixelFormat format, uint32_t layerCount,
-                                                  uint64_t usage,
-                                                  ui::ChromaSiting *outChromaSiting) const override;
-
-    [[nodiscard]] status_t getDefaultPlaneLayouts(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage, std::vector<ui::PlaneLayout> *outPlaneLayouts) const override;
-
 private:
     void unlockBlocking(buffer_handle_t bufferHandle) const;
 
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 51c6e92..3a5167a 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -138,48 +138,6 @@
     status_t setSmpte2094_10(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>> smpte2094_10);
 
-    /**
-     * Gets the default metadata for a gralloc buffer allocated with the given parameters.
-     *
-     * These functions are supported by gralloc 4.0+.
-     */
-    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
-                                         uint32_t layerCount, uint64_t usage,
-                                         uint32_t* outPixelFormatFourCC);
-    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
-                                           uint32_t layerCount, uint64_t usage,
-                                           uint64_t* outPixelFormatModifier);
-    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
-                                      uint32_t layerCount, uint64_t usage,
-                                      uint64_t* outAllocationSize);
-    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
-                                        uint32_t layerCount, uint64_t usage,
-                                        uint64_t* outProtectedContent);
-    status_t getDefaultCompression(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType* outCompression);
-    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
-                                   uint32_t layerCount, uint64_t usage,
-                                   ui::Compression* outCompression);
-    status_t getDefaultInterlaced(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
-    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
-                                  uint32_t layerCount, uint64_t usage,
-                                  ui::Interlaced* outInterlaced);
-    status_t getDefaultChromaSiting(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-            uint64_t usage,
-            aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
-    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    ui::ChromaSiting* outChromaSiting);
-    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
-                                    uint32_t layerCount, uint64_t usage,
-                                    std::vector<ui::PlaneLayout>* outPlaneLayouts);
-
     const GrallocMapper& getGrallocMapper() const {
         return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0b755aa..c2c856e 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 0527c8a..2bca14d 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 "
@@ -2185,6 +2186,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);
@@ -2218,6 +2223,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);
@@ -2272,6 +2281,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);
@@ -2302,6 +2315,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);
@@ -2387,6 +2404,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/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..f4ea440 100644
--- a/services/gpuservice/vts/Android.bp
+++ b/services/gpuservice/vts/Android.bp
@@ -22,6 +22,7 @@
     libs: [
         "tradefed",
         "vts-core-tradefed-harness",
+        "compatibility-host-util",
     ],
     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/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 9fa9016..290a646 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -19,11 +19,16 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import android.platform.test.annotations.RestrictedBuildTest;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
 
+import com.android.compatibility.common.util.PropertyUtil;
+import com.android.compatibility.common.util.GmsTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -57,15 +62,23 @@
                 commandResult.getStatus(), CommandStatus.SUCCESS);
     }
 
+    @GmsTest(requirement = "VSR-3.3-004")
+    @RestrictedBuildTest
     @Test
     public void testGpuWorkPeriodTracepointFormat() throws Exception {
         CommandResult commandResult = getDevice().executeShellV2Command(
                 String.format("cat %s", GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH));
 
         // If we failed to cat the tracepoint format then the test ends here.
-        assumeTrue(String.format("Failed to cat the gpu_work_period tracepoint format at %s",
-                        GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH),
-                commandResult.getStatus().equals(CommandStatus.SUCCESS));
+        if (!commandResult.getStatus().equals(CommandStatus.SUCCESS)) {
+            String message = String.format(
+                "Failed to cat the gpu_work_period tracepoint format at %s\n",
+                GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH);
+
+            // Tracepoint MUST exist on devices released with Android 14 or later
+            assumeTrue(message, PropertyUtil.getVsrApiLevel(getDevice()) >= 34);
+            fail(message);
+        }
 
         // Otherwise, we check that the fields match the expected fields.
         String actualFields = Arrays.stream(
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 78cdd0d..cd427f0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -110,6 +110,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);
 }
@@ -475,8 +477,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)) {
@@ -486,7 +488,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;
@@ -540,19 +552,16 @@
     return {};
 }
 
-Point resolveTouchedPosition(const MotionEntry& entry) {
+std::pair<float, float> resolveTouchedPosition(const MotionEntry& entry) {
     const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
     // Always dispatch mouse events to cursor position.
     if (isFromMouse) {
-        return Point(static_cast<int32_t>(entry.xCursorPosition),
-                     static_cast<int32_t>(entry.yCursorPosition));
+        return {entry.xCursorPosition, entry.yCursorPosition};
     }
 
     const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
-    return Point(static_cast<int32_t>(
-                         entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)),
-                 static_cast<int32_t>(
-                         entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
+    return {entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X),
+            entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)};
 }
 
 std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
@@ -1159,7 +1168,7 @@
 }
 
 std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
-InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus,
+InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
                                            bool ignoreDragWindow) const {
     // Traverse windows from front to back to find touched window.
     std::vector<InputTarget> outsideTargets;
@@ -1170,7 +1179,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, outsideTargets};
         }
 
@@ -1184,14 +1194,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()) {
@@ -2231,8 +2241,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();
         }
@@ -2270,7 +2279,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);
             outInjectionResult = InputEventInjectionResult::FAILED;
             return {};
@@ -4764,6 +4774,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::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
                                                    const MotionEntry& motionEntry) const {
     const WindowInfo& info = *window->getInfo();
@@ -4801,7 +4817,7 @@
     TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, 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());
             }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index b94858b..2246d47 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -239,11 +239,11 @@
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
     std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>>
-    findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus = false,
+    findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus = false,
                               bool ignoreDragWindow = false) const 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);
@@ -374,6 +374,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.
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 97d57e4..ec0388d 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -242,13 +242,13 @@
 
 input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
     if (map != nullptr) {
-        std::string value;
-        auto prop = std::make_unique<input_property_t>();
-        if (!map->propertyMap->tryGetProperty(key, value)) {
+        std::optional<std::string> value = map->propertyMap->getString(key);
+        if (!value.has_value()) {
             return nullptr;
         }
+        auto prop = std::make_unique<input_property_t>();
         prop->key = key;
-        prop->value = value.c_str();
+        prop->value = value->c_str();
         return prop.release();
     }
     return nullptr;
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 9dbdd5a..95f819a 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -22,6 +22,20 @@
 
 namespace android {
 
+struct FloatPoint {
+    float x;
+    float y;
+
+    inline FloatPoint(float x, float y) : x(x), y(y) {}
+
+    inline explicit FloatPoint(vec2 p) : x(p.x), y(p.y) {}
+
+    template <typename T, typename U>
+    operator std::tuple<T, U>() {
+        return {x, y};
+    }
+};
+
 /**
  * Interface for tracking a mouse / touch pad pointer and touch pad spots.
  *
@@ -40,23 +54,16 @@
 public:
     /* Gets the bounds of the region that the pointer can traverse.
      * Returns true if the bounds are available. */
-    virtual bool getBounds(float* outMinX, float* outMinY,
-            float* outMaxX, float* outMaxY) const = 0;
+    virtual std::optional<FloatRect> getBounds() const = 0;
 
     /* Move the pointer. */
     virtual void move(float deltaX, float deltaY) = 0;
 
-    /* Sets a mask that indicates which buttons are pressed. */
-    virtual void setButtonState(int32_t buttonState) = 0;
-
-    /* Gets a mask that indicates which buttons are pressed. */
-    virtual int32_t getButtonState() const = 0;
-
     /* Sets the absolute location of the pointer. */
     virtual void setPosition(float x, float y) = 0;
 
     /* Gets the absolute location of the pointer. */
-    virtual void getPosition(float* outX, float* outY) const = 0;
+    virtual FloatPoint getPosition() const = 0;
 
     enum class Transition {
         // Fade/unfade immediately.
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 3d3a8ea..e65f3af 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -52,6 +52,7 @@
 #include <utils/Timers.h>
 
 #include <filesystem>
+#include <optional>
 #include <regex>
 #include <utility>
 
@@ -673,9 +674,9 @@
 
 bool EventHub::Device::isExternalDeviceLocked() {
     if (configuration) {
-        bool value;
-        if (configuration->tryGetProperty("device.internal", value)) {
-            return !value;
+        std::optional<bool> isInternal = configuration->getBool("device.internal");
+        if (isInternal.has_value()) {
+            return !isInternal.value();
         }
     }
     return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH;
@@ -683,9 +684,9 @@
 
 bool EventHub::Device::deviceHasMicLocked() {
     if (configuration) {
-        bool value;
-        if (configuration->tryGetProperty("audio.mic", value)) {
-            return value;
+        std::optional<bool> hasMic = configuration->getBool("audio.mic");
+        if (hasMic.has_value()) {
+            return hasMic.value();
         }
     }
     return false;
@@ -2281,8 +2282,8 @@
     }
 
     // See if the device is specially configured to be of a certain type.
-    std::string deviceType;
-    if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
+    if (device->configuration) {
+        std::string deviceType = device->configuration->getString("device.type").value_or("");
         if (deviceType == "rotaryEncoder") {
             device->classes |= InputDeviceClass::ROTARY_ENCODER;
         } else if (deviceType == "externalStylus") {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0d2030e..ddf6c87 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -210,14 +210,13 @@
     // Touchscreens and touchpad devices.
     static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
             sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
-    // TODO(b/246587538): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) and DualSense
-    // (ce6) touchpads, or at least load this setting from the IDC file.
+    // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
+    // at least load this setting from the IDC file.
     const InputDeviceIdentifier identifier = contextPtr->getDeviceIdentifier();
-    const bool isSonyGamepadTouchpad = identifier.vendor == 0x054c &&
-            (identifier.product == 0x05c4 || identifier.product == 0x09cc ||
-             identifier.product == 0x0ce6);
+    const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
+            (identifier.product == 0x05c4 || identifier.product == 0x09cc);
     if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
-        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyGamepadTouchpad) {
+        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
         mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
@@ -466,7 +465,7 @@
                              mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE));
 
     for_each_mapper(
-            [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); });
+            [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
 
     if (mController) {
         mController->populateDeviceInfo(&outDeviceInfo);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 13e4d0c..83cf287 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -20,6 +20,8 @@
 
 #include "CursorInputMapper.h"
 
+#include <optional>
+
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
 #include "PointerControllerInterface.h"
@@ -79,30 +81,31 @@
     return mSource;
 }
 
-void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void CursorInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
     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);
-            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
+        if (const auto bounds = mPointerController->getBounds(); bounds) {
+            info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f,
+                                0.0f, 0.0f);
+            info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f,
+                                0.0f, 0.0f);
         }
     } else {
-        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
-        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
-        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
-                             0.0f);
-        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
-                             0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
+                            0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
+                            0.0f);
     }
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+    info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
 
     if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
     if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
 }
 
@@ -250,18 +253,17 @@
 
 void CursorInputMapper::configureParameters() {
     mParameters.mode = Parameters::Mode::POINTER;
-    std::string cursorModeString;
-    if (getDeviceContext().getConfiguration().tryGetProperty("cursor.mode", cursorModeString)) {
-        if (cursorModeString == "navigation") {
+    const PropertyMap& config = getDeviceContext().getConfiguration();
+    std::optional<std::string> cursorModeString = config.getString("cursor.mode");
+    if (cursorModeString.has_value()) {
+        if (*cursorModeString == "navigation") {
             mParameters.mode = Parameters::Mode::NAVIGATION;
-        } else if (cursorModeString != "pointer" && cursorModeString != "default") {
-            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str());
+        } else if (*cursorModeString != "pointer" && *cursorModeString != "default") {
+            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString->c_str());
         }
     }
 
-    mParameters.orientationAware = false;
-    getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware",
-                                                         mParameters.orientationAware);
+    mParameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
 
     mParameters.hasAssociatedDisplay = false;
     if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
@@ -371,15 +373,10 @@
             if (moved) {
                 mPointerController->move(deltaX, deltaY);
             }
-
-            if (buttonsChanged) {
-                mPointerController->setButtonState(currentButtonState);
-            }
-
             mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
         }
 
-        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
 
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 939cceb..5f02203 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -57,7 +57,7 @@
     virtual ~CursorInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 2809939..a44d15b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -30,11 +30,11 @@
     return AINPUT_SOURCE_STYLUS;
 }
 
-void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
     if (mRawPressureAxis.valid) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
-                             0.0f, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
+                            0.0f, 0.0f);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index b6c9055..11b5315 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -30,7 +30,7 @@
     virtual ~ExternalStylusInputMapper() = default;
 
     uint32_t getSources() const override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index ba2ea99..9cf3696 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -29,8 +29,8 @@
 
 InputMapper::~InputMapper() {}
 
-void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    info->addSource(getSources());
+void InputMapper::populateDeviceInfo(InputDeviceInfo& info) {
+    info.addSource(getSources());
 }
 
 void InputMapper::dump(std::string& dump) {}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 104305b..2722edd 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -51,7 +51,7 @@
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
 
     virtual uint32_t getSources() const = 0;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo);
     virtual void dump(std::string& dump);
     [[nodiscard]] virtual std::list<NotifyArgs> configure(nsecs_t when,
                                                           const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index d7f8b17..7724cf7 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -29,7 +29,7 @@
     return AINPUT_SOURCE_JOYSTICK;
 }
 
-void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
     for (const auto& [_, axis] : mAxes) {
@@ -41,16 +41,16 @@
     }
 }
 
-void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) {
-    info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz,
-                         axis.resolution);
+void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo& info) {
+    info.addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz,
+                        axis.resolution);
     /* In order to ease the transition for developers from using the old axes
      * to the newer, more semantically correct axes, we'll continue to register
      * the old axes as duplicates of their corresponding new ones.  */
     int32_t compatAxis = getCompatAxis(axisId);
     if (compatAxis >= 0) {
-        info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat,
-                             axis.fuzz, axis.resolution);
+        info.addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat,
+                            axis.fuzz, axis.resolution);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 72b8a52..9ca4176 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -26,7 +26,7 @@
     virtual ~JoystickInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
@@ -106,7 +106,7 @@
     static bool isCenteredAxis(int32_t axis);
     static int32_t getCompatAxis(int32_t axis);
 
-    static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
+    static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo& info);
     static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value);
 };
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index dc0454d..269c106 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -85,18 +85,18 @@
     return ADISPLAY_ID_NONE;
 }
 
-void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
-    info->setKeyboardType(mKeyboardType);
-    info->setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
+    info.setKeyboardType(mKeyboardType);
+    info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
 
     if (mKeyboardLayoutInfo) {
-        info->setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
+        info.setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
     } else {
         std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
         if (layoutInfo) {
-            info->setKeyboardLayoutInfo(
+            info.setKeyboardLayoutInfo(
                     KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
         }
     }
@@ -154,15 +154,10 @@
 }
 
 void KeyboardInputMapper::configureParameters() {
-    mParameters.orientationAware = false;
     const PropertyMap& config = getDeviceContext().getConfiguration();
-    config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
-
-    mParameters.handlesKeyRepeat = false;
-    config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
-
-    mParameters.doNotWakeByDefault = false;
-    config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
+    mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
+    mParameters.handlesKeyRepeat = config.getBool("keyboard.handlesKeyRepeat").value_or(false);
+    mParameters.doNotWakeByDefault = config.getBool("keyboard.doNotWakeByDefault").value_or(false);
 }
 
 void KeyboardInputMapper::dumpParameters(std::string& dump) const {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index da5b8ee..2fc82c3 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -27,7 +27,7 @@
     ~KeyboardInputMapper() override = default;
 
     uint32_t getSources() const override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 19a79d7..94cc145 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,8 @@
 
 #include "RotaryEncoderInputMapper.h"
 
+#include <optional>
+
 #include "CursorScrollAccumulator.h"
 
 namespace android {
@@ -35,22 +37,23 @@
     return mSource;
 }
 
-void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
     if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
-        float res = 0.0f;
-        if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) {
+        const PropertyMap& config = getDeviceContext().getConfiguration();
+        std::optional<float> res = config.getFloat("device.res");
+        if (!res.has_value()) {
             ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
         }
-        if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor",
-                                                                  mScalingFactor)) {
+        std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
+        if (!scalingFactor.has_value()) {
             ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
                   "default to 1.0!\n");
-            mScalingFactor = 1.0f;
         }
-        info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                             res * mScalingFactor);
+        mScalingFactor = scalingFactor.value_or(1.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                            res.value_or(0.0f) * mScalingFactor);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index cb5fd88..a0516c4 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -29,7 +29,7 @@
     virtual ~RotaryEncoderInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     virtual void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index f797bc9..60e6727 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -61,12 +61,6 @@
     return AINPUT_SOURCE_SENSOR;
 }
 
-template <typename T>
-bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
-    const auto& config = getDeviceContext().getConfiguration();
-    return config.tryGetProperty(keyName, outValue);
-}
-
 void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
                                                  int32_t sensorDataIndex, const Axis& axis) {
     auto it = mSensors.find(sensorType);
@@ -79,12 +73,12 @@
     }
 }
 
-void SensorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void SensorInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
     for (const auto& [sensorType, sensor] : mSensors) {
-        info->addSensorInfo(sensor.sensorInfo);
-        info->setHasSensor(true);
+        info.addSensorInfo(sensor.sensorInfo);
+        info.setHasSensor(true);
     }
 }
 
@@ -201,6 +195,17 @@
 SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
                                                           const Axis& axis) {
     InputDeviceIdentifier identifier = getDeviceContext().getDeviceIdentifier();
+    const auto& config = getDeviceContext().getConfiguration();
+
+    std::string prefix = "sensor." + ftl::enum_string(sensorType);
+    transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
+
+    int32_t flags = 0;
+    std::optional<int32_t> reportingMode = config.getInt(prefix + ".reportingMode");
+    if (reportingMode.has_value()) {
+        flags |= (*reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
+    }
+
     // Sensor Id will be assigned to device Id to distinguish same sensor from multiple input
     // devices, in such a way that the sensor Id will be same as input device Id.
     // The sensorType is to distinguish different sensors within one device.
@@ -209,28 +214,15 @@
                                      identifier.version, sensorType,
                                      InputDeviceSensorAccuracy::ACCURACY_HIGH,
                                      /*maxRange=*/axis.max, /*resolution=*/axis.scale,
-                                     /*power=*/0.0f, /*minDelay=*/0,
-                                     /*fifoReservedEventCount=*/0, /*fifoMaxEventCount=*/0,
-                                     ftl::enum_string(sensorType), /*maxDelay=*/0, /*flags=*/0,
-                                     getDeviceId());
-
-    std::string prefix = "sensor." + ftl::enum_string(sensorType);
-    transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
-
-    int32_t reportingMode = 0;
-    if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) {
-        sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
-    }
-
-    tryGetProperty(prefix + ".maxDelay", sensorInfo.maxDelay);
-
-    tryGetProperty(prefix + ".minDelay", sensorInfo.minDelay);
-
-    tryGetProperty(prefix + ".power", sensorInfo.power);
-
-    tryGetProperty(prefix + ".fifoReservedEventCount", sensorInfo.fifoReservedEventCount);
-
-    tryGetProperty(prefix + ".fifoMaxEventCount", sensorInfo.fifoMaxEventCount);
+                                     /*power=*/config.getFloat(prefix + ".power").value_or(0.0f),
+                                     /*minDelay=*/config.getInt(prefix + ".minDelay").value_or(0),
+                                     /*fifoReservedEventCount=*/
+                                     config.getInt(prefix + ".fifoReservedEventCount").value_or(0),
+                                     /*fifoMaxEventCount=*/
+                                     config.getInt(prefix + ".fifoMaxEventCount").value_or(0),
+                                     ftl::enum_string(sensorType),
+                                     /*maxDelay=*/config.getInt(prefix + ".maxDelay").value_or(0),
+                                     /*flags=*/flags, getDeviceId());
 
     return Sensor(sensorInfo);
 }
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 457567b..7f47df7 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <optional>
+#include <string>
+
 #include "InputMapper.h"
 
 namespace android {
@@ -28,7 +31,7 @@
     ~SensorInputMapper() override;
 
     uint32_t getSources() const override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
@@ -120,9 +123,6 @@
 
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force);
 
-    template <typename T>
-    bool tryGetProperty(std::string keyName, T& outValue);
-
     void parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
                                   int32_t sensorDataIndex, const Axis& axis);
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index b53fc73..df7ba49 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -134,16 +134,16 @@
     return mSource;
 }
 
-void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
     if (mDeviceMode == DeviceMode::DISABLED) {
         return;
     }
 
-    info->addMotionRange(mOrientedRanges.x);
-    info->addMotionRange(mOrientedRanges.y);
-    info->addMotionRange(mOrientedRanges.pressure);
+    info.addMotionRange(mOrientedRanges.x);
+    info.addMotionRange(mOrientedRanges.y);
+    info.addMotionRange(mOrientedRanges.pressure);
 
     if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
         // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
@@ -153,46 +153,46 @@
         // touchpad in one sample cycle.
         const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
         const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
-        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
-                             x.resolution);
-        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
-                             y.resolution);
+        info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
+                            x.resolution);
+        info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
+                            y.resolution);
     }
 
     if (mOrientedRanges.size) {
-        info->addMotionRange(*mOrientedRanges.size);
+        info.addMotionRange(*mOrientedRanges.size);
     }
 
     if (mOrientedRanges.touchMajor) {
-        info->addMotionRange(*mOrientedRanges.touchMajor);
-        info->addMotionRange(*mOrientedRanges.touchMinor);
+        info.addMotionRange(*mOrientedRanges.touchMajor);
+        info.addMotionRange(*mOrientedRanges.touchMinor);
     }
 
     if (mOrientedRanges.toolMajor) {
-        info->addMotionRange(*mOrientedRanges.toolMajor);
-        info->addMotionRange(*mOrientedRanges.toolMinor);
+        info.addMotionRange(*mOrientedRanges.toolMajor);
+        info.addMotionRange(*mOrientedRanges.toolMinor);
     }
 
     if (mOrientedRanges.orientation) {
-        info->addMotionRange(*mOrientedRanges.orientation);
+        info.addMotionRange(*mOrientedRanges.orientation);
     }
 
     if (mOrientedRanges.distance) {
-        info->addMotionRange(*mOrientedRanges.distance);
+        info.addMotionRange(*mOrientedRanges.distance);
     }
 
     if (mOrientedRanges.tilt) {
-        info->addMotionRange(*mOrientedRanges.tilt);
+        info.addMotionRange(*mOrientedRanges.tilt);
     }
 
     if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
     if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+        info.addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
-    info->setButtonUnderPad(mParameters.hasButtonUnderPad);
-    info->setUsiVersion(mParameters.usiVersion);
+    info.setButtonUnderPad(mParameters.hasButtonUnderPad);
+    info.setUsiVersion(mParameters.usiVersion);
 }
 
 void TouchInputMapper::dump(std::string& dump) {
@@ -366,15 +366,15 @@
             ? Parameters::GestureMode::SINGLE_TOUCH
             : Parameters::GestureMode::MULTI_TOUCH;
 
-    std::string gestureModeString;
-    if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode",
-                                                             gestureModeString)) {
-        if (gestureModeString == "single-touch") {
+    const PropertyMap& config = getDeviceContext().getConfiguration();
+    std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
+    if (gestureModeString.has_value()) {
+        if (*gestureModeString == "single-touch") {
             mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
-        } else if (gestureModeString == "multi-touch") {
+        } else if (*gestureModeString == "multi-touch") {
             mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
-        } else if (gestureModeString != "default") {
-            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str());
+        } else if (*gestureModeString != "default") {
+            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
         }
     }
 
@@ -382,24 +382,23 @@
 
     mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
 
-    mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
-    getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
-                                                         mParameters.orientationAware);
+    mParameters.orientationAware =
+            config.getBool("touch.orientationAware")
+                    .value_or(mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
 
     mParameters.orientation = ui::ROTATION_0;
-    std::string orientationString;
-    if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
-                                                             orientationString)) {
+    std::optional<std::string> orientationString = config.getString("touch.orientation");
+    if (orientationString.has_value()) {
         if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
             ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
-        } else if (orientationString == "ORIENTATION_90") {
+        } else if (*orientationString == "ORIENTATION_90") {
             mParameters.orientation = ui::ROTATION_90;
-        } else if (orientationString == "ORIENTATION_180") {
+        } else if (*orientationString == "ORIENTATION_180") {
             mParameters.orientation = ui::ROTATION_180;
-        } else if (orientationString == "ORIENTATION_270") {
+        } else if (*orientationString == "ORIENTATION_270") {
             mParameters.orientation = ui::ROTATION_270;
-        } else if (orientationString != "ORIENTATION_0") {
-            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
+        } else if (*orientationString != "ORIENTATION_0") {
+            ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
         }
     }
 
@@ -413,10 +412,7 @@
         mParameters.hasAssociatedDisplay = true;
         if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
             mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
-            std::string uniqueDisplayId;
-            getDeviceContext().getConfiguration().tryGetProperty("touch.displayId",
-                                                                 uniqueDisplayId);
-            mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
+            mParameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
         }
     }
     if (getDeviceContext().getAssociatedDisplayPort()) {
@@ -426,20 +422,19 @@
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
     // up in your pocket but you can enable it using the input device configuration.
-    mParameters.wake = getDeviceContext().isExternal();
-    getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
+    mParameters.wake = config.getBool("touch.wake").value_or(getDeviceContext().isExternal());
 
-    InputDeviceUsiVersion usiVersion;
-    if (getDeviceContext().getConfiguration().tryGetProperty("touch.usiVersionMajor",
-                                                             usiVersion.majorVersion) &&
-        getDeviceContext().getConfiguration().tryGetProperty("touch.usiVersionMinor",
-                                                             usiVersion.minorVersion)) {
-        mParameters.usiVersion = usiVersion;
+    std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
+    std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
+    if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
+        mParameters.usiVersion = {
+                .majorVersion = *usiVersionMajor,
+                .minorVersion = *usiVersionMinor,
+        };
     }
 
-    mParameters.enableForInactiveViewport = false;
-    getDeviceContext().getConfiguration().tryGetProperty("touch.enableForInactiveViewport",
-                                                         mParameters.enableForInactiveViewport);
+    mParameters.enableForInactiveViewport =
+            config.getBool("touch.enableForInactiveViewport").value_or(false);
 }
 
 void TouchInputMapper::configureDeviceType() {
@@ -457,7 +452,8 @@
     // Type association takes precedence over the device type found in the idc file.
     std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
     if (deviceTypeString.empty()) {
-        getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString);
+        deviceTypeString =
+                getDeviceContext().getConfiguration().getString("touch.deviceType").value_or("");
     }
     if (deviceTypeString == "touchScreen") {
         mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
@@ -805,40 +801,39 @@
         };
     }
 
-    // Compute oriented precision, scales and ranges.
-    // Note that the maximum value reported is an inclusive maximum value so it is one
-    // unit less than the total width or height of the display.
-    // TODO(b/20508709): Calculate the oriented ranges using the input device's raw frame.
-    switch (mInputDeviceOrientation) {
-        case ui::ROTATION_90:
-        case ui::ROTATION_270:
-            mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = mDisplayBounds.height - 1;
-            mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = 0;
-            mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY();
+    // Oriented X/Y range (in the rotated display's orientation)
+    const FloatRect rawFrame = Rect{mRawPointerAxes.x.minValue, mRawPointerAxes.y.minValue,
+                                    mRawPointerAxes.x.maxValue, mRawPointerAxes.y.maxValue}
+                                       .toFloatRect();
+    const auto orientedRangeRect = mRawToRotatedDisplay.transform(rawFrame);
+    mOrientedRanges.x.min = orientedRangeRect.left;
+    mOrientedRanges.y.min = orientedRangeRect.top;
+    mOrientedRanges.x.max = orientedRangeRect.right;
+    mOrientedRanges.y.max = orientedRangeRect.bottom;
 
-            mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = mDisplayBounds.width - 1;
-            mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = 0;
-            mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX();
-            break;
+    // Oriented flat (in the rotated display's orientation)
+    const auto orientedFlat =
+            transformWithoutTranslation(mRawToRotatedDisplay,
+                                        {static_cast<float>(mRawPointerAxes.x.flat),
+                                         static_cast<float>(mRawPointerAxes.y.flat)});
+    mOrientedRanges.x.flat = std::abs(orientedFlat.x);
+    mOrientedRanges.y.flat = std::abs(orientedFlat.y);
 
-        default:
-            mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = mDisplayBounds.width - 1;
-            mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = 0;
-            mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX();
+    // Oriented fuzz (in the rotated display's orientation)
+    const auto orientedFuzz =
+            transformWithoutTranslation(mRawToRotatedDisplay,
+                                        {static_cast<float>(mRawPointerAxes.x.fuzz),
+                                         static_cast<float>(mRawPointerAxes.y.fuzz)});
+    mOrientedRanges.x.fuzz = std::abs(orientedFuzz.x);
+    mOrientedRanges.y.fuzz = std::abs(orientedFuzz.y);
 
-            mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = mDisplayBounds.height - 1;
-            mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = 0;
-            mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY();
-            break;
-    }
+    // Oriented resolution (in the rotated display's orientation)
+    const auto orientedRes =
+            transformWithoutTranslation(mRawToRotatedDisplay,
+                                        {static_cast<float>(mRawPointerAxes.x.resolution),
+                                         static_cast<float>(mRawPointerAxes.y.resolution)});
+    mOrientedRanges.x.resolution = std::abs(orientedRes.x);
+    mOrientedRanges.y.resolution = std::abs(orientedRes.y);
 }
 
 void TouchInputMapper::computeInputTransforms() {
@@ -1161,92 +1156,79 @@
 
     // Size
     out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
-    std::string sizeCalibrationString;
-    if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) {
-        if (sizeCalibrationString == "none") {
+    std::optional<std::string> sizeCalibrationString = in.getString("touch.size.calibration");
+    if (sizeCalibrationString.has_value()) {
+        if (*sizeCalibrationString == "none") {
             out.sizeCalibration = Calibration::SizeCalibration::NONE;
-        } else if (sizeCalibrationString == "geometric") {
+        } else if (*sizeCalibrationString == "geometric") {
             out.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
-        } else if (sizeCalibrationString == "diameter") {
+        } else if (*sizeCalibrationString == "diameter") {
             out.sizeCalibration = Calibration::SizeCalibration::DIAMETER;
-        } else if (sizeCalibrationString == "box") {
+        } else if (*sizeCalibrationString == "box") {
             out.sizeCalibration = Calibration::SizeCalibration::BOX;
-        } else if (sizeCalibrationString == "area") {
+        } else if (*sizeCalibrationString == "area") {
             out.sizeCalibration = Calibration::SizeCalibration::AREA;
-        } else if (sizeCalibrationString != "default") {
-            ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str());
+        } else if (*sizeCalibrationString != "default") {
+            ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString->c_str());
         }
     }
 
-    float sizeScale;
-
-    if (in.tryGetProperty("touch.size.scale", sizeScale)) {
-        out.sizeScale = sizeScale;
-    }
-    float sizeBias;
-    if (in.tryGetProperty("touch.size.bias", sizeBias)) {
-        out.sizeBias = sizeBias;
-    }
-    bool sizeIsSummed;
-    if (in.tryGetProperty("touch.size.isSummed", sizeIsSummed)) {
-        out.sizeIsSummed = sizeIsSummed;
-    }
+    out.sizeScale = in.getFloat("touch.size.scale");
+    out.sizeBias = in.getFloat("touch.size.bias");
+    out.sizeIsSummed = in.getBool("touch.size.isSummed");
 
     // Pressure
     out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
-    std::string pressureCalibrationString;
-    if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) {
-        if (pressureCalibrationString == "none") {
+    std::optional<std::string> pressureCalibrationString =
+            in.getString("touch.pressure.calibration");
+    if (pressureCalibrationString.has_value()) {
+        if (*pressureCalibrationString == "none") {
             out.pressureCalibration = Calibration::PressureCalibration::NONE;
-        } else if (pressureCalibrationString == "physical") {
+        } else if (*pressureCalibrationString == "physical") {
             out.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
-        } else if (pressureCalibrationString == "amplitude") {
+        } else if (*pressureCalibrationString == "amplitude") {
             out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
-        } else if (pressureCalibrationString != "default") {
+        } else if (*pressureCalibrationString != "default") {
             ALOGW("Invalid value for touch.pressure.calibration: '%s'",
-                  pressureCalibrationString.c_str());
+                  pressureCalibrationString->c_str());
         }
     }
 
-    float pressureScale;
-    if (in.tryGetProperty("touch.pressure.scale", pressureScale)) {
-        out.pressureScale = pressureScale;
-    }
+    out.pressureScale = in.getFloat("touch.pressure.scale");
 
     // Orientation
     out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
-    std::string orientationCalibrationString;
-    if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) {
-        if (orientationCalibrationString == "none") {
+    std::optional<std::string> orientationCalibrationString =
+            in.getString("touch.orientation.calibration");
+    if (orientationCalibrationString.has_value()) {
+        if (*orientationCalibrationString == "none") {
             out.orientationCalibration = Calibration::OrientationCalibration::NONE;
-        } else if (orientationCalibrationString == "interpolated") {
+        } else if (*orientationCalibrationString == "interpolated") {
             out.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
-        } else if (orientationCalibrationString == "vector") {
+        } else if (*orientationCalibrationString == "vector") {
             out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
-        } else if (orientationCalibrationString != "default") {
+        } else if (*orientationCalibrationString != "default") {
             ALOGW("Invalid value for touch.orientation.calibration: '%s'",
-                  orientationCalibrationString.c_str());
+                  orientationCalibrationString->c_str());
         }
     }
 
     // Distance
     out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
-    std::string distanceCalibrationString;
-    if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) {
-        if (distanceCalibrationString == "none") {
+    std::optional<std::string> distanceCalibrationString =
+            in.getString("touch.distance.calibration");
+    if (distanceCalibrationString.has_value()) {
+        if (*distanceCalibrationString == "none") {
             out.distanceCalibration = Calibration::DistanceCalibration::NONE;
-        } else if (distanceCalibrationString == "scaled") {
+        } else if (*distanceCalibrationString == "scaled") {
             out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
-        } else if (distanceCalibrationString != "default") {
+        } else if (*distanceCalibrationString != "default") {
             ALOGW("Invalid value for touch.distance.calibration: '%s'",
-                  distanceCalibrationString.c_str());
+                  distanceCalibrationString->c_str());
         }
     }
 
-    float distanceScale;
-    if (in.tryGetProperty("touch.distance.scale", distanceScale)) {
-        out.distanceScale = distanceScale;
-    }
+    out.distanceScale = in.getFloat("touch.distance.scale");
 }
 
 void TouchInputMapper::resolveCalibration() {
@@ -1676,7 +1658,6 @@
     mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
     mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
 
-    mPointerController->setButtonState(mCurrentRawState.buttonState);
     mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
                                  mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
                                  mCurrentCookedState.cookedPointerData.touchingIdBits |
@@ -2692,8 +2673,7 @@
         // the pointer is hovering again even if the user is not currently touching
         // the touch pad.  This ensures that a view will receive a fresh hover enter
         // event after a tap.
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        const auto [x, y] = mPointerController->getPosition();
 
         PointerProperties pointerProperties;
         pointerProperties.clear();
@@ -2899,8 +2879,7 @@
             mPointerVelocityControl.reset();
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        const auto [x, y] = mPointerController->getPosition();
 
         mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
         mPointerGesture.currentGestureIdBits.clear();
@@ -2926,8 +2905,7 @@
              mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
             lastFingerCount == 1) {
             if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
+                const auto [x, y] = mPointerController->getPosition();
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                     ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP");
@@ -2989,8 +2967,7 @@
         mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
         if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
+                const auto [x, y] = mPointerController->getPosition();
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                     mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -3026,8 +3003,7 @@
             down = false;
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        const auto [x, y] = mPointerController->getPosition();
 
         mPointerGesture.currentGestureIdBits.clear();
         mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3052,8 +3028,6 @@
         prepareMultiFingerPointerGestures(when, outCancelPreviousGesture, outFinishPreviousGesture);
     }
 
-    mPointerController->setButtonState(mCurrentRawState.buttonState);
-
     if (DEBUG_GESTURES) {
         ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
               "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
@@ -3194,8 +3168,8 @@
         mCurrentRawState.rawPointerData
                 .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
                                                &mPointerGesture.referenceTouchY);
-        mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                                        &mPointerGesture.referenceGestureY);
+        std::tie(mPointerGesture.referenceGestureX, mPointerGesture.referenceGestureY) =
+                mPointerController->getPosition();
     }
 
     // Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3510,8 +3484,7 @@
         hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
         down = !hovering;
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        const auto [x, y] = mPointerController->getPosition();
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[index]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3549,9 +3522,8 @@
         down = isPointerDown(mCurrentRawState.buttonState);
         hovering = !down;
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+        const auto [x, y] = mPointerController->getPosition();
+        const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3591,15 +3563,13 @@
     if (down || hovering) {
         mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
         mPointerController->clearSpots();
-        mPointerController->setButtonState(mCurrentRawState.buttonState);
         mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
     } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
         mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
     }
     int32_t displayId = mPointerController->getDisplayId();
 
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
@@ -3820,7 +3790,7 @@
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mDeviceMode == DeviceMode::POINTER) {
-        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
     }
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7b464ef..ae7faa9 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <optional>
+#include <string>
+
 #include <stdint.h>
 #include <ui/Rotation.h>
 
@@ -147,7 +150,7 @@
     ~TouchInputMapper() override;
 
     uint32_t getSources() const override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     void dump(std::string& dump) override;
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 3309767..661461b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -206,6 +206,11 @@
     return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
 }
 
+void TouchpadInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
+    InputMapper::populateDeviceInfo(info);
+    mGestureConverter.populateMotionRanges(info);
+}
+
 void TouchpadInputMapper::dump(std::string& dump) {
     dump += INDENT2 "Touchpad Input Mapper:\n";
     dump += INDENT3 "Gesture converter:\n";
@@ -251,8 +256,9 @@
 
 std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
     mStateConverter.reset();
-    mGestureConverter.reset();
-    return InputMapper::reset(when);
+    std::list<NotifyArgs> out = mGestureConverter.reset(when);
+    out += InputMapper::reset(when);
+    return out;
 }
 
 std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index d693bca..fb36d92 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -41,6 +41,7 @@
     ~TouchpadInputMapper();
 
     uint32_t getSources() const override;
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     void dump(std::string& dump) override;
 
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 7645b12..2c77fc4 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -29,10 +29,10 @@
     return 0;
 }
 
-void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
-    info->setVibrator(true);
+    info.setVibrator(true);
 }
 
 std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index e98f63a..e665973 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -26,7 +26,7 @@
     virtual ~VibratorInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index d636d44..fd2be5f 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -16,13 +16,14 @@
 
 #include "gestures/GestureConverter.h"
 
+#include <optional>
 #include <sstream>
 
 #include <android-base/stringprintf.h>
-#include <android/input.h>
 #include <ftl/enum.h>
 #include <linux/input-event-codes.h>
 #include <log/log_main.h>
+#include <ui/FloatRect.h>
 
 #include "TouchCursorInputMapperCommon.h"
 #include "input/Input.h"
@@ -71,8 +72,54 @@
     return out.str();
 }
 
-void GestureConverter::reset() {
-    mButtonState = 0;
+std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    switch (mCurrentClassification) {
+        case MotionClassification::TWO_FINGER_SWIPE:
+            out.push_back(endScroll(when, when));
+            break;
+        case MotionClassification::MULTI_FINGER_SWIPE:
+            out += handleMultiFingerSwipeLift(when, when);
+            break;
+        case MotionClassification::PINCH:
+            out += endPinch(when, when);
+            break;
+        case MotionClassification::NONE:
+            // When a button is pressed, the Gestures library always ends the current gesture,
+            // so we don't have to worry about the case where buttons need to be lifted during a
+            // pinch or swipe.
+            if (mButtonState) {
+                out += releaseAllButtons(when, when);
+            }
+            break;
+        default:
+            break;
+    }
+    mCurrentClassification = MotionClassification::NONE;
+    mDownTime = 0;
+    return out;
+}
+
+void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
+    info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
+
+    // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture
+    // is enabled.
+    if (std::optional<FloatRect> rect = mPointerController->getBounds(); rect.has_value()) {
+        info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, rect->left, rect->right, 0, 0, 0);
+        info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, rect->top, rect->bottom, 0, 0, 0);
+    }
+
+    info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
+    info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
+
+    // The other axes that can be reported don't have ranges that are easy to define. RELATIVE_X/Y
+    // and GESTURE_SCROLL_X/Y_DISTANCE are the result of acceleration functions being applied to
+    // finger movements, so their maximum values can't simply be derived from the size of the
+    // touchpad. GESTURE_PINCH_SCALE_FACTOR's maximum value depends on the minimum finger separation
+    // that the pad can report, which cannot be determined from its raw axis information. (Assuming
+    // a minimum finger separation of 1 unit would let us calculate a theoretical maximum, but it
+    // would be orders of magnitude too high, so probably not very useful.)
 }
 
 std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
@@ -98,7 +145,6 @@
         case kGestureTypePinch:
             return handlePinch(when, readTime, gesture);
         default:
-            // TODO(b/251196347): handle more gesture types.
             return {};
     }
 }
@@ -111,8 +157,8 @@
     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
     mPointerController->move(deltaX, deltaY);
     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     PointerCoords coords;
     coords.clear();
@@ -136,8 +182,7 @@
     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
 
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     PointerCoords coords;
     coords.clear();
@@ -198,22 +243,56 @@
     return out;
 }
 
+std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+
+    PointerCoords coords;
+    coords.clear();
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+    const bool pointerDown = isPointerDown(mButtonState);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+    uint32_t newButtonState = mButtonState;
+    for (uint32_t button = AMOTION_EVENT_BUTTON_PRIMARY; button <= AMOTION_EVENT_BUTTON_FORWARD;
+         button <<= 1) {
+        if (mButtonState & button) {
+            newButtonState &= ~button;
+            out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                                         button, newButtonState, /*pointerCount=*/1,
+                                         mFingerProps.data(), &coords, xCursorPosition,
+                                         yCursorPosition));
+        }
+    }
+    if (pointerDown) {
+        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
+                                     newButtonState, /*pointerCount=*/1, mFingerProps.data(),
+                                     &coords, xCursorPosition, yCursorPosition));
+    }
+    mButtonState = 0;
+    return out;
+}
+
 std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime,
                                                      const Gesture& gesture) {
     std::list<NotifyArgs> out;
     PointerCoords& coords = mFakeFingerCoords[0];
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
         mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
         coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
         coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
         mDownTime = when;
-        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
-                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
-                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
-                                     yCursorPosition));
+        NotifyMotionArgs args =
+                makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
+                               mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+                               mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+        args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+        out.push_back(args);
     }
     float deltaX = gesture.details.scroll.dx;
     float deltaY = gesture.details.scroll.dy;
@@ -224,9 +303,12 @@
     // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
-    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
-                                 mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
-                                 mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+    NotifyMotionArgs args =
+            makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
+                           mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+                           mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+    args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+    out.push_back(args);
     return out;
 }
 
@@ -239,14 +321,18 @@
         return {};
     }
 
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    return endScroll(when, readTime);
+}
+
+NotifyArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
-    NotifyArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
-                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
-                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
-                                     yCursorPosition);
+    NotifyMotionArgs args =
+            makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+                           mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+                           mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+    args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
     mCurrentClassification = MotionClassification::NONE;
     return args;
 }
@@ -256,8 +342,8 @@
                                                                              uint32_t fingerCount,
                                                                              float dx, float dy) {
     std::list<NotifyArgs> out = {};
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
         // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
         // three and then put a fourth finger down), the gesture library will treat it as two
@@ -292,8 +378,6 @@
                                          yCursorPosition));
         }
     }
-    // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
-    // values.
     float rotatedDeltaX = dx, rotatedDeltaY = -dy;
     rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY);
     for (size_t i = 0; i < mSwipeFingerCount; i++) {
@@ -304,8 +388,6 @@
                             coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY);
     }
     float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
-    // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
-    // values.
     float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
@@ -322,8 +404,7 @@
     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
         return out;
     }
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
 
@@ -346,9 +427,7 @@
 
 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
                                                                   const Gesture& gesture) {
-    std::list<NotifyArgs> out;
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     // Pinch gesture phases are reported a little differently from others, in that the same details
     // struct is used for all phases of the gesture, just with different zoom_state values. When
@@ -371,6 +450,7 @@
         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
         mDownTime = when;
+        std::list<NotifyArgs> out;
         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
                                      mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
@@ -385,19 +465,7 @@
     }
 
     if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
-        mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
-        out.push_back(makeMotionArgs(when, readTime,
-                                     AMOTION_EVENT_ACTION_POINTER_UP |
-                                             1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
-                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
-                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
-                                     yCursorPosition));
-        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
-                                     mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
-                                     mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
-        mCurrentClassification = MotionClassification::NONE;
-        mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
-        return out;
+        return endPinch(when, readTime);
     }
 
     mPinchFingerSeparation *= gesture.details.pinch.dz;
@@ -409,9 +477,27 @@
     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
                                       xCursorPosition + mPinchFingerSeparation / 2);
     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
-    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
-                                 mButtonState, /* pointerCount= */ 2, mFingerProps.data(),
+    return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
+                           mButtonState, /*pointerCount=*/2, mFingerProps.data(),
+                           mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)};
+}
+
+std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
+    out.push_back(makeMotionArgs(when, readTime,
+                                 AMOTION_EVENT_ACTION_POINTER_UP |
+                                         1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
+                                 /*actionButton=*/0, mButtonState, /*pointerCount=*/2,
+                                 mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+                                 yCursorPosition));
+    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
+                                 mButtonState, /*pointerCount=*/1, mFingerProps.data(),
                                  mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+    mCurrentClassification = MotionClassification::NONE;
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
     return out;
 }
 
@@ -421,10 +507,7 @@
                                                   const PointerProperties* pointerProperties,
                                                   const PointerCoords* pointerCoords,
                                                   float xCursorPosition, float yCursorPosition) {
-    // TODO(b/260226362): consider what the appropriate source for these events is.
-    const uint32_t source = AINPUT_SOURCE_MOUSE;
-
-    return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, source,
+    return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
                             mPointerController->getDisplayId(), /* policyFlags= */ POLICY_FLAG_WAKE,
                             action, /* actionButton= */ actionButton, /* flags= */ 0,
                             mReaderContext.getGlobalMetaState(), buttonState,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 2ec5841..2714d03 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -21,6 +21,7 @@
 #include <memory>
 
 #include <PointerControllerInterface.h>
+#include <android/input.h>
 #include <utils/Timers.h>
 
 #include "EventHub.h"
@@ -43,7 +44,9 @@
     std::string dump() const;
 
     void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
-    void reset();
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+
+    void populateMotionRanges(InputDeviceInfo& info) const;
 
     [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
                                                       const Gesture& gesture);
@@ -52,15 +55,19 @@
     [[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
     [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
                                                             const Gesture& gesture);
+    [[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime);
     [[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime,
                                                      const Gesture& gesture);
     [[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+    [[nodiscard]] NotifyArgs endScroll(nsecs_t when, nsecs_t readTime);
+
     [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
                                                                uint32_t fingerCount, float dx,
                                                                float dy);
     [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime);
     [[nodiscard]] std::list<NotifyArgs> handlePinch(nsecs_t when, nsecs_t readTime,
                                                     const Gesture& gesture);
+    [[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime);
 
     NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
                                     int32_t actionButton, int32_t buttonState,
@@ -98,6 +105,9 @@
             {.id = 3, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
     }};
     std::array<PointerCoords, MAX_FAKE_FINGERS> mFakeFingerCoords = {};
+
+    // TODO(b/260226362): consider what the appropriate source for these events is.
+    static constexpr uint32_t SOURCE = AINPUT_SOURCE_MOUSE;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index c091a51..d344bab 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+// clang-format off
+#include "../Macros.h"
+// clang-format on
 #include "gestures/HardwareStateConverter.h"
 
 #include <chrono>
@@ -80,14 +83,9 @@
     schs.fingers.clear();
     for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
         MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
-        if (!slot.isInUse()) {
-            continue;
-        }
         // Some touchpads continue to report contacts even after they've identified them as palms.
-        // We want to exclude these contacts from the HardwareStates, but still need to report a
-        // tracking ID of -1 if a finger turns into a palm.
-        const bool isPalm = slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM;
-        if (isPalm && mFingerSlots.find(i) == mFingerSlots.end()) {
+        // We want to exclude these contacts from the HardwareStates.
+        if (!slot.isInUse() || slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
             continue;
         }
 
@@ -101,12 +99,7 @@
         fingerState.orientation = slot.getOrientation();
         fingerState.position_x = slot.getX();
         fingerState.position_y = slot.getY();
-        fingerState.tracking_id = isPalm ? -1 : slot.getTrackingId();
-        if (fingerState.tracking_id == -1) {
-            mFingerSlots.erase(i);
-        } else {
-            mFingerSlots.insert(i);
-        }
+        fingerState.tracking_id = slot.getTrackingId();
     }
     schs.state.fingers = schs.fingers.data();
     schs.state.finger_cnt = schs.fingers.size();
@@ -117,7 +110,6 @@
 void HardwareStateConverter::reset() {
     mCursorButtonAccumulator.reset(mDeviceContext);
     mTouchButtonAccumulator.reset();
-    mFingerSlots.clear();
     mMscTimestamp = 0;
 }
 
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index d6787b7..c314b0d 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -54,7 +54,6 @@
     MultiTouchMotionAccumulator mMotionAccumulator;
     TouchButtonAccumulator mTouchButtonAccumulator;
     int32_t mMscTimestamp = 0;
-    std::set<size_t> mFingerSlots;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index 3d88338..be2bfed 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -19,6 +19,7 @@
 #include "gestures/PropertyProvider.h"
 
 #include <algorithm>
+#include <optional>
 #include <utility>
 
 #include <android-base/stringprintf.h>
@@ -255,36 +256,34 @@
     bool parsedSuccessfully = false;
     Visitor setVisitor{
             [&](int*) {
-                int32_t value;
-                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
-                if (parsedSuccessfully) {
-                    setIntValues({value});
+                if (std::optional<int32_t> value = idcProperties.getInt(propertyName); value) {
+                    parsedSuccessfully = true;
+                    setIntValues({*value});
                 }
             },
             [&](GesturesPropBool*) {
-                bool value;
-                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
-                if (parsedSuccessfully) {
-                    setBoolValues({value});
+                if (std::optional<bool> value = idcProperties.getBool(propertyName); value) {
+                    parsedSuccessfully = true;
+                    setBoolValues({*value});
                 }
             },
             [&](double*) {
-                double value;
-                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
-                if (parsedSuccessfully) {
-                    setRealValues({value});
+                if (std::optional<double> value = idcProperties.getDouble(propertyName); value) {
+                    parsedSuccessfully = true;
+                    setRealValues({*value});
                 }
             },
             [&](const char**) {
                 ALOGE("Gesture property \"%s\" is a string, and so cannot be set in an IDC file.",
                       mName.c_str());
+                // We've already reported the type mismatch, so set parsedSuccessfully.
+                parsedSuccessfully = true;
             },
     };
     std::visit(setVisitor, mDataPointer);
 
-    ALOGE_IF(!parsedSuccessfully, "Gesture property \"%s\" could set due to a type mismatch.",
+    ALOGE_IF(!parsedSuccessfully, "Gesture property \"%s\" couldn't be set due to a type mismatch.",
              mName.c_str());
-    return;
 }
 
 template <typename T, typename U>
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 28dad95..ca517f3 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -37,17 +37,8 @@
     mY = y;
 }
 
-void FakePointerController::setButtonState(int32_t buttonState) {
-    mButtonState = buttonState;
-}
-
-int32_t FakePointerController::getButtonState() const {
-    return mButtonState;
-}
-
-void FakePointerController::getPosition(float* outX, float* outY) const {
-    *outX = mX;
-    *outY = mY;
+FloatPoint FakePointerController::getPosition() const {
+    return {mX, mY};
 }
 
 int32_t FakePointerController::getDisplayId() const {
@@ -59,8 +50,7 @@
 }
 
 void FakePointerController::assertPosition(float x, float y) {
-    float actualX, actualY;
-    getPosition(&actualX, &actualY);
+    const auto [actualX, actualY] = getPosition();
     ASSERT_NEAR(x, actualX, 1);
     ASSERT_NEAR(y, actualY, 1);
 }
@@ -69,13 +59,8 @@
     return mIsPointerShown;
 }
 
-bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
-                                      float* outMaxY) const {
-    *outMinX = mMinX;
-    *outMinY = mMinY;
-    *outMaxX = mMaxX;
-    *outMaxY = mMaxY;
-    return mHaveBounds;
+std::optional<FloatRect> FakePointerController::getBounds() const {
+    return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt;
 }
 
 void FakePointerController::move(float deltaX, float deltaY) {
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index dd56e65..c374267 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -32,9 +32,7 @@
     const std::map<int32_t, std::vector<int32_t>>& getSpots();
 
     void setPosition(float x, float y) override;
-    void setButtonState(int32_t buttonState) override;
-    int32_t getButtonState() const override;
-    void getPosition(float* outX, float* outY) const override;
+    FloatPoint getPosition() const override;
     int32_t getDisplayId() const override;
     void setDisplayViewport(const DisplayViewport& viewport) override;
 
@@ -42,7 +40,7 @@
     bool isPointerShown();
 
 private:
-    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
+    std::optional<FloatRect> getBounds() const override;
     void move(float deltaX, float deltaY) override;
     void fade(Transition) override;
     void unfade(Transition) override;
@@ -54,7 +52,6 @@
     bool mHaveBounds{false};
     float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
     float mX{0}, mY{0};
-    int32_t mButtonState{0};
     int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
     bool mIsPointerShown{false};
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 9c624ba..33f404d 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -252,14 +252,16 @@
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
                       WithGestureScrollDistance(0, 0, EPSILON),
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
-                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime)));
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime),
+                      WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
     args.pop_front();
     ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                       WithCoords(POINTER_X, POINTER_Y - 10),
                       WithGestureScrollDistance(0, 10, EPSILON),
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
-                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+                      WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
 
     Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -269,7 +271,8 @@
                       WithCoords(POINTER_X, POINTER_Y - 15),
                       WithGestureScrollDistance(0, 5, EPSILON),
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
-                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+                      WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
 
     Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
                          GESTURES_FLING_START);
@@ -280,7 +283,8 @@
                       WithCoords(POINTER_X, POINTER_Y - 15),
                       WithGestureScrollDistance(0, 0, EPSILON),
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
-                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+                      WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
 }
 
 TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -781,4 +785,107 @@
                       WithGesturePinchScaleFactor(0, EPSILON)));
 }
 
+TEST_F(GestureConverterTest, ResetWithButtonPressed) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                        /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+                        /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
+    (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+
+    std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+    ASSERT_EQ(3u, args.size());
+
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                      WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+                      WithCoords(POINTER_X, POINTER_Y),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                      WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+                      WithCoords(POINTER_X, POINTER_Y),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+    args.pop_front();
+    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+                      WithCoords(POINTER_X, POINTER_Y),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringScroll) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+    (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+    ASSERT_EQ(1u, args.size());
+    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                      WithCoords(POINTER_X, POINTER_Y - 10),
+                      WithGestureScrollDistance(0, 0, EPSILON),
+                      WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+                      WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                         /*dy=*/10);
+    (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+    ASSERT_EQ(3u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                      WithGestureOffset(0, 0, EPSILON),
+                      WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+                      WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                      WithGestureOffset(0, 0, EPSILON),
+                      WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+                      WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+                      WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+                      WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringPinch) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+    Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+                         GESTURES_ZOOM_START);
+    (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+    std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                      WithMotionClassification(MotionClassification::PINCH),
+                      WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                      WithMotionClassification(MotionClassification::PINCH),
+                      WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
+                      WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 36b9bab..3e97241 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -231,8 +231,7 @@
 
     schs = processSync(conv, time);
     ASSERT_TRUE(schs.has_value());
-    ASSERT_EQ(1, schs->state.finger_cnt);
-    EXPECT_EQ(-1, schs->state.fingers[0].tracking_id);
+    ASSERT_EQ(0, schs->state.finger_cnt);
 
     processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53);
     processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e4ba241..e299643 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -230,10 +230,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);
         });
     }
 
@@ -3938,8 +3938,7 @@
 public:
     void SetUp() override {
         InputDispatcherTest::SetUp();
-        mDisplayInfos.clear();
-        mWindowInfos.clear();
+        removeAllWindowsAndDisplays();
     }
 
     void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -3955,6 +3954,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() {
@@ -3987,11 +3991,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}});
@@ -4066,6 +4070,81 @@
     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),
+                         [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) {
+                             return ftl::enum_string(testParamInfo.param);
+                         });
+
 using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
                                             sp<IBinder>, sp<IBinder>)>;
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 9732e8d..853c5b0 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,6 +16,7 @@
 
 #include <cinttypes>
 #include <memory>
+#include <optional>
 
 #include <CursorInputMapper.h>
 #include <InputDevice.h>
@@ -122,7 +123,7 @@
 
 static void assertAxisResolution(MultiTouchInputMapper& mapper, int axis, float resolution) {
     InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(info);
 
     const InputDeviceInfo::MotionRange* motionRange =
             info.getMotionRange(axis, AINPUT_SOURCE_TOUCHSCREEN);
@@ -131,7 +132,7 @@
 
 static void assertAxisNotPresent(MultiTouchInputMapper& mapper, int axis) {
     InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(info);
 
     const InputDeviceInfo::MotionRange* motionRange =
             info.getMotionRange(axis, AINPUT_SOURCE_TOUCHSCREEN);
@@ -254,11 +255,11 @@
 private:
     uint32_t getSources() const override { return mSources; }
 
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
+    void populateDeviceInfo(InputDeviceInfo& deviceInfo) override {
         InputMapper::populateDeviceInfo(deviceInfo);
 
         if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
-            deviceInfo->setKeyboardType(mKeyboardType);
+            deviceInfo.setKeyboardType(mKeyboardType);
         }
     }
 
@@ -1830,7 +1831,7 @@
         ::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
 TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
 
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsGenerateKeyEvents) {
     const auto stylusId = TestFixture::mStylusInfo.getId();
 
     TestFixture::mStylus->pressKey(BTN_STYLUS);
@@ -1844,7 +1845,7 @@
                   WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
 }
 
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingTouchGesture) {
     const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
     const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
     const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1890,7 +1891,7 @@
                   WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
 }
 
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingHoveringTouchGesture) {
     const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
     const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
     const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1966,7 +1967,7 @@
                   WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
 }
 
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsWithinTouchGesture) {
     const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
     const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
     const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2020,7 +2021,7 @@
                   WithDeviceId(touchscreenId))));
 }
 
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
     TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
     TestFixture::mReader->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
@@ -2077,7 +2078,7 @@
 // ongoing stylus gesture that is being emitted by the touchscreen.
 using ExternalStylusIntegrationTest = TouchIntegrationTest;
 
-TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureReported) {
     const Point centerPoint = mDevice->getCenterPoint();
 
     // Create an external stylus capable of reporting pressure data that
@@ -2123,7 +2124,7 @@
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
 }
 
-TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureNotReported) {
     const Point centerPoint = mDevice->getCenterPoint();
 
     // Create an external stylus capable of reporting pressure data that
@@ -2191,7 +2192,7 @@
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
 }
 
-TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_UnfusedExternalStylus) {
     const Point centerPoint = mDevice->getCenterPoint();
 
     // Create an external stylus device that does not support pressure. It should not affect any
@@ -2355,10 +2356,10 @@
     InputReaderConfiguration config;
     std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
-    std::string propertyValue;
-    ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
+    std::optional<std::string> propertyValue = mDevice->getConfiguration().getString("key");
+    ASSERT_TRUE(propertyValue.has_value())
             << "Device should have read configuration during configuration phase.";
-    ASSERT_EQ("value", propertyValue);
+    ASSERT_EQ("value", *propertyValue);
 
     ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
@@ -3807,7 +3808,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(info);
 
     // Initially there may not be a valid motion range.
     ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
@@ -3819,7 +3820,7 @@
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
 
     InputDeviceInfo info2;
-    mapper.populateDeviceInfo(&info2);
+    mapper.populateDeviceInfo(info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
@@ -3837,7 +3838,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(info);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
@@ -4113,7 +4114,6 @@
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
-    mFakePointerController->setButtonState(0);
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -4124,14 +4124,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
@@ -4140,21 +4138,18 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4165,26 +4160,20 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
-            motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
-            mFakePointerController->getButtonState());
+              motionArgs.buttonState);
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
-            mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
-            motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
-            mFakePointerController->getButtonState());
+              motionArgs.buttonState);
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
@@ -4193,14 +4182,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
 
@@ -4209,7 +4196,6 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
@@ -4217,14 +4203,12 @@
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
@@ -4239,14 +4223,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4255,14 +4237,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
 
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
@@ -4280,14 +4260,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4296,14 +4274,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4321,14 +4297,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4337,14 +4311,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4362,14 +4334,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4378,14 +4348,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
-    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(
             assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
 
@@ -4400,7 +4368,6 @@
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
-    mFakePointerController->setButtonState(0);
 
     NotifyMotionArgs args;
 
@@ -4427,7 +4394,6 @@
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
-    mFakePointerController->setButtonState(0);
 
     NotifyMotionArgs args;
 
@@ -4606,7 +4572,6 @@
 
     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);
@@ -4634,7 +4599,6 @@
 
     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);
@@ -6927,9 +6891,11 @@
         // four times the resolution of the display in the Y axis.
         prepareButtons();
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, PRECISION_RAW_X_MIN, PRECISION_RAW_X_MAX,
-                                       0, 0);
+                                       PRECISION_RAW_X_FLAT, PRECISION_RAW_X_FUZZ,
+                                       PRECISION_RAW_X_RES);
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, PRECISION_RAW_Y_MIN, PRECISION_RAW_Y_MAX,
-                                       0, 0);
+                                       PRECISION_RAW_Y_FLAT, PRECISION_RAW_Y_FUZZ,
+                                       PRECISION_RAW_Y_RES);
     }
 
     static const int32_t PRECISION_RAW_X_MIN = TouchInputMapperTest::RAW_X_MIN;
@@ -6937,6 +6903,15 @@
     static const int32_t PRECISION_RAW_Y_MIN = TouchInputMapperTest::RAW_Y_MIN;
     static const int32_t PRECISION_RAW_Y_MAX = PRECISION_RAW_Y_MIN + DISPLAY_HEIGHT * 4 - 1;
 
+    static const int32_t PRECISION_RAW_X_RES = 50;  // units per millimeter
+    static const int32_t PRECISION_RAW_Y_RES = 100; // units per millimeter
+
+    static const int32_t PRECISION_RAW_X_FLAT = 16;
+    static const int32_t PRECISION_RAW_Y_FLAT = 32;
+
+    static const int32_t PRECISION_RAW_X_FUZZ = 4;
+    static const int32_t PRECISION_RAW_Y_FUZZ = 8;
+
     static const std::array<Point, 4> kRawCorners;
 };
 
@@ -7094,6 +7069,41 @@
     }
 }
 
+TEST_P(TouchscreenPrecisionTestsFixture, MotionRangesAreOrientedInRotatedDisplay) {
+    const ui::Rotation displayRotation = GetParam();
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(displayRotation);
+
+    __attribute__((unused)) SingleTouchInputMapper& mapper =
+            addMapperAndConfigure<SingleTouchInputMapper>();
+
+    const InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
+    // MotionRanges use display pixels as their units
+    const auto* xRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN);
+    const auto* yRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN);
+
+    // The MotionRanges should be oriented in the rotated display's coordinate space
+    const bool displayRotated =
+            displayRotation == ui::ROTATION_90 || displayRotation == ui::ROTATION_270;
+
+    constexpr float MAX_X = 479.5;
+    constexpr float MAX_Y = 799.75;
+    EXPECT_EQ(xRange->min, 0.f);
+    EXPECT_EQ(yRange->min, 0.f);
+    EXPECT_EQ(xRange->max, displayRotated ? MAX_Y : MAX_X);
+    EXPECT_EQ(yRange->max, displayRotated ? MAX_X : MAX_Y);
+
+    EXPECT_EQ(xRange->flat, 8.f);
+    EXPECT_EQ(yRange->flat, 8.f);
+
+    EXPECT_EQ(xRange->fuzz, 2.f);
+    EXPECT_EQ(yRange->fuzz, 2.f);
+
+    EXPECT_EQ(xRange->resolution, 25.f); // pixels per millimeter
+    EXPECT_EQ(yRange->resolution, 25.f); // pixels per millimeter
+}
+
 // Run the precision tests for all rotations.
 INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFixture,
                          ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
@@ -8434,7 +8444,7 @@
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(info);
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN,
             0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f));
@@ -9117,7 +9127,6 @@
             std::make_shared<FakePointerController>();
     fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(100, 200);
-    fakePointerController->setButtonState(0);
     mFakePolicy->setPointerController(fakePointerController);
 
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
@@ -10112,7 +10121,6 @@
             std::make_shared<FakePointerController>();
     fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(0, 0);
-    fakePointerController->setButtonState(0);
 
     // prepare device and capture
     prepareDisplay(ui::ROTATION_0);
@@ -10262,7 +10270,6 @@
             std::make_shared<FakePointerController>();
     fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(0, 0);
-    fakePointerController->setButtonState(0);
 
     // prepare device and capture
     prepareDisplay(ui::ROTATION_0);
@@ -10402,7 +10409,6 @@
                 std::make_shared<FakePointerController>();
         fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
         fakePointerController->setPosition(0, 0);
-        fakePointerController->setButtonState(0);
         prepareDisplay(ui::ROTATION_0);
 
         prepareAxes(POSITION);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index edd14f8..09f7ae8 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -145,7 +145,7 @@
 
 MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
     *result_listener << "expected flags " << flags << ", but got " << arg.flags;
-    return arg.flags == flags;
+    return arg.flags == static_cast<int32_t>(flags);
 }
 
 MATCHER_P(WithMotionClassification, classification,
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index be85026..9a19b97 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -60,7 +60,7 @@
                     std::list<NotifyArgs> unused =
                             mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
                     InputDeviceInfo info;
-                    mapper.populateDeviceInfo(&info);
+                    mapper.populateDeviceInfo(info);
                 },
                 [&]() -> void {
                     int32_t type, code;
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 8e2d677..33e7dbf 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -59,7 +59,7 @@
                 },
                 [&]() -> void {
                     InputDeviceInfo info;
-                    mapper.populateDeviceInfo(&info);
+                    mapper.populateDeviceInfo(info);
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 546121d..2cb5cdf 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -225,14 +225,21 @@
 public:
     FuzzPointerController(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
     ~FuzzPointerController() {}
-    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
-        return mFdp->ConsumeBool();
+    std::optional<FloatRect> getBounds() const override {
+        if (mFdp->ConsumeBool()) {
+            return {};
+        } else {
+            return FloatRect{mFdp->ConsumeFloatingPoint<float>(),
+                             mFdp->ConsumeFloatingPoint<float>(),
+                             mFdp->ConsumeFloatingPoint<float>(),
+                             mFdp->ConsumeFloatingPoint<float>()};
+        }
     }
     void move(float deltaX, float deltaY) override {}
-    void setButtonState(int32_t buttonState) override {}
-    int32_t getButtonState() const override { return mFdp->ConsumeIntegral<int32_t>(); }
     void setPosition(float x, float y) override {}
-    void getPosition(float* outX, float* outY) const override {}
+    FloatPoint getPosition() const override {
+        return {mFdp->ConsumeFloatingPoint<float>(), mFdp->ConsumeFloatingPoint<float>()};
+    }
     void fade(Transition transition) override {}
     void unfade(Transition transition) override {}
     void setPresentation(Presentation presentation) override {}
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 011455b..59cb94a 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -74,7 +74,7 @@
                 },
                 [&]() -> void {
                     InputDeviceInfo info;
-                    mapper.populateDeviceInfo(&info);
+                    mapper.populateDeviceInfo(info);
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 0fb3cad..398d602 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1648,7 +1648,7 @@
     int deviceId = c->getDeviceId();
     int sensorDeviceId = getDeviceIdFromHandle(sensorHandle);
     if (sensorDeviceId != c->getDeviceId()) {
-        ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs"
+        ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs "
               "to device %d", c->getDeviceId(), sensorDeviceId);
         return BAD_VALUE;
     }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index eae5871..35ca3a5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -210,8 +210,8 @@
     // The dimming flag
     bool dimmingEnabled{true};
 
-    float currentSdrHdrRatio = 1.f;
-    float desiredSdrHdrRatio = 1.f;
+    float currentHdrSdrRatio = 1.f;
+    float desiredHdrSdrRatio = 1.f;
 
     gui::CachingHint cachingHint = gui::CachingHint::Enabled;
     virtual ~LayerFECompositionState();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index d5c488e..ce2b96f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -247,6 +247,10 @@
 
     ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
 
+    float getHdrSdrRatio() const {
+        return getOutputLayer()->getLayerFE().getCompositionState()->currentHdrSdrRatio;
+    };
+
     wp<GraphicBuffer> getBuffer() const { return mBuffer.get(); }
 
     bool isProtected() const { return mIsProtected.get(); }
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 615d04b..426cc57 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -121,9 +121,9 @@
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
     dumpVal(out, "dimming enabled", dimmingEnabled);
-    if (currentSdrHdrRatio > 1.01f || desiredSdrHdrRatio > 1.01f) {
-        dumpVal(out, "current sdr/hdr ratio", currentSdrHdrRatio);
-        dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio);
+    if (currentHdrSdrRatio > 1.01f || desiredHdrSdrRatio > 1.01f) {
+        dumpVal(out, "current hdr/sdr ratio", currentHdrSdrRatio);
+        dumpVal(out, "desired hdr/sdr ratio", desiredHdrSdrRatio);
     }
     dumpVal(out, "colorTransform", colorTransform);
     dumpVal(out, "caching hint", toString(cachingHint));
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1b86cd3..0ac0ecb 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -344,8 +344,8 @@
         // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
         // range that we may need to re-adjust to the current display conditions
         if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
-            layerFEState->currentSdrHdrRatio > 1.01f) {
-            layerBrightnessNits *= layerFEState->currentSdrHdrRatio;
+            layerFEState->currentHdrSdrRatio > 1.01f) {
+            layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
         }
         state.dimmingRatio =
                 std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index a00ce57..8ced0ac 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -378,6 +378,10 @@
             // to avoid flickering/color differences.
             return true;
         }
+        // TODO(b/274804887): temp fix of overdimming issue, skip caching if hsdr/sdr ratio > 1.01f
+        if (layer.getState()->getHdrSdrRatio() > 1.01f) {
+            return true;
+        }
         return false;
     });
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index ca5ba69..bd030d0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -662,6 +662,26 @@
     EXPECT_FALSE(cachedSet.requiresHolePunch());
 }
 
+TEST_F(CachedSetTest, holePunch_requiresNonHdrWithExtendedBrightness) {
+    const auto dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_DCI_P3 |
+                                                      ui::Dataspace::TRANSFER_SRGB |
+                                                      ui::Dataspace::RANGE_EXTENDED);
+    mTestLayers[0]->outputLayerCompositionState.dataspace = dataspace;
+    mTestLayers[0]->layerFECompositionState.currentHdrSdrRatio = 5.f;
+    mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    layerFECompositionState.blendMode = hal::BlendMode::NONE;
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+    CachedSet cachedSet(layer);
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
 TEST_F(CachedSetTest, holePunch_requiresNoBlending) {
     CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
     auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 3cdb3d5..01db0cd 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -167,6 +167,7 @@
             .receivesInput = receivesInput(),
             .isSecure = isSecure(),
             .isPrimary = isPrimary(),
+            .isVirtual = isVirtual(),
             .rotationFlags = ui::Transform::toRotationFlags(mOrientation),
             .transformHint = getTransformHint()};
 }
@@ -181,11 +182,23 @@
         getCompositionDisplay()->applyDisplayBrightness(true);
     }
 
-    mPowerMode = mode;
+    if (mPowerMode) {
+        *mPowerMode = mode;
+    } else {
+        mPowerMode.emplace("PowerMode -" + to_string(getId()), mode);
+    }
 
     getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
 }
 
+void DisplayDevice::tracePowerMode() {
+    // assign the same value for tracing
+    if (mPowerMode) {
+        const hal::PowerMode powerMode = *mPowerMode;
+        *mPowerMode = powerMode;
+    }
+}
+
 void DisplayDevice::enableLayerCaching(bool enable) {
     getCompositionDisplay()->setLayerCachingEnabled(enable);
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d9c3e1c..51876e7 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -175,6 +175,7 @@
     std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
+    void tracePowerMode();
 
     // Enables layer caching on this DisplayDevice
     void enableLayerCaching(bool enable);
@@ -276,7 +277,8 @@
     static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
     // Allow nullopt as initial power mode.
-    std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
+    using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
+    std::optional<TracedPowerMode> mPowerMode;
 
     std::optional<float> mStagedBrightness;
     std::optional<float> mBrightness;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 925f111..ded734e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -885,15 +885,17 @@
 
 void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
                                                nsecs_t previousPresentTime) {
-    if (mPredictionState == PredictionState::Expired ||
-        mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+    const bool presentTimeValid =
+            mSurfaceFlingerActuals.presentTime >= mSurfaceFlingerActuals.startTime;
+    if (mPredictionState == PredictionState::Expired || !presentTimeValid) {
         // Cannot do jank classification with expired predictions or invalid signal times. Set the
         // deltas to 0 as both negative and positive deltas are used as real values.
         mJankType = JankType::Unknown;
         deadlineDelta = 0;
         deltaToVsync = 0;
-        if (mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+        if (!presentTimeValid) {
             mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime;
+            mJankType |= JankType::DisplayHAL;
         }
 
         return;
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 6b9d7a2..76b36fe 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -30,6 +30,7 @@
     bool isSecure;
     // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
     bool isPrimary;
+    bool isVirtual;
     ui::Transform::RotationFlags rotationFlags;
     ui::Transform::RotationFlags transformHint;
     std::string getDebugString() const {
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index 6659825..ce21233 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -65,7 +65,13 @@
     }
 }
 
-LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args)
-      : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {}
+LayerCreationArgs::LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer)
+      : LayerCreationArgs(nullptr, nullptr, /*name=*/"", /*flags=*/0, /*metadata=*/{}, id,
+                          internalLayer) {}
+
+LayerCreationArgs LayerCreationArgs::fromOtherArgs(const LayerCreationArgs& other) {
+    // returns a new instance of LayerCreationArgs with a unique id.
+    return LayerCreationArgs(other.flinger, other.client, other.name, other.flags, other.metadata);
+}
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 2cd6b55..011250c 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -37,11 +37,13 @@
 struct LayerCreationArgs {
     static std::atomic<uint32_t> sSequence;
     static uint32_t getInternalLayerId(uint32_t id);
+    static LayerCreationArgs fromOtherArgs(const LayerCreationArgs& other);
 
     LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
                       uint32_t flags, gui::LayerMetadata, std::optional<uint32_t> id = std::nullopt,
                       bool internalLayer = false);
-    LayerCreationArgs(const LayerCreationArgs&);
+    LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer = false);
+    LayerCreationArgs() = default; // for tracing
 
     android::SurfaceFlinger* flinger;
     sp<android::Client> client;
@@ -56,6 +58,8 @@
     wp<IBinder> parentHandle = nullptr;
     wp<IBinder> mirrorLayerHandle = nullptr;
     ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 3dd89ba..b25b731 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -104,6 +104,16 @@
         static const TraversalPath ROOT;
     };
 
+    struct TraversalPathHash {
+        std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
+            uint32_t hashCode = key.id * 31;
+            if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
+                hashCode += key.mirrorRootId * 31;
+            }
+            return std::hash<size_t>{}(hashCode);
+        }
+    };
+
     // Helper class to add nodes to an existing traversal id and removes the
     // node when it goes out of scope.
     class ScopedAddToTraversalPath {
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 33cc429..3706225 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -20,8 +20,7 @@
 #define LOG_TAG "LayerLifecycleManager"
 
 #include "LayerLifecycleManager.h"
-#include "Layer.h" // temporarily needed for LayerHandle
-#include "LayerHandle.h"
+#include "Client.h" // temporarily needed for LayerCreationArgs
 #include "LayerLog.h"
 #include "SwapErase.h"
 
@@ -72,12 +71,14 @@
     }
 }
 
-void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) {
+void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles,
+                                               bool ignoreUnknownHandles) {
     std::vector<uint32_t> layersToBeDestroyed;
     for (const auto& layerId : destroyedHandles) {
         auto it = mIdToLayer.find(layerId);
         if (it == mIdToLayer.end()) {
-            LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId);
+            LOG_ALWAYS_FATAL_IF(!ignoreUnknownHandles, "%s Layerid not found %d", __func__,
+                                layerId);
             continue;
         }
         RequestedLayerState& layer = it->second.owner;
@@ -167,7 +168,7 @@
     for (const auto& transaction : transactions) {
         for (const auto& resolvedComposerState : transaction.states) {
             const auto& clientState = resolvedComposerState.state;
-            uint32_t layerId = LayerHandle::getLayerId(clientState.surface);
+            uint32_t layerId = resolvedComposerState.layerId;
             if (layerId == UNASSIGNED_LAYER_ID) {
                 ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get());
                 continue;
@@ -175,15 +176,14 @@
 
             RequestedLayerState* layer = getLayerFromId(layerId);
             if (layer == nullptr) {
-                LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__,
-                                 clientState.surface.get(), layerId);
+                LOG_ALWAYS_FATAL("%s Layer with layerid=%d not found", __func__, layerId);
                 continue;
             }
 
             if (!layer->handleAlive) {
-                LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of "
+                LOG_ALWAYS_FATAL("%s Layer's with layerid=%d) is not alive. Possible out of "
                                  "order LayerLifecycleManager updates",
-                                 __func__, clientState.surface.get(), layerId);
+                                 __func__, layerId);
                 continue;
             }
 
@@ -198,13 +198,11 @@
 
             if (layer->what & layer_state_t::eBackgroundColorChanged) {
                 if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
-                    LayerCreationArgs backgroundLayerArgs{nullptr,
-                                                          nullptr,
-                                                          layer->name + "BackgroundColorLayer",
-                                                          ISurfaceComposerClient::eFXSurfaceEffect,
-                                                          {},
-                                                          layer->id,
-                                                          /*internalLayer=*/true};
+                    LayerCreationArgs backgroundLayerArgs(layer->id,
+                                                          /*internalLayer=*/true);
+                    backgroundLayerArgs.parentId = layer->id;
+                    backgroundLayerArgs.name = layer->name + "BackgroundColorLayer";
+                    backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect;
                     std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
                     newLayers.emplace_back(
                             std::make_unique<RequestedLayerState>(backgroundLayerArgs));
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 25d27ee..3d9a74c 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -40,7 +40,10 @@
     // External state changes should be updated in the following order:
     void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>);
     void applyTransactions(const std::vector<TransactionState>&);
-    void onHandlesDestroyed(const std::vector<uint32_t>&);
+    // Ignore unknown handles when iteroping with legacy front end. In the old world, we
+    // would create child layers which are not necessary with the new front end. This means
+    // we will get notified for handle changes that don't exist in the new front end.
+    void onHandlesDestroyed(const std::vector<uint32_t>&, bool ignoreUnknownHandles = false);
 
     // Detaches the layer from its relative parent to prevent a loop in the
     // layer hierarchy. This overrides the RequestedLayerState and leaves
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 8a45093..2d6d8ad 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -28,9 +28,14 @@
                              const LayerHierarchy::TraversalPath& path)
       : path(path) {
     static uint32_t sUniqueSequenceId = 0;
-    // Provide a unique id for clones otherwise keeping using the sequence id.
-    // The seq id can still be useful for debugging if its available.
-    uniqueSequence = (path.isClone()) ? sUniqueSequenceId++ : state.id;
+    // Provide a unique id for all snapshots.
+    // A front end layer can generate multiple snapshots if its mirrored.
+    // Additionally, if the layer is not reachable, we may choose to destroy
+    // and recreate the snapshot in which case the unique sequence id will
+    // change. The consumer shouldn't tie any lifetimes to this unique id but
+    // register a LayerLifecycleManager::ILifecycleListener or get a list of
+    // destroyed layers from LayerLifecycleManager.
+    uniqueSequence = sUniqueSequenceId++;
     sequence = static_cast<int32_t>(state.id);
     name = state.name;
     textureName = state.textureName;
@@ -39,6 +44,8 @@
     inputInfo.id = static_cast<int32_t>(uniqueSequence);
     inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
     inputInfo.ownerPid = state.ownerPid;
+    uid = state.ownerUid;
+    pid = state.ownerPid;
     changes = RequestedLayerState::Changes::Created;
     mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
             ? path
@@ -174,7 +181,12 @@
     std::stringstream debug;
     debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
           << getIsVisibleReason() << "} changes=" << changes.string()
-          << " layerStack=" << outputFilter.layerStack.id << "}";
+          << " layerStack=" << outputFilter.layerStack.id << " geomLayerBounds={"
+          << geomLayerBounds.left << "," << geomLayerBounds.top << "," << geomLayerBounds.bottom
+          << "," << geomLayerBounds.right << "}"
+          << " geomLayerTransform={tx=" << geomLayerTransform.tx()
+          << ",ty=" << geomLayerTransform.ty() << "}"
+          << "}";
     return debug.str();
 }
 
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 6fb2f57..5491d9a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -89,10 +89,13 @@
     gui::GameMode gameMode;
     scheduler::LayerInfo::FrameRate frameRate;
     ui::Transform::RotationFlags fixedTransformHint;
+    std::optional<ui::Transform::RotationFlags> transformHint;
     bool handleSkipScreenshotFlag = false;
     int32_t frameRateSelectionPriority;
     LayerHierarchy::TraversalPath mirrorRootPath;
     bool unreachable = true;
+    uid_t uid;
+    pid_t pid;
     ChildState childState;
 
     static bool isOpaqueFormat(PixelFormat format);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index a16de1b..babcbe7 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -21,10 +21,16 @@
 
 #include "LayerSnapshotBuilder.h"
 #include <gui/TraceUtils.h>
+#include <ui/FloatRect.h>
+
 #include <numeric>
+#include <optional>
+
+#include <gui/TraceUtils.h>
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/Hal.h"
 #include "LayerLog.h"
+#include "LayerSnapshotBuilder.h"
 #include "TimeStats/TimeStats.h"
 #include "ftl/small_map.h"
 
@@ -101,43 +107,52 @@
 }
 
 /**
+ * Returns the bounds used to fill the input frame and the touchable region.
+ *
  * Similar to getInputTransform, we need to update the bounds to include the transform.
  * This is because bounds don't include the buffer transform, where the input assumes
  * that's already included.
  */
-Rect getInputBounds(const LayerSnapshot& snapshot) {
-    if (!snapshot.hasBufferOrSidebandStream()) {
-        return snapshot.croppedBufferSize;
+std::pair<FloatRect, bool> getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) {
+    FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect();
+    if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() &&
+        snapshot.localTransform.getType() != ui::Transform::IDENTITY) {
+        inputBounds = snapshot.localTransform.transform(inputBounds);
     }
 
-    if (snapshot.localTransform.getType() == ui::Transform::IDENTITY ||
-        !snapshot.croppedBufferSize.isValid()) {
-        return snapshot.croppedBufferSize;
+    bool inputBoundsValid = snapshot.croppedBufferSize.isValid();
+    if (!inputBoundsValid) {
+        /**
+         * Input bounds are based on the layer crop or buffer size. But if we are using
+         * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
+         * we can use the parent bounds as the input bounds if the layer does not have buffer
+         * or a crop. We want to unify this logic but because of compat reasons we cannot always
+         * use the parent bounds. A layer without a buffer can get input. So when a window is
+         * initially added, its touchable region can fill its parent layer bounds and that can
+         * have negative consequences.
+         */
+        inputBounds = fillParentBounds ? snapshot.geomLayerBounds : FloatRect{};
     }
-    return snapshot.localTransform.transform(snapshot.croppedBufferSize);
+
+    // Clamp surface inset to the input bounds.
+    const float inset = static_cast<float>(snapshot.inputInfo.surfaceInset);
+    const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
+    const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
+
+    // Apply the insets to the input bounds.
+    inputBounds.left += xSurfaceInset;
+    inputBounds.top += ySurfaceInset;
+    inputBounds.right -= xSurfaceInset;
+    inputBounds.bottom -= ySurfaceInset;
+    return {inputBounds, inputBoundsValid};
 }
 
-void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& screenToDisplay,
-                        const LayerSnapshot& snapshot) {
-    Rect tmpBounds = getInputBounds(snapshot);
-    if (!tmpBounds.isValid()) {
-        info.touchableRegion.clear();
-        // A layer could have invalid input bounds and still expect to receive touch input if it has
-        // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated
-        // correctly to determine the coordinate space for input events. Use an empty rect so that
-        // the layer will receive input in its own layer space.
-        tmpBounds = Rect::EMPTY_RECT;
-    }
-
+Rect getInputBoundsInDisplaySpace(const LayerSnapshot& snapshot, const FloatRect& insetBounds,
+                                  const ui::Transform& screenToDisplay) {
     // InputDispatcher works in the display device's coordinate space. Here, we calculate the
     // frame and transform used for the layer, which determines the bounds and the coordinate space
     // within which the layer will receive input.
-    //
-    // The coordinate space within which each of the bounds are specified is explicitly documented
-    // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A
-    // Transform converts one coordinate space to another, which is apparent in its naming. For
-    // example, "layerToDisplay" transforms layer space to display space.
-    //
+
     // Coordinate space definitions:
     //   - display: The display device's coordinate space. Correlates to pixels on the display.
     //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
@@ -145,37 +160,34 @@
     //   - input: The coordinate space in which this layer will receive input events. This could be
     //            different than layer space if a surfaceInset is used, which changes the origin
     //            of the input space.
-    const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect();
-
-    // Clamp surface inset to the input bounds.
-    const auto surfaceInset = static_cast<float>(info.surfaceInset);
-    const float xSurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f));
-    const float ySurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f));
-
-    // Apply the insets to the input bounds.
-    const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
-                                       inputBoundsInLayer.top + ySurfaceInset,
-                                       inputBoundsInLayer.right - xSurfaceInset,
-                                       inputBoundsInLayer.bottom - ySurfaceInset);
 
     // Crop the input bounds to ensure it is within the parent's bounds.
-    const FloatRect croppedInsetBoundsInLayer =
-            snapshot.geomLayerBounds.intersect(insetBoundsInLayer);
+    const FloatRect croppedInsetBoundsInLayer = snapshot.geomLayerBounds.intersect(insetBounds);
 
     const ui::Transform layerToScreen = getInputTransform(snapshot);
     const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
 
-    const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+    return Rect{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+}
+
+void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& screenToDisplay,
+                        const LayerSnapshot& snapshot) {
+    auto [inputBounds, inputBoundsValid] = getInputBounds(snapshot, /*fillParentBounds=*/false);
+    if (!inputBoundsValid) {
+        info.touchableRegion.clear();
+    }
+
+    const Rect roundedFrameInDisplay =
+            getInputBoundsInDisplaySpace(snapshot, inputBounds, screenToDisplay);
     info.frameLeft = roundedFrameInDisplay.left;
     info.frameTop = roundedFrameInDisplay.top;
     info.frameRight = roundedFrameInDisplay.right;
     info.frameBottom = roundedFrameInDisplay.bottom;
 
     ui::Transform inputToLayer;
-    inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
-    const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;
+    inputToLayer.set(inputBounds.left, inputBounds.top);
+    const ui::Transform layerToScreen = getInputTransform(snapshot);
+    const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
 
     // InputDispatcher expects a display-to-input transform.
     info.transform = inputToDisplay.inverse();
@@ -642,12 +654,14 @@
     snapshot.relativeLayerMetadata.mMap.clear();
 }
 
-uint32_t getDisplayRotationFlags(
-        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays,
-        const ui::LayerStack& layerStack) {
-    static frontend::DisplayInfo sDefaultDisplayInfo = {.isPrimary = false};
-    auto display = displays.get(layerStack).value_or(sDefaultDisplayInfo).get();
-    return display.isPrimary ? display.rotationFlags : 0;
+uint32_t getPrimaryDisplayRotationFlags(
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+    for (auto& [_, display] : displays) {
+        if (display.isPrimary) {
+            return display.rotationFlags;
+        }
+    }
+    return 0;
 }
 
 void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
@@ -674,8 +688,7 @@
             ? requested.layerStack
             : parentSnapshot.outputFilter.layerStack;
 
-    uint32_t displayRotationFlags =
-            getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
+    uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
     const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
             snapshot.changes.any(RequestedLayerState::Changes::Visibility |
                                  RequestedLayerState::Changes::Created);
@@ -689,7 +702,7 @@
                 : Fence::NO_FENCE;
         snapshot.buffer =
                 requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
-        snapshot.bufferSize = requested.getBufferSize(displayRotationFlags);
+        snapshot.bufferSize = requested.getBufferSize(primaryDisplayRotationFlags);
         snapshot.geomBufferSize = snapshot.bufferSize;
         snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
         snapshot.dataspace = requested.dataspace;
@@ -707,8 +720,8 @@
         snapshot.sidebandStream = requested.sidebandStream;
         snapshot.transparentRegionHint = requested.transparentRegion;
         snapshot.color.rgb = requested.getColor().rgb;
-        snapshot.currentSdrHdrRatio = requested.currentSdrHdrRatio;
-        snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio;
+        snapshot.currentHdrSdrRatio = requested.currentHdrSdrRatio;
+        snapshot.desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
     }
 
     if (snapshot.isHiddenByPolicyFromParent &&
@@ -744,15 +757,28 @@
         snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
                 ? requested.gameMode
                 : parentSnapshot.gameMode;
-        snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
-                ? requested.fixedTransformHint
-                : parentSnapshot.fixedTransformHint;
         // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
         // marked as skip capture
         snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
                 (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
     }
 
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren) ||
+        args.displayChanges) {
+        snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
+                ? requested.fixedTransformHint
+                : parentSnapshot.fixedTransformHint;
+
+        if (snapshot.fixedTransformHint != ui::Transform::ROT_INVALID) {
+            snapshot.transformHint = snapshot.fixedTransformHint;
+        } else {
+            const auto display = args.displays.get(snapshot.outputFilter.layerStack);
+            snapshot.transformHint = display.has_value()
+                    ? std::make_optional<>(display->get().transformHint)
+                    : std::nullopt;
+        }
+    }
+
     if (forceUpdate ||
         snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
                              RequestedLayerState::Changes::Hierarchy)) {
@@ -792,7 +818,7 @@
     if (forceUpdate ||
         snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
                              RequestedLayerState::Changes::Geometry)) {
-        updateLayerBounds(snapshot, requested, parentSnapshot, displayRotationFlags);
+        updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
         updateRoundedCorner(snapshot, requested, parentSnapshot);
     }
 
@@ -863,10 +889,10 @@
 void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot,
                                              const RequestedLayerState& requested,
                                              const LayerSnapshot& parentSnapshot,
-                                             uint32_t displayRotationFlags) {
+                                             uint32_t primaryDisplayRotationFlags) {
     snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
     snapshot.geomCrop = requested.crop;
-    snapshot.localTransform = requested.getTransform(displayRotationFlags);
+    snapshot.localTransform = requested.getTransform(primaryDisplayRotationFlags);
     snapshot.localTransformInverse = snapshot.localTransform.inverse();
     snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
     const bool transformWasInvalid = snapshot.invalidTransform;
@@ -880,14 +906,14 @@
                                    requestedT.dsdy(), requestedT.dtdx(), requestedT.dtdy());
         std::string bufferDebug;
         if (requested.externalTexture) {
-            auto unRotBuffer = requested.getUnrotatedBufferSize(displayRotationFlags);
+            auto unRotBuffer = requested.getUnrotatedBufferSize(primaryDisplayRotationFlags);
             auto& destFrame = requested.destinationFrame;
             bufferDebug = base::StringPrintf(" buffer={%d,%d}  displayRot=%d"
                                              " destFrame={%d,%d,%d,%d} unRotBuffer={%d,%d}",
                                              requested.externalTexture->getWidth(),
                                              requested.externalTexture->getHeight(),
-                                             displayRotationFlags, destFrame.left, destFrame.top,
-                                             destFrame.right, destFrame.bottom,
+                                             primaryDisplayRotationFlags, destFrame.left,
+                                             destFrame.top, destFrame.right, destFrame.bottom,
                                              unRotBuffer.getHeight(), unRotBuffer.getWidth());
         }
         ALOGW("Resetting transform for %s because it is invalid.%s%s",
@@ -1008,12 +1034,26 @@
 
     auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
     if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
-        const Rect bounds(cropLayerSnapshot ? cropLayerSnapshot->transformedBounds
-                                            : snapshot.transformedBounds);
-        snapshot.inputInfo.touchableRegion = Region(displayInfo.transform.transform(bounds));
+        Rect inputBoundsInDisplaySpace;
+        if (!cropLayerSnapshot) {
+            FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace =
+                    getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
+        } else {
+            FloatRect inputBounds =
+                    getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace =
+                    getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+                                                 displayInfo.transform);
+        }
+        snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
     } else if (cropLayerSnapshot) {
+        FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+        Rect inputBoundsInDisplaySpace =
+                getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+                                             displayInfo.transform);
         snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(
-                displayInfo.transform.transform(Rect{cropLayerSnapshot->transformedBounds}));
+                displayInfo.transform.transform(inputBoundsInDisplaySpace));
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3997a0a..7b1ff27 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -68,6 +68,7 @@
     void update(const Args&);
     std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots();
     LayerSnapshot* getSnapshot(uint32_t layerId) const;
+    LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;
 
     typedef std::function<void(const LayerSnapshot& snapshot)> ConstVisitor;
 
@@ -86,7 +87,6 @@
 
 private:
     friend class LayerSnapshotTest;
-    LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;
     static LayerSnapshot getRootSnapshot();
 
     // return true if we were able to successfully update the snapshots via
@@ -120,16 +120,8 @@
     void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
                           const Args& args);
 
-    struct TraversalPathHash {
-        std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
-            uint32_t hashCode = key.id * 31;
-            if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
-                hashCode += key.mirrorRootId * 31;
-            }
-            return std::hash<size_t>{}(hashCode);
-        }
-    };
-    std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*, TraversalPathHash>
+    std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
+                       LayerHierarchy::TraversalPathHash>
             mIdToSnapshot;
     std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
     LayerSnapshot mRootSnapshot;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index e2cbe28..1f670c8 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -24,7 +24,6 @@
 
 #include "Layer.h"
 #include "LayerCreationArgs.h"
-#include "LayerHandle.h"
 #include "LayerLog.h"
 #include "RequestedLayerState.h"
 
@@ -33,14 +32,6 @@
 using namespace ftl::flag_operators;
 
 namespace {
-uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
-    if (!surfaceControl) {
-        return UNASSIGNED_LAYER_ID;
-    }
-
-    return LayerHandle::getLayerId(surfaceControl->getHandle());
-}
-
 std::string layerIdToString(uint32_t layerId) {
     return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
 }
@@ -64,17 +55,17 @@
         layerCreationFlags(args.flags),
         textureName(args.textureName),
         ownerUid(args.ownerUid),
-        ownerPid(args.ownerPid) {
+        ownerPid(args.ownerPid),
+        parentId(args.parentId),
+        layerIdToMirror(args.layerIdToMirror) {
     layerId = static_cast<int32_t>(args.sequence);
     changes |= RequestedLayerState::Changes::Created;
     metadata.merge(args.metadata);
     changes |= RequestedLayerState::Changes::Metadata;
     handleAlive = true;
-    parentId = LayerHandle::getLayerId(args.parentHandle.promote());
-    if (args.parentHandle != nullptr) {
+    if (parentId != UNASSIGNED_LAYER_ID) {
         canBeRoot = false;
     }
-    layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
     if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
         changes |= RequestedLayerState::Changes::Mirror;
     } else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) {
@@ -109,8 +100,8 @@
     layerStack = ui::DEFAULT_LAYER_STACK;
     transformToDisplayInverse = false;
     dataspace = ui::Dataspace::UNKNOWN;
-    desiredSdrHdrRatio = 1.f;
-    currentSdrHdrRatio = 1.f;
+    desiredHdrSdrRatio = 1.f;
+    currentHdrSdrRatio = 1.f;
     dataspaceRequested = false;
     hdrMetadata.validTypes = 0;
     surfaceDamageRegion = Region::INVALID_REGION;
@@ -209,7 +200,7 @@
     }
     if (clientState.what & layer_state_t::eReparent) {
         changes |= RequestedLayerState::Changes::Parent;
-        parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
+        parentId = resolvedComposerState.parentId;
         parentSurfaceControlForChild = nullptr;
         // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
         // but thats the existing logic and until we make this behavior more explicit, we need
@@ -218,7 +209,7 @@
     }
     if (clientState.what & layer_state_t::eRelativeLayerChanged) {
         changes |= RequestedLayerState::Changes::RelativeParent;
-        relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl);
+        relativeParentId = resolvedComposerState.relativeParentId;
         isRelativeOf = true;
         relativeLayerSurfaceControl = nullptr;
     }
@@ -235,10 +226,8 @@
         changes |= RequestedLayerState::Changes::RelativeParent;
     }
     if (clientState.what & layer_state_t::eInputInfoChanged) {
-        wp<IBinder>& touchableRegionCropHandle =
-                windowInfoHandle->editInfo()->touchableRegionCropHandle;
-        touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
-        touchableRegionCropHandle.clear();
+        touchCropId = resolvedComposerState.touchCropId;
+        windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
     }
     if (clientState.what & layer_state_t::eStretchChanged) {
         stretchEffect.sanitize();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 6f5485d..216e95f 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -105,17 +105,17 @@
     std::shared_ptr<renderengine::ExternalTexture> externalTexture;
     gui::GameMode gameMode;
     scheduler::LayerInfo::FrameRate requestedFrameRate;
-    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
     uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+    uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
 
     // book keeping states
     bool handleAlive = true;
     bool isRelativeOf = false;
-    uint32_t parentId = UNASSIGNED_LAYER_ID;
-    uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
     std::vector<uint32_t> mirrorIds{};
-    uint32_t touchCropId = UNASSIGNED_LAYER_ID;
-    uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
     ftl::Flags<RequestedLayerState::Changes> changes;
     bool bgColorLayer = false;
 };
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
new file mode 100644
index 0000000..e1449b6
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <gui/DisplayInfo.h>
+
+#include "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android {
+struct LayerCreatedState {
+    LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
+          : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
+    wp<Layer> layer;
+    // Indicates the initial parent of the created layer, only used for creating layer in
+    // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+    wp<Layer> initialParent;
+    // Indicates whether the layer getting created should be added at root if there's no parent
+    // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
+    // be added offscreen.
+    bool addToRoot;
+};
+} // namespace android
+
+namespace android::surfaceflinger::frontend {
+
+// Atomic set of changes affecting layer state. These changes are queued in binder threads and
+// applied every vsync.
+struct Update {
+    std::vector<TransactionState> transactions;
+    std::vector<LayerCreatedState> layerCreatedStates;
+    std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
+    std::vector<LayerCreationArgs> layerCreationArgs;
+    std::vector<uint32_t> destroyedHandles;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index c88554e..9eefbe4 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -40,7 +40,8 @@
 
     for (const auto& listener : toInvoke) {
         ATRACE_NAME("invoking onHdrLayerInfoChanged");
-        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
+                                        info.maxDesiredHdrSdrRatio);
     }
 }
 
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 9b70c16..bf7c775 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -43,27 +43,18 @@
         // With peak display brightnesses exceeding 1,000 nits currently, HLG's request could
         // actually be satisfied in some ambient conditions such that limiting that max for that
         // content in theory makes sense
-        float maxDesiredSdrHdrRatio = 0.f;
+        float maxDesiredHdrSdrRatio = 0.f;
 
         bool operator==(const HdrLayerInfo& other) const {
             return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
-                    maxH == other.maxH && flags == other.flags;
+                    maxH == other.maxH && flags == other.flags &&
+                    maxDesiredHdrSdrRatio == other.maxDesiredHdrSdrRatio;
         }
 
         bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
 
         void mergeDesiredRatio(float update) {
-            if (maxDesiredSdrHdrRatio == 0.f) {
-                // If nothing is set, take the incoming value
-                maxDesiredSdrHdrRatio = update;
-            } else if (update == 1.f) {
-                // If the request is to "go to max", then take it regardless
-                maxDesiredSdrHdrRatio = 1.f;
-            } else if (maxDesiredSdrHdrRatio != 1.f) {
-                // If we're not currently asked to "go to max", then take the max
-                // of the incoming requests
-                maxDesiredSdrHdrRatio = std::max(maxDesiredSdrHdrRatio, update);
-            }
+            maxDesiredHdrSdrRatio = std::max(maxDesiredHdrSdrRatio, update);
         }
     };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0f2af2f..755e585 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -52,8 +52,11 @@
 #include <system/graphics-base-v1.0.h>
 #include <ui/DataspaceUtils.h>
 #include <ui/DebugUtils.h>
+#include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <ui/Transform.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/NativeHandle.h>
@@ -335,6 +338,7 @@
         return nullptr;
     }
     mGetHandleCalled = true;
+    mHandleAlive = true;
     return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this));
 }
 
@@ -639,8 +643,8 @@
     snapshot->surfaceDamage = surfaceDamageRegion;
     snapshot->hasProtectedContent = isProtected();
     snapshot->dimmingEnabled = isDimmingEnabled();
-    snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio();
-    snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio();
+    snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio();
+    snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio();
     snapshot->cachingHint = getCachingHint();
 
     const bool usesRoundedCorners = hasRoundedCorners();
@@ -1501,7 +1505,7 @@
         transformHint = ui::Transform::ROT_0;
     }
 
-    setTransformHint(transformHint);
+    setTransformHintLegacy(transformHint);
 }
 
 // ----------------------------------------------------------------------------
@@ -1649,9 +1653,9 @@
     mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
-size_t Layer::getChildrenCount() const {
+size_t Layer::getDescendantCount() const {
     size_t count = 0;
-    for (const sp<Layer>& child : mCurrentChildren) {
+    for (const sp<Layer>& child : mDrawingChildren) {
         count += 1 + child->getChildrenCount();
     }
     return count;
@@ -1898,6 +1902,12 @@
     }
 }
 
+void Layer::traverseChildren(const LayerVector::Visitor& visitor) {
+    for (const sp<Layer>& child : mDrawingChildren) {
+        visitor(child.get());
+    }
+}
+
 LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                              const std::vector<Layer*>& layersInTree) {
     LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
@@ -2297,62 +2307,21 @@
 }
 
 void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
-    Rect tmpBounds = getInputBounds();
-    if (!tmpBounds.isValid()) {
+    auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false);
+    if (!inputBoundsValid) {
         info.touchableRegion.clear();
-        // A layer could have invalid input bounds and still expect to receive touch input if it has
-        // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated
-        // correctly to determine the coordinate space for input events. Use an empty rect so that
-        // the layer will receive input in its own layer space.
-        tmpBounds = Rect::EMPTY_RECT;
     }
 
-    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
-    // frame and transform used for the layer, which determines the bounds and the coordinate space
-    // within which the layer will receive input.
-    //
-    // The coordinate space within which each of the bounds are specified is explicitly documented
-    // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A
-    // Transform converts one coordinate space to another, which is apparent in its naming. For
-    // example, "layerToDisplay" transforms layer space to display space.
-    //
-    // Coordinate space definitions:
-    //   - display: The display device's coordinate space. Correlates to pixels on the display.
-    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
-    //   - layer: The coordinate space of this layer.
-    //   - input: The coordinate space in which this layer will receive input events. This could be
-    //            different than layer space if a surfaceInset is used, which changes the origin
-    //            of the input space.
-    const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect();
-
-    // Clamp surface inset to the input bounds.
-    const auto surfaceInset = static_cast<float>(info.surfaceInset);
-    const float xSurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f));
-    const float ySurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f));
-
-    // Apply the insets to the input bounds.
-    const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
-                                       inputBoundsInLayer.top + ySurfaceInset,
-                                       inputBoundsInLayer.right - xSurfaceInset,
-                                       inputBoundsInLayer.bottom - ySurfaceInset);
-
-    // Crop the input bounds to ensure it is within the parent's bounds.
-    const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer);
-
-    const ui::Transform layerToScreen = getInputTransform();
-    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
-
-    const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+    const Rect roundedFrameInDisplay = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
     info.frameLeft = roundedFrameInDisplay.left;
     info.frameTop = roundedFrameInDisplay.top;
     info.frameRight = roundedFrameInDisplay.right;
     info.frameBottom = roundedFrameInDisplay.bottom;
 
     ui::Transform inputToLayer;
-    inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
-    const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;
+    inputToLayer.set(inputBounds.left, inputBounds.top);
+    const ui::Transform layerToScreen = getInputTransform();
+    const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
 
     // InputDispatcher expects a display-to-input transform.
     info.transform = inputToDisplay.inverse();
@@ -2485,13 +2454,23 @@
         info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
     }
 
-    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
-        const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds);
-        info.touchableRegion = Region(displayTransform.transform(bounds));
+        Rect inputBoundsInDisplaySpace;
+        if (!cropLayer) {
+            FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        } else {
+            FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace =
+                    cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        }
+        info.touchableRegion = Region(inputBoundsInDisplaySpace);
     } else if (cropLayer != nullptr) {
-        info.touchableRegion = info.touchableRegion.intersect(
-                displayTransform.transform(Rect{cropLayer->mScreenBounds}));
+        FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
+        Rect inputBoundsInDisplaySpace =
+                cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace);
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2513,6 +2492,27 @@
     return info;
 }
 
+Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds,
+                                         const ui::Transform& screenToDisplay) {
+    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
+    // frame and transform used for the layer, which determines the bounds and the coordinate space
+    // within which the layer will receive input.
+
+    // Coordinate space definitions:
+    //   - display: The display device's coordinate space. Correlates to pixels on the display.
+    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
+    //   - layer: The coordinate space of this layer.
+    //   - input: The coordinate space in which this layer will receive input events. This could be
+    //            different than layer space if a surfaceInset is used, which changes the origin
+    //            of the input space.
+
+    // Crop the input bounds to ensure it is within the parent's bounds.
+    const FloatRect croppedInputBounds = mBounds.intersect(inputBounds);
+    const ui::Transform layerToScreen = getInputTransform();
+    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
+    return Rect{layerToDisplay.transform(croppedInputBounds)};
+}
+
 sp<Layer> Layer::getClonedRoot() {
     if (mClonedChild != nullptr) {
         return sp<Layer>::fromExisting(this);
@@ -2865,9 +2865,13 @@
 
 void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->transformHint = mSkipReportingTransformHint
-                ? std::nullopt
-                : std::make_optional<uint32_t>(mTransformHint);
+        if (mFlinger->mLayerLifecycleManagerEnabled) {
+            handle->transformHint = mTransformHint;
+        } else {
+            handle->transformHint = mSkipReportingTransformHint
+                    ? std::nullopt
+                    : std::make_optional<uint32_t>(mTransformHintLegacy);
+        }
         handle->dequeueReadyTime = dequeueReadyTime;
         handle->currentMaxAcquiredBufferCount =
                 mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
@@ -3141,11 +3145,11 @@
 }
 
 bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio) {
-    if (mDrawingState.currentSdrHdrRatio == currentBufferRatio &&
-        mDrawingState.desiredSdrHdrRatio == desiredRatio)
+    if (mDrawingState.currentHdrSdrRatio == currentBufferRatio &&
+        mDrawingState.desiredHdrSdrRatio == desiredRatio)
         return false;
-    mDrawingState.currentSdrHdrRatio = currentBufferRatio;
-    mDrawingState.desiredSdrHdrRatio = desiredRatio;
+    mDrawingState.currentHdrSdrRatio = currentBufferRatio;
+    mDrawingState.desiredHdrSdrRatio = desiredRatio;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -3168,6 +3172,7 @@
 }
 
 bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
     mDrawingState.surfaceDamageRegion = surfaceDamage;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -3399,8 +3404,8 @@
     if (lastDataspace != mBufferInfo.mDataspace) {
         mFlinger->mHdrLayerInfoChanged = true;
     }
-    if (mBufferInfo.mDesiredSdrHdrRatio != mDrawingState.desiredSdrHdrRatio) {
-        mBufferInfo.mDesiredSdrHdrRatio = mDrawingState.desiredSdrHdrRatio;
+    if (mBufferInfo.mDesiredHdrSdrRatio != mDrawingState.desiredHdrSdrRatio) {
+        mBufferInfo.mDesiredHdrSdrRatio = mDrawingState.desiredHdrSdrRatio;
         mFlinger->mHdrLayerInfoChanged = true;
     }
     mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
@@ -3461,20 +3466,46 @@
 }
 
 /**
+ * Returns the bounds used to fill the input frame and the touchable region.
+ *
  * Similar to getInputTransform, we need to update the bounds to include the transform.
  * This is because bounds don't include the buffer transform, where the input assumes
  * that's already included.
  */
-Rect Layer::getInputBounds() const {
-    if (!hasBufferOrSidebandStream()) {
-        return getCroppedBufferSize(getDrawingState());
+std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const {
+    Rect croppedBufferSize = getCroppedBufferSize(getDrawingState());
+    FloatRect inputBounds = croppedBufferSize.toFloatRect();
+    if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() &&
+        mDrawingState.transform.getType() != ui::Transform::IDENTITY) {
+        inputBounds = mDrawingState.transform.transform(inputBounds);
     }
 
-    Rect bufferBounds = getCroppedBufferSize(getDrawingState());
-    if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
-        return bufferBounds;
+    bool inputBoundsValid = croppedBufferSize.isValid();
+    if (!inputBoundsValid) {
+        /**
+         * Input bounds are based on the layer crop or buffer size. But if we are using
+         * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
+         * we can use the parent bounds as the input bounds if the layer does not have buffer
+         * or a crop. We want to unify this logic but because of compat reasons we cannot always
+         * use the parent bounds. A layer without a buffer can get input. So when a window is
+         * initially added, its touchable region can fill its parent layer bounds and that can
+         * have negative consequences.
+         */
+        inputBounds = fillParentBounds ? mBounds : FloatRect{};
     }
-    return mDrawingState.transform.transform(bufferBounds);
+
+    // Clamp surface inset to the input bounds.
+    const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset);
+    const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
+    const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
+
+    // Apply the insets to the input bounds.
+    inputBounds.left += xSurfaceInset;
+    inputBounds.top += ySurfaceInset;
+    inputBounds.right -= xSurfaceInset;
+    inputBounds.bottom -= ySurfaceInset;
+
+    return {inputBounds, inputBoundsValid};
 }
 
 bool Layer::simpleBufferUpdate(const layer_state_t& s) const {
@@ -3660,9 +3691,9 @@
     }
 
     if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
-        if (mDrawingState.currentSdrHdrRatio != s.currentSdrHdrRatio ||
-            mDrawingState.desiredSdrHdrRatio != s.desiredSdrHdrRatio) {
-            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+        if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio ||
+            mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
+            ALOGV("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
             return false;
         }
     }
@@ -4000,10 +4031,10 @@
     return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
 }
 
-void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
-    mTransformHint = getFixedTransformHint();
-    if (mTransformHint == ui::Transform::ROT_INVALID) {
-        mTransformHint = displayTransformHint;
+void Layer::setTransformHintLegacy(ui::Transform::RotationFlags displayTransformHint) {
+    mTransformHintLegacy = getFixedTransformHint();
+    if (mTransformHintLegacy == ui::Transform::ROT_INVALID) {
+        mTransformHintLegacy = displayTransformHint;
     }
     mSkipReportingTransformHint = false;
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2fb122c..1af648a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -225,8 +225,8 @@
         gui::DropInputMode dropInputMode;
         bool autoRefresh = false;
         bool dimmingEnabled = true;
-        float currentSdrHdrRatio = 1.f;
-        float desiredSdrHdrRatio = 1.f;
+        float currentHdrSdrRatio = 1.f;
+        float desiredHdrSdrRatio = 1.f;
         gui::CachingHint cachingHint = gui::CachingHint::Enabled;
         int64_t latchedVsyncId = 0;
     };
@@ -296,8 +296,8 @@
     virtual bool hasColorTransform() const;
     virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
     virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
-    float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; }
-    float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; }
+    float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; }
+    float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; }
     gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
 
     bool setTransform(uint32_t /*transform*/);
@@ -455,8 +455,6 @@
     sp<GraphicBuffer> getBuffer() const;
     const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const;
 
-    ui::Transform::RotationFlags getTransformHint() const { return mTransformHint; }
-
     /*
      * Returns if a frame is ready
      */
@@ -519,7 +517,7 @@
         uint64_t mFrameNumber;
 
         bool mFrameLatencyNeeded{false};
-        float mDesiredSdrHdrRatio = 1.f;
+        float mDesiredHdrSdrRatio = 1.f;
     };
 
     BufferInfo mBufferInfo;
@@ -570,6 +568,8 @@
 
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
+    Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds,
+                                      const ui::Transform& displayTransform);
 
     // Compute bounds for the layer and cache the results.
     void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
@@ -700,6 +700,7 @@
     void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
     void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
     void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+    void traverseChildren(const LayerVector::Visitor&);
 
     /**
      * Traverse only children in z order, ignoring relative layers that are not children of the
@@ -707,7 +708,10 @@
      */
     void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
 
-    size_t getChildrenCount() const;
+    size_t getDescendantCount() const;
+    size_t getChildrenCount() const { return mDrawingChildren.size(); }
+    bool isHandleAlive() const { return mHandleAlive; }
+    bool onHandleDestroyed() { return mHandleAlive = false; }
 
     // ONLY CALL THIS FROM THE LAYER DTOR!
     // See b/141111965.  We need to add current children to offscreen layers in
@@ -848,6 +852,11 @@
     void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
     void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
                                         std::unordered_set<Layer*>& visited);
+    sp<Layer> getClonedFrom() const {
+        return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr;
+    }
+    bool isClone() { return mClonedFrom != nullptr; }
+
     bool willPresentCurrentTransaction() const;
 
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
@@ -867,6 +876,9 @@
         };
     };
     bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
+    void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
+        mTransformHint = transformHint;
+    }
 
 protected:
     // For unit tests
@@ -885,10 +897,6 @@
     void gatherBufferInfo();
     void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
 
-    sp<Layer> getClonedFrom() const {
-        return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr;
-    }
-    bool isClone() { return mClonedFrom != nullptr; }
     bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
 
     void cloneDrawingState(const Layer* from);
@@ -931,7 +939,7 @@
      * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
      * in this layer's space, regardless of the specified crop layer.
      */
-    Rect getInputBounds() const;
+    std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const;
 
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -1147,14 +1155,15 @@
     float mBorderWidth;
     half4 mBorderColor;
 
-    void setTransformHint(ui::Transform::RotationFlags);
+    void setTransformHintLegacy(ui::Transform::RotationFlags);
 
     const uint32_t mTextureName;
 
     // Transform hint provided to the producer. This must be accessed holding
     // the mStateLock.
-    ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+    ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
     bool mSkipReportingTransformHint = true;
+    std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
 
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
@@ -1191,6 +1200,7 @@
     std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
     std::unique_ptr<frontend::LayerSnapshot> mSnapshot =
             std::make_unique<frontend::LayerSnapshot>();
+    bool mHandleAlive = false;
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 55281fa..b5ae1a7 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -15,6 +15,8 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerSnapshot.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
@@ -248,47 +250,88 @@
     outRegion.bottom = proto.bottom();
 }
 
-void LayerProtoHelper::writeHierarchyToProto(
-        LayersProto& outLayersProto, const frontend::LayerHierarchy& root,
-        const frontend::LayerSnapshotBuilder& snapshotBuilder,
-        const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags) {
-    using Variant = frontend::LayerHierarchy::Variant;
-    frontend::LayerSnapshot defaultSnapshot;
-
-    LayerProto* layerProto = outLayersProto.add_layers();
-    const frontend::RequestedLayerState& layer = *root.getLayer();
-    frontend::LayerSnapshot* snapshot = snapshotBuilder.getSnapshot(layer.id);
-
-    if (!snapshot) {
-        defaultSnapshot.uniqueSequence = layer.id;
-        snapshot = &defaultSnapshot;
-    }
-    writeSnapshotToProto(layerProto, layer, *snapshot, traceFlags);
-    for (const auto& [child, variant] : root.mChildren) {
-        if (variant == Variant::Attached || variant == Variant::Detached) {
-            layerProto->add_children(child->getLayer()->id);
-        } else if (variant == Variant::Relative) {
-            layerProto->add_relatives(child->getLayer()->id);
+LayersProto LayerProtoFromSnapshotGenerator::generate(const frontend::LayerHierarchy& root) {
+    mLayersProto.clear_layers();
+    std::unordered_set<uint64_t> stackIdsToSkip;
+    if ((mTraceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+        for (const auto& [layerStack, displayInfo] : mDisplayInfos) {
+            if (displayInfo.isVirtual) {
+                stackIdsToSkip.insert(layerStack.id);
+            }
         }
     }
 
-    auto parent = root.getParent();
-    if (parent && parent->getLayer()) {
-        layerProto->set_parent(parent->getLayer()->id);
-    } else {
-        layerProto->set_parent(-1);
+    frontend::LayerHierarchy::TraversalPath path = frontend::LayerHierarchy::TraversalPath::ROOT;
+    for (auto& [child, variant] : root.mChildren) {
+        if (variant != frontend::LayerHierarchy::Variant::Attached ||
+            stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+            continue;
+        }
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, path);
     }
 
-    auto relativeParent = root.getRelativeParent();
-    if (relativeParent && relativeParent->getLayer()) {
-        layerProto->set_z_order_relative_of(relativeParent->getLayer()->id);
-    } else {
-        layerProto->set_z_order_relative_of(-1);
+    // fill in relative and parent info
+    for (int i = 0; i < mLayersProto.layers_size(); i++) {
+        auto layerProto = mLayersProto.mutable_layers()->Mutable(i);
+        auto it = mChildToRelativeParent.find(layerProto->id());
+        if (it == mChildToRelativeParent.end()) {
+            layerProto->set_z_order_relative_of(-1);
+        } else {
+            layerProto->set_z_order_relative_of(it->second);
+        }
+        it = mChildToParent.find(layerProto->id());
+        if (it == mChildToParent.end()) {
+            layerProto->set_parent(-1);
+        } else {
+            layerProto->set_parent(it->second);
+        }
     }
 
-    if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
-        auto it = legacyLayers.find(layer.id);
-        if (it != legacyLayers.end()) {
+    mDefaultSnapshots.clear();
+    mChildToRelativeParent.clear();
+    return std::move(mLayersProto);
+}
+
+frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
+        frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer) {
+    frontend::LayerSnapshot* snapshot = mSnapshotBuilder.getSnapshot(path);
+    if (snapshot) {
+        return snapshot;
+    } else {
+        mDefaultSnapshots[path] = frontend::LayerSnapshot(layer, path);
+        return &mDefaultSnapshots[path];
+    }
+}
+
+void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
+        const frontend::LayerHierarchy& root, frontend::LayerHierarchy::TraversalPath& path) {
+    using Variant = frontend::LayerHierarchy::Variant;
+    LayerProto* layerProto = mLayersProto.add_layers();
+    const frontend::RequestedLayerState& layer = *root.getLayer();
+    frontend::LayerSnapshot* snapshot = getSnapshot(path, layer);
+    LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
+
+    for (const auto& [child, variant] : root.mChildren) {
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer);
+        if (variant == Variant::Attached || variant == Variant::Detached ||
+            variant == Variant::Mirror) {
+            mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
+            layerProto->add_children(childSnapshot->uniqueSequence);
+        } else if (variant == Variant::Relative) {
+            mChildToRelativeParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
+            layerProto->add_relatives(childSnapshot->uniqueSequence);
+        }
+    }
+
+    if (mTraceFlags & LayerTracing::TRACE_COMPOSITION) {
+        auto it = mLegacyLayers.find(layer.id);
+        if (it != mLegacyLayers.end()) {
             it->second->writeCompositionStateToProto(layerProto);
         }
     }
@@ -298,7 +341,10 @@
         if (variant == Variant::Detached) {
             continue;
         }
-        writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags);
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        writeHierarchyToProto(*child, path);
     }
 }
 
@@ -345,6 +391,7 @@
     layerInfo->set_shadow_radius(snapshot.shadowRadius);
 
     layerInfo->set_id(snapshot.uniqueSequence);
+    layerInfo->set_original_id(snapshot.sequence);
     layerInfo->set_name(requestedState.name);
     layerInfo->set_type("Layer");
 
@@ -394,6 +441,22 @@
                                    [&]() { return layerInfo->mutable_destination_frame(); });
 }
 
+google::protobuf::RepeatedPtrField<DisplayProto> LayerProtoHelper::writeDisplayInfoToProto(
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos) {
+    google::protobuf::RepeatedPtrField<DisplayProto> displays;
+    displays.Reserve(displayInfos.size());
+    for (const auto& [layerStack, displayInfo] : displayInfos) {
+        auto displayProto = displays.Add();
+        displayProto->set_id(displayInfo.info.displayId);
+        displayProto->set_layer_stack(layerStack.id);
+        displayProto->mutable_size()->set_w(displayInfo.info.logicalWidth);
+        displayProto->mutable_size()->set_h(displayInfo.info.logicalHeight);
+        writeTransformToProto(displayInfo.transform, displayProto->mutable_transform());
+        displayProto->set_is_virtual(displayInfo.isVirtual);
+    }
+    return displays;
+}
+
 } // namespace surfaceflinger
 } // namespace android
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index de4bd01..b84a49b 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -25,6 +25,9 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
+#include <cstdint>
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerSnapshot.h"
 
 namespace android {
 namespace surfaceflinger {
@@ -58,15 +61,44 @@
     static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
     static void writeToProto(const android::BlurRegion region, BlurRegion*);
     static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
-    static void writeHierarchyToProto(LayersProto& layersProto,
-                                      const frontend::LayerHierarchy& root,
-                                      const frontend::LayerSnapshotBuilder& snapshotBuilder,
-                                      const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers,
-                                      uint32_t traceFlags);
-
     static void writeSnapshotToProto(LayerProto* outProto,
                                      const frontend::RequestedLayerState& requestedState,
                                      const frontend::LayerSnapshot& snapshot, uint32_t traceFlags);
+    static google::protobuf::RepeatedPtrField<DisplayProto> writeDisplayInfoToProto(
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos);
+};
+
+class LayerProtoFromSnapshotGenerator {
+public:
+    LayerProtoFromSnapshotGenerator(
+            const frontend::LayerSnapshotBuilder& snapshotBuilder,
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+            const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags)
+          : mSnapshotBuilder(snapshotBuilder),
+            mLegacyLayers(legacyLayers),
+            mDisplayInfos(displayInfos),
+            mTraceFlags(traceFlags) {}
+    LayersProto generate(const frontend::LayerHierarchy& root);
+
+private:
+    void writeHierarchyToProto(const frontend::LayerHierarchy& root,
+                               frontend::LayerHierarchy::TraversalPath& path);
+    frontend::LayerSnapshot* getSnapshot(frontend::LayerHierarchy::TraversalPath& path,
+                                         const frontend::RequestedLayerState& layer);
+
+    const frontend::LayerSnapshotBuilder& mSnapshotBuilder;
+    const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers;
+    const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& mDisplayInfos;
+    uint32_t mTraceFlags;
+    LayersProto mLayersProto;
+    // winscope expects all the layers, so provide a snapshot even if it not currently drawing
+    std::unordered_map<frontend::LayerHierarchy::TraversalPath, frontend::LayerSnapshot,
+                       frontend::LayerHierarchy::TraversalPathHash>
+            mDefaultSnapshots;
+    std::unordered_map<uint32_t /* child unique seq*/, uint32_t /* relative parent unique seq*/>
+            mChildToRelativeParent;
+    std::unordered_map<uint32_t /* child unique seq*/, uint32_t /* parent unique seq*/>
+            mChildToParent;
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 03a7f22..1b8ff28 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -39,9 +39,12 @@
 
 LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
                                  ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
-                                 bool allowSecureLayers)
+                                 bool allowSecureLayers, const ui::Transform& layerTransform,
+                                 const Rect& layerBufferSize)
       : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, allowSecureLayers),
         mLayer(std::move(layer)),
+        mLayerTransform(layerTransform),
+        mLayerBufferSize(layerBufferSize),
         mCrop(crop),
         mFlinger(flinger),
         mChildrenOnly(childrenOnly) {}
@@ -60,7 +63,8 @@
 
 Rect LayerRenderArea::getSourceCrop() const {
     if (mCrop.isEmpty()) {
-        return mLayer->getBufferSize(mLayer->getDrawingState());
+        // TODO this should probably be mBounds instead of just buffer bounds
+        return mLayerBufferSize;
     } else {
         return mCrop;
     }
@@ -70,7 +74,7 @@
     using namespace std::string_literals;
 
     if (!mChildrenOnly) {
-        mTransform = mLayer->getTransform().inverse();
+        mTransform = mLayerTransform.inverse();
     }
 
     if (mFlinger.mLayerLifecycleManagerEnabled) {
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 322dbd1..9bb13b3 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -33,7 +33,8 @@
 class LayerRenderArea : public RenderArea {
 public:
     LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
-                    ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers);
+                    ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
+                    const ui::Transform& layerTransform, const Rect& layerBufferSize);
 
     const ui::Transform& getTransform() const override;
     bool isSecure() const override;
@@ -45,6 +46,8 @@
 
 private:
     const sp<Layer> mLayer;
+    const ui::Transform mLayerTransform;
+    const Rect mLayerBufferSize;
     const Rect mCrop;
 
     ui::Transform mTransform;
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/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 5bd8a99..d5d8688 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -60,7 +60,4 @@
         "libgtest",
         "libscheduler",
     ],
-    sanitize: {
-        address: true,
-    },
 }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index f6fe468..eec7c08 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -308,7 +308,7 @@
         // significantly faster than the display rate, at it would cause a significant frame drop.
         // It is more appropriate to choose a higher display rate even if
         // a pull-down will be required.
-        constexpr float kMinMultiplier = 0.25f;
+        constexpr float kMinMultiplier = 0.75f;
         if (multiplier >= kMinMultiplier &&
             isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
             return kScoreForFractionalPairs;
@@ -958,7 +958,7 @@
         }
 
         const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
-        const auto id = frameRateMode.modePtr->getId();
+        const auto id = modePtr->getId();
         if (ascending && frameRateMode.fps < *maxRenderRateForMode.get(id)) {
             // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
             //  use a lower frame rate when we want Ascending frame rates.
@@ -970,14 +970,20 @@
         if (ascending) {
             score = 1.0f / score;
         }
+
+        constexpr float kScore = std::numeric_limits<float>::max();
         if (preferredDisplayModeOpt) {
             if (*preferredDisplayModeOpt == modePtr->getId()) {
-                constexpr float kScore = std::numeric_limits<float>::max();
                 ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
                 return;
             }
             constexpr float kNonPreferredModePenalty = 0.95f;
             score *= kNonPreferredModePenalty;
+        } else if (ascending && id == getMinRefreshRateByPolicyLocked()->getId()) {
+            // TODO(b/266481656): Once this bug is fixed, we can remove this workaround
+            //  and actually use a lower frame rate when we want Ascending frame rates.
+            ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
+            return;
         }
 
         ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f18dfdc..7f8d394 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -109,7 +109,6 @@
 void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
     demotePacesetterDisplay();
 
-    std::scoped_lock lock(mDisplayLock);
     promotePacesetterDisplay(pacesetterIdOpt);
 }
 
@@ -123,26 +122,34 @@
                                         std::shared_ptr<VsyncSchedule> vsyncSchedule) {
     demotePacesetterDisplay();
 
-    std::scoped_lock lock(mDisplayLock);
-    mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
-    mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
+    {
+        std::scoped_lock lock(mDisplayLock);
+        mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+        mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
 
-    promotePacesetterDisplay();
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
+    }
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
     demotePacesetterDisplay();
 
-    std::scoped_lock lock(mDisplayLock);
-    mRefreshRateSelectors.erase(displayId);
-    mVsyncSchedules.erase(displayId);
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
+    {
+        std::scoped_lock lock(mDisplayLock);
+        mRefreshRateSelectors.erase(displayId);
+        mVsyncSchedules.erase(displayId);
 
-    // Do not allow removing the final display. Code in the scheduler expects
-    // there to be at least one display. (This may be relaxed in the future with
-    // headless virtual display.)
-    LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
+        // Do not allow removing the final display. Code in the scheduler expects
+        // there to be at least one display. (This may be relaxed in the future with
+        // headless virtual display.)
+        LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
 
-    promotePacesetterDisplay();
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
+    }
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 void Scheduler::run() {
@@ -678,6 +685,18 @@
 }
 
 void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
+
+    {
+        std::scoped_lock lock(mDisplayLock);
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
+    }
+
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
+}
+
+std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
+        std::optional<PhysicalDisplayId> pacesetterIdOpt) {
     // TODO(b/241286431): Choose the pacesetter display.
     mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
     ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
@@ -697,14 +716,22 @@
         vsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
                                              true /* force */);
     }
+    return vsyncSchedule;
+}
 
+void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) {
     onNewVsyncSchedule(vsyncSchedule->getDispatch());
+    std::vector<android::EventThread*> threads;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
+        threads.reserve(mConnections.size());
         for (auto& [_, connection] : mConnections) {
-            connection.thread->onNewVsyncSchedule(vsyncSchedule);
+            threads.push_back(connection.thread.get());
         }
     }
+    for (auto* thread : threads) {
+        thread->onNewVsyncSchedule(vsyncSchedule);
+    }
 }
 
 void Scheduler::demotePacesetterDisplay() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 62a5fb2..3423652 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -165,7 +165,7 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
+    VsyncModulator& vsyncModulator() { return *mVsyncModulator; }
 
     // In some cases, we should only modulate for the pacesetter display. In those
     // cases, the caller should pass in the relevant display, and the method
@@ -321,7 +321,18 @@
     // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
     // The new `mPacesetterDisplayId` is never `std::nullopt`.
     void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+    // Changes to the displays (e.g. registering and unregistering) must be made
+    // while mDisplayLock is locked, and the new pacesetter then must be promoted while
+    // mDisplayLock is still locked. However, a new pacesetter means that
+    // MessageQueue and EventThread need to use the new pacesetter's
+    // VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
+    // or else we may deadlock with EventThread.
+    std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
+            std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
             REQUIRES(kMainThreadContext, mDisplayLock);
+    void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
 
     // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
     // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index c9af4c2..586357f 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -187,9 +187,9 @@
     static_cast<void>(updateVsyncConfigLocked());
 }
 
-bool VsyncModulator::isVsyncConfigDefault() const {
+bool VsyncModulator::isVsyncConfigEarly() const {
     std::lock_guard<std::mutex> lock(mMutex);
-    return getNextVsyncConfigType() == VsyncConfigType::Late;
+    return getNextVsyncConfigType() != VsyncConfigType::Late;
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index dc4dafd..be0d334 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -53,8 +53,12 @@
 
     explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
 
+    bool isVsyncConfigEarly() const EXCLUDES(mMutex);
+
     VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
 
+    void cancelRefreshRateChange() { mRefreshRateChangePending = false; }
+
     [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
 
     // Changes offsets in response to transaction flags or commit.
@@ -72,8 +76,6 @@
 
     [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
 
-    [[nodiscard]] bool isVsyncConfigDefault() const;
-
 protected:
     // Called from unit tests as well
     void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3c5a3de..a271083 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -105,6 +105,7 @@
 #include <memory>
 #include <mutex>
 #include <optional>
+#include <string>
 #include <type_traits>
 #include <unordered_map>
 #include <vector>
@@ -130,6 +131,7 @@
 #include "FrameTracer/FrameTracer.h"
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerLifecycleManager.h"
 #include "FrontEnd/LayerSnapshot.h"
 #include "HdrLayerInfoReporter.h"
 #include "Layer.h"
@@ -292,6 +294,13 @@
             displayHdrCapabilities.getDesiredMinLuminance()};
 }
 
+uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
+    if (!surfaceControl) {
+        return UNASSIGNED_LAYER_ID;
+    }
+    return LayerHandle::getLayerId(surfaceControl->getHandle());
+}
+
 }  // namespace anonymous
 
 // ---------------------------------------------------------------------------
@@ -497,14 +506,13 @@
     // the window manager died on us. prepare its eulogy.
     mBootFinished = false;
 
-    // Sever the link to inputflinger since it's gone as well.
-    static_cast<void>(mScheduler->schedule(
-            [this] { mInputFlinger.clear(); }));
+    static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
+        // Sever the link to inputflinger since it's gone as well.
+        mInputFlinger.clear();
 
-    // restore initial conditions (default device unblank, etc)
-    initializeDisplays();
+        initializeDisplays();
+    }));
 
-    // restart the boot-animation
     startBootAnim();
 }
 
@@ -873,7 +881,9 @@
     mDrawingState = mCurrentState;
 
     onActiveDisplayChangedLocked(nullptr, *display);
-    initializeDisplays();
+
+    static_cast<void>(mScheduler->schedule(
+            [this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); }));
 
     mPowerAdvisor->init();
 
@@ -2175,7 +2185,7 @@
     }
 }
 
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
                                                 bool transactionsFlushed,
                                                 bool& outTransactionsAreEmpty) {
     bool needsTraversal = false;
@@ -2227,7 +2237,7 @@
     }
 }
 
-bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update,
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
                                           bool transactionsFlushed, bool& outTransactionsAreEmpty) {
     using Changes = frontend::RequestedLayerState::Changes;
     ATRACE_NAME("updateLayerSnapshots");
@@ -2477,9 +2487,14 @@
                                     Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
         const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
-        LifecycleUpdate updates;
+        frontend::Update updates;
         if (flushTransactions) {
             updates = flushLifecycleUpdates();
+            if (mTransactionTracing) {
+                mTransactionTracing->addCommittedTransactions(vsyncId.value, frameTime.ns(),
+                                                              updates, mFrontEndDisplayInfos,
+                                                              mFrontEndDisplayInfosChanged);
+            }
         }
         bool transactionsAreEmpty;
         if (mLegacyFrontEndEnabled) {
@@ -2519,7 +2534,7 @@
     updateCursorAsync();
     if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and tracing should only be enabled for debugging.
-        mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
     }
     mLastCommittedVsyncId = vsyncId;
 
@@ -2546,6 +2561,7 @@
         if (!dropFrame) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
+        display->tracePowerMode();
         displayIds.push_back(display->getId());
     }
     mPowerAdvisor->setDisplays(displayIds);
@@ -2624,7 +2640,9 @@
         CompositionResult compositionResult{layerFE->stealCompositionResult()};
         layer->onPreComposition(compositionResult.refreshStartTime);
         for (auto releaseFence : compositionResult.releaseFences) {
-            layer->onLayerDisplayed(releaseFence);
+            Layer* clonedFrom = layer->getClonedFrom().get();
+            auto owningLayer = clonedFrom ? clonedFrom : layer;
+            owningLayer->onLayerDisplayed(releaseFence);
         }
         if (compositionResult.lastClientCompositionFence) {
             layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
@@ -2690,7 +2708,7 @@
     mLayersWithQueuedFrames.clear();
     if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and should only be used for debugging.
-        mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
     }
 
     if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2743,7 +2761,7 @@
     // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
     if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
                 (int32_t)Dataspace::RANGE_EXTENDED &&
-        snapshot.desiredSdrHdrRatio > 1.01f) {
+        snapshot.desiredHdrSdrRatio > 1.01f) {
         return true;
     }
     return false;
@@ -2885,7 +2903,10 @@
                         const auto* outputLayer =
                             compositionDisplay->getOutputLayerForLayer(layerFe);
                         if (outputLayer) {
-                            info.mergeDesiredRatio(snapshot.desiredSdrHdrRatio);
+                            const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
+                                    ? std::numeric_limits<float>::infinity()
+                                    : snapshot.desiredHdrSdrRatio;
+                            info.mergeDesiredRatio(desiredHdrSdrRatio);
                             info.numberOfHdrLayers++;
                             const auto displayFrame = outputLayer->getState().displayFrame;
                             const int32_t area = displayFrame.width() * displayFrame.height();
@@ -3484,7 +3505,7 @@
 
             // TODO(b/175678251) Call a listener instead.
             if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
-                updateActiveDisplayVsyncLocked(*display);
+                resetPhaseConfiguration(display->getActiveMode().fps);
             }
         }
         return;
@@ -3518,9 +3539,11 @@
     }
 }
 
-void SurfaceFlinger::updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay) {
+void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) {
+    // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+    mScheduler->vsyncModulator().cancelRefreshRateChange();
+
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay.getActiveMode().fps;
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3699,8 +3722,11 @@
         ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
         if (updateWindowInfo) {
             mWindowInfosListenerInvoker
-                    ->windowInfosChanged(windowInfos, displayInfos,
-                                         inputWindowCommands.windowInfosReportedListeners);
+                    ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
+                                         std::move(
+                                                 inputWindowCommands.windowInfosReportedListeners),
+                                         /* forceImmediateCall= */
+                                         !inputWindowCommands.focusRequests.empty());
         } else {
             // If there are listeners but no changes to input windows, call the listeners
             // immediately.
@@ -4060,15 +4086,41 @@
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
-status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
                                         const sp<Layer>& layer, const wp<Layer>& parent,
                                         uint32_t* outTransformHint) {
     if (mNumLayers >= MAX_LAYERS) {
         ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
               MAX_LAYERS);
         static_cast<void>(mScheduler->schedule([=] {
+            ALOGE("Dumping layer keeping > 20 children alive:");
+            bool leakingParentLayerFound = false;
+            mDrawingState.traverse([&](Layer* layer) {
+                if (leakingParentLayerFound) {
+                    return;
+                }
+                if (layer->getChildrenCount() > 20) {
+                    leakingParentLayerFound = true;
+                    sp<Layer> parent = sp<Layer>::fromExisting(layer);
+                    while (parent) {
+                        ALOGE("Parent Layer: %s handleIsAlive: %s", parent->getName().c_str(),
+                              std::to_string(parent->isHandleAlive()).c_str());
+                        parent = parent->getParent();
+                    }
+                    // Sample up to 100 layers
+                    ALOGE("Dumping random sampling of child layers total(%zu): ",
+                          layer->getChildrenCount());
+                    int sampleSize = (layer->getChildrenCount() / 100) + 1;
+                    layer->traverseChildren([&](Layer* layer) {
+                        if (rand() % sampleSize == 0) {
+                            ALOGE("Child Layer: %s", layer->getName().c_str());
+                        }
+                    });
+                }
+            });
+
             ALOGE("Dumping random sampling of on-screen layers: ");
-            mDrawingState.traverse([&](Layer *layer) {
+            mDrawingState.traverse([&](Layer* layer) {
                 // Aim to dump about 200 layers to avoid totally trashing
                 // logcat. On the other hand, if there really are 4096 layers
                 // something has gone totally wrong its probably the most
@@ -4077,6 +4129,8 @@
                     ALOGE("Layer: %s", layer->getName().c_str());
                 }
             });
+            ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
+                  mOffscreenLayers.size());
             for (Layer* offscreenLayer : mOffscreenLayers) {
                 if (rand() % 20 == 13) {
                     ALOGE("Offscreen-layer: %s", offscreenLayer->getName().c_str());
@@ -4090,11 +4144,15 @@
     if (outTransformHint) {
         *outTransformHint = mActiveDisplayTransformHint;
     }
-
+    args.parentId = LayerHandle::getLayerId(args.parentHandle.promote());
+    args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
     {
         std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
         mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
         mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
+        args.mirrorLayerHandle.clear();
+        args.parentHandle.clear();
+        mNewLayerArgs.emplace_back(std::move(args));
     }
 
     setTransactionFlags(eTransactionNeeded);
@@ -4260,7 +4318,7 @@
 
 bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
                                        VsyncId vsyncId) {
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
     return applyTransactionsLocked(transactions, vsyncId);
 }
 
@@ -4279,10 +4337,6 @@
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
     }
-
-    if (mTransactionTracing) {
-        mTransactionTracing->addCommittedTransactions(transactions, vsyncId.value);
-    }
     return needsTraversal;
 }
 
@@ -4341,9 +4395,8 @@
         // We don't want to latch unsignaled if are in early / client composition
         // as it leads to jank due to RenderEngine waiting for unsignaled buffer
         // or window animations being slow.
-        const auto isDefaultVsyncConfig = mScheduler->vsyncModulator().isVsyncConfigDefault();
-        if (!isDefaultVsyncConfig) {
-            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
+        if (mScheduler->vsyncModulator().isVsyncConfigEarly()) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; isVsyncConfigEarly)",
                   __func__);
             return false;
         }
@@ -4422,6 +4475,21 @@
                                                      layerName.c_str(), transactionId);
             mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
         }
+        resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
+        if (resolvedState.state.what & layer_state_t::eReparent) {
+            resolvedState.parentId =
+                    getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+        }
+        if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
+            resolvedState.relativeParentId =
+                    getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+        }
+        if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
+            wp<IBinder>& touchableRegionCropHandle =
+                    resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+            resolvedState.touchCropId =
+                    LayerHandle::getLayerId(touchableRegionCropHandle.promote());
+        }
     }
 
     TransactionState state{frameTimelineInfo,
@@ -4546,7 +4614,7 @@
 
 bool SurfaceFlinger::applyAndCommitDisplayTransactionStates(
         std::vector<TransactionState>& transactions) {
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
     bool needsTraversal = false;
     uint32_t transactionFlags = 0;
     for (auto& transaction : transactions) {
@@ -4891,7 +4959,7 @@
         if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
-        if (layer->setExtendedRangeBrightness(s.currentSdrHdrRatio, s.desiredSdrHdrRatio)) {
+        if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -5066,6 +5134,12 @@
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eBufferChanged) {
+        std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
+        frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
+        if (snapshot) {
+            transformHint = snapshot->transformHint;
+        }
+        layer->setTransformHint(transformHint);
         if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
                              desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
                              frameTimelineInfo)) {
@@ -5110,7 +5184,7 @@
 
     sp<Layer> mirrorLayer;
     sp<Layer> mirrorFrom;
-    LayerCreationArgs mirrorArgs(args);
+    LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
     {
         Mutex::Autolock _l(mStateLock);
         mirrorFrom = LayerHandle::getLayer(mirrorFromHandle);
@@ -5130,11 +5204,6 @@
 
     outResult.layerId = mirrorLayer->sequence;
     outResult.layerName = String16(mirrorLayer->getDebugName());
-    if (mTransactionTracing) {
-        mTransactionTracing->onMirrorLayerAdded(outResult.handle->localBinder(),
-                                                mirrorLayer->sequence, args.name,
-                                                mirrorFrom->sequence);
-    }
     return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */,
                           nullptr /* parent */, nullptr /* outTransformHint */);
 }
@@ -5161,7 +5230,7 @@
         }
 
         layerStack = display->getLayerStack();
-        LayerCreationArgs mirrorArgs(args);
+        LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
         mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
         mirrorArgs.addToRoot = true;
         mirrorArgs.layerStackToMirror = layerStack;
@@ -5176,11 +5245,6 @@
         return result;
     }
 
-    if (mTransactionTracing) {
-        mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), outResult.layerId,
-                                          args.name, args.flags, -1 /* parentId */);
-    }
-
     if (mLegacyFrontEndEnabled) {
         std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
         mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
@@ -5228,12 +5292,6 @@
         args.addToRoot = false;
     }
 
-    const int parentId = parent ? parent->getSequence() : -1;
-    if (mTransactionTracing) {
-        mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence,
-                                          args.name, args.flags, parentId);
-    }
-
     uint32_t outTransformHint;
     result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
     if (result != NO_ERROR) {
@@ -5275,17 +5333,15 @@
 
     Mutex::Autolock lock(mStateLock);
     markLayerPendingRemovalLocked(layer);
+    layer->onHandleDestroyed();
     mBufferCountTracker.remove(handle);
     layer.clear();
-    if (mTransactionTracing) {
-        mTransactionTracing->onHandleRemoved(handle);
-    }
 
     setTransactionFlags(eTransactionFlushNeeded);
 }
 
-void SurfaceFlinger::onInitializeDisplays() {
-    const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::initializeDisplays() {
+    const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
     if (!display) return;
 
     const sp<IBinder> token = display->getDisplayToken().promote();
@@ -5293,13 +5349,13 @@
 
     TransactionState state;
     state.inputWindowCommands = mInputWindowCommands;
-    nsecs_t now = systemTime();
+    const nsecs_t now = systemTime();
     state.desiredPresentTime = now;
     state.postTime = now;
     state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER;
     state.originPid = mPid;
     state.originUid = static_cast<int>(getuid());
-    uint64_t transactionId = (((uint64_t)mPid) << 32) | mUniqueTransactionId++;
+    const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++;
     state.id = transactionId;
 
     // reset screen orientation and use primary layer stack
@@ -5319,21 +5375,16 @@
     std::vector<TransactionState> transactions;
     transactions.emplace_back(state);
 
-    // It should be on the main thread, apply it directly.
     if (mLegacyFrontEndEnabled) {
-        applyTransactionsLocked(transactions, /*vsyncId=*/{0});
+        applyTransactions(transactions, VsyncId{0});
     } else {
         applyAndCommitDisplayTransactionStates(transactions);
     }
 
-    setPowerModeInternal(display, hal::PowerMode::ON);
-}
-
-void SurfaceFlinger::initializeDisplays() {
-    // Async since we may be called from the main thread.
-    static_cast<void>(mScheduler->schedule(
-            [this]() FTL_FAKE_GUARD(mStateLock)
-                    FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); }));
+    {
+        ftl::FakeGuard guard(mStateLock);
+        setPowerModeInternal(display, hal::PowerMode::ON);
+    }
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -5535,7 +5586,8 @@
             LayersTraceProto* layersTrace = traceFileProto.add_entry();
             LayersProto layersProto = dumpProtoFromMainThread();
             layersTrace->mutable_layers()->Swap(&layersProto);
-            dumpDisplayProto(*layersTrace);
+            auto displayProtos = dumpDisplayProto();
+            layersTrace->mutable_displays()->Swap(&displayProtos);
 
             if (asProto) {
                 result.append(traceFileProto.SerializeAsString());
@@ -5774,22 +5826,15 @@
         return layersProto;
     }
 
-    const frontend::LayerHierarchy& root = mLayerHierarchyBuilder.getHierarchy();
-    LayersProto layersProto;
-    for (auto& [child, variant] : root.mChildren) {
-        if (variant != frontend::LayerHierarchy::Variant::Attached ||
-            stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
-            continue;
-        }
-        LayerProtoHelper::writeHierarchyToProto(layersProto, *child, mLayerSnapshotBuilder,
-                                                mLegacyLayers, traceFlags);
-    }
-    return layersProto;
+    return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, {},
+                                           traceFlags)
+            .generate(mLayerHierarchyBuilder.getHierarchy());
 }
 
-void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const {
+google::protobuf::RepeatedPtrField<DisplayProto> SurfaceFlinger::dumpDisplayProto() const {
+    google::protobuf::RepeatedPtrField<DisplayProto> displays;
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
-        DisplayProto* displayProto = layersTraceProto.add_displays();
+        DisplayProto* displayProto = displays.Add();
         displayProto->set_id(display->getId().value);
         displayProto->set_name(display->getDisplayName());
         displayProto->set_layer_stack(display->getLayerStack().id);
@@ -5802,6 +5847,7 @@
                                                 displayProto->mutable_transform());
         displayProto->set_is_virtual(display->isVirtual());
     }
+    return displays;
 }
 
 void SurfaceFlinger::dumpHwc(std::string& result) const {
@@ -6332,9 +6378,10 @@
                         int64_t startingTime =
                                 (fixedStartingTime) ? fixedStartingTime : systemTime();
                         mScheduler
-                                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) {
-                                    mLayerTracing.notify(true /* visibleRegionDirty */,
-                                                         startingTime, mLastCommittedVsyncId.value);
+                                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+                                                   kMainThreadContext) {
+                                    addToLayerTracing(true /* visibleRegionDirty */, startingTime,
+                                                      mLastCommittedVsyncId.value);
                                 })
                                 .wait();
                     }
@@ -7007,13 +7054,34 @@
 
     bool childrenOnly = args.childrenOnly;
     RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
+        ui::Transform layerTransform;
+        Rect layerBufferSize;
+        if (mLayerLifecycleManagerEnabled) {
+            frontend::LayerSnapshot* snapshot =
+                    mLayerSnapshotBuilder.getSnapshot(parent->getSequence());
+            if (!snapshot) {
+                ALOGW("Couldn't find layer snapshot for %d", parent->getSequence());
+            } else {
+                layerTransform = snapshot->localTransform;
+                layerBufferSize = snapshot->bufferSize;
+            }
+        } else {
+            layerTransform = parent->getTransform();
+            layerBufferSize = parent->getBufferSize(parent->getDrawingState());
+        }
+
         return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
-                                                 childrenOnly, args.captureSecureLayers);
+                                                 childrenOnly, args.captureSecureLayers,
+                                                 layerTransform, layerBufferSize);
     });
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
-        FloatRect parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
-                                              : crop.toFloatRect();
+        std::optional<FloatRect> parentCrop = std::nullopt;
+        if (args.childrenOnly) {
+            parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
+                                        : crop.toFloatRect();
+        }
+
         getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid,
                                                             std::move(excludeLayerIds),
                                                             args.childrenOnly, parentCrop);
@@ -7697,10 +7765,6 @@
     if (hintDisplay) {
         layer->updateTransformHint(hintDisplay->getTransformHint());
     }
-
-    if (mTransactionTracing) {
-        mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
-    }
 }
 
 void SurfaceFlinger::sample() {
@@ -7720,14 +7784,20 @@
                                                   const DisplayDevice& activeDisplay) {
     ATRACE_CALL();
 
+    // For the first display activated during boot, there is no need to force setDesiredActiveMode,
+    // because DM is about to send its policy via setDesiredDisplayModeSpecs.
+    bool forceApplyPolicy = false;
+
     if (inactiveDisplayPtr) {
         inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+        forceApplyPolicy = true;
     }
 
     mActiveDisplayId = activeDisplay.getPhysicalId();
     activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
 
-    updateActiveDisplayVsyncLocked(activeDisplay);
+    resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+
     mScheduler->setModeChangePending(false);
     mScheduler->setPacesetterDisplay(mActiveDisplayId);
 
@@ -7738,8 +7808,8 @@
     // 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.
-    constexpr bool kForce = true;
-    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce);
+    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(),
+                                   forceApplyPolicy);
 }
 
 status_t SurfaceFlinger::addWindowInfosListener(
@@ -7835,10 +7905,6 @@
             sp<Layer> childMirror;
             createEffectLayer(mirrorArgs, &unused, &childMirror);
             childMirror->setClonedChild(layer->createClone());
-            if (mTransactionTracing) {
-                mTransactionTracing->onLayerAddedToDrawingState(childMirror->getSequence(),
-                                                                vsyncId.value);
-            }
             childMirror->reparent(mirrorDisplay.rootHandle);
         }
     }
@@ -7965,7 +8031,7 @@
                     if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
                         return;
                     }
-                    if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
+                    if (uid != CaptureArgs::UNSET_UID && snapshot->uid != uid) {
                         return;
                     }
                     if (!snapshot->hasSomethingToDraw()) {
@@ -7992,7 +8058,8 @@
 std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
 SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid,
                                                 std::unordered_set<uint32_t> excludeLayerIds,
-                                                bool childrenOnly, const FloatRect& parentCrop) {
+                                                bool childrenOnly,
+                                                const std::optional<FloatRect>& parentCrop) {
     return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
             parentCrop]() {
         auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
@@ -8005,7 +8072,7 @@
                      .globalShadowSettings = mDrawingState.globalShadowSettings,
                      .supportsBlur = mSupportsBlur,
                      .forceFullDamage = mForceFullDamage,
-                     .parentCrop = {parentCrop},
+                     .parentCrop = parentCrop,
                      .excludeLayerIds = std::move(excludeLayerIds),
                      .supportedLayerGenericMetadata =
                              getHwComposer().getSupportedLayerGenericMetadata(),
@@ -8023,8 +8090,8 @@
     };
 }
 
-SurfaceFlinger::LifecycleUpdate SurfaceFlinger::flushLifecycleUpdates() {
-    LifecycleUpdate update;
+frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
+    frontend::Update update;
     ATRACE_NAME("TransactionHandler:flushTransactions");
     // Locking:
     // 1. to prevent onHandleDestroyed from being called while the state lock is held,
@@ -8041,12 +8108,28 @@
         mCreatedLayers.clear();
         update.newLayers = std::move(mNewLayers);
         mNewLayers.clear();
+        update.layerCreationArgs = std::move(mNewLayerArgs);
+        mNewLayerArgs.clear();
         update.destroyedHandles = std::move(mDestroyedHandles);
         mDestroyedHandles.clear();
     }
     return update;
 }
 
+void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+    const uint32_t tracingFlags = mLayerTracing.getFlags();
+    LayersProto layers(dumpDrawingStateProto(tracingFlags));
+    if (tracingFlags & LayerTracing::TRACE_EXTRA) {
+        dumpOffscreenLayersProto(layers);
+    }
+    std::string hwcDump;
+    if (tracingFlags & LayerTracing::TRACE_HWC) {
+        dumpHwc(hwcDump);
+    }
+    auto displays = dumpDisplayProto();
+    mLayerTracing.notify(visibleRegionDirty, time, vsyncId, &layers, std::move(hwcDump), &displays);
+}
+
 // gui::ISurfaceComposer
 
 binder::Status SurfaceComposerAIDL::bootFinished() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 338531f..74d00dd 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -455,26 +455,6 @@
         FINISHED,
     };
 
-    struct LayerCreatedState {
-        LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
-              : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
-        wp<Layer> layer;
-        // Indicates the initial parent of the created layer, only used for creating layer in
-        // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
-        wp<Layer> initialParent;
-        // Indicates whether the layer getting created should be added at root if there's no parent
-        // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
-        // be added offscreen.
-        bool addToRoot;
-    };
-
-    struct LifecycleUpdate {
-        std::vector<TransactionState> transactions;
-        std::vector<LayerCreatedState> layerCreatedStates;
-        std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
-        std::vector<uint32_t> destroyedHandles;
-    };
-
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
     static Dumper dumper(F&& dump) {
         using namespace std::placeholders;
@@ -721,13 +701,13 @@
             int64_t vsyncId);
     void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
                                           std::vector<std::pair<Layer*, LayerFE*>>& layers);
-    bool updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+    bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
                                     bool transactionsFlushed, bool& out)
             REQUIRES(kMainThreadContext);
-    bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed,
+    bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
                               bool& out) REQUIRES(kMainThreadContext);
     void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
-    LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext);
+    frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
 
     void updateInputFlinger();
     void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -737,6 +717,8 @@
     void updateCursorAsync();
 
     void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
+
+    void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext);
     void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
 
     /*
@@ -818,7 +800,7 @@
     void markLayerPendingRemovalLocked(const sp<Layer>& layer);
 
     // add a layer to SurfaceFlinger
-    status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+    status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
                             const sp<Layer>& layer, const wp<Layer>& parentLayer,
                             uint32_t* outTransformHint);
 
@@ -855,8 +837,7 @@
      */
 
     // Called during boot, and restart after system_server death.
-    void initializeDisplays();
-    void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
+    void initializeDisplays() REQUIRES(kMainThreadContext);
 
     sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
             REQUIRES(mStateLock) {
@@ -1085,7 +1066,9 @@
     LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
-    void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
+    google::protobuf::RepeatedPtrField<DisplayProto> dumpDisplayProto() const;
+    void addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId)
+            REQUIRES(kMainThreadContext);
 
     // Dumps state from HW Composer
     void dumpHwc(std::string& result) const;
@@ -1121,9 +1104,6 @@
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
-    void updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay)
-            REQUIRES(mStateLock, kMainThreadContext);
-
     bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
 
     ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
@@ -1242,7 +1222,7 @@
     bool mLayerCachingEnabled = false;
     bool mBackpressureGpuComposition = false;
 
-    LayerTracing mLayerTracing{*this};
+    LayerTracing mLayerTracing;
     bool mLayerTracingEnabled = false;
 
     std::optional<TransactionTracing> mTransactionTracing;
@@ -1402,7 +1382,7 @@
                     snapshotFilterFn);
     std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
             uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
-            bool childrenOnly, const FloatRect& parentCrop);
+            bool childrenOnly, const std::optional<FloatRect>& optionalParentCrop);
 
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 
@@ -1424,6 +1404,7 @@
 
     std::vector<uint32_t> mDestroyedHandles;
     std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
+    std::vector<LayerCreationArgs> mNewLayerArgs;
     // These classes do not store any client state but help with managing transaction callbacks
     // and stats.
     std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
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/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 558b3be..1adc3a5 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -24,16 +24,24 @@
 #include <cutils/compiler.h>
 #include <utils/Trace.h>
 
-namespace std {
-template <class Rep, class Period>
-bool signbit(std::chrono::duration<Rep, Period> v) {
-    return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
-}
-} // namespace std
-
 namespace android {
 
 namespace {
+template <class Rep, class Period>
+bool signbit(std::chrono::duration<Rep, Period> v) {
+    return std::signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
+}
+
+template <typename Enum, typename std::enable_if<std::is_enum<Enum>::value>::type* = nullptr>
+bool signbit(Enum e) {
+    return std::signbit(static_cast<typename std::underlying_type<Enum>::type>(e));
+}
+
+template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
+bool signbit(T t) {
+    return std::signbit(t);
+}
+
 template <typename T>
 int64_t to_int64(T v) {
     return int64_t(v);
@@ -49,14 +57,12 @@
 class TracedOrdinal {
 public:
     static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
-                          std::is_same<std::chrono::nanoseconds, T>(),
+                          std::is_same<std::chrono::nanoseconds, T>() || std::is_enum<T>(),
                   "Type is not supported. Please test it with systrace before adding "
                   "it to the list.");
 
     TracedOrdinal(std::string name, T initialValue)
-          : mName(std::move(name)),
-            mHasGoneNegative(std::signbit(initialValue)),
-            mData(initialValue) {
+          : mName(std::move(name)), mHasGoneNegative(signbit(initialValue)), mData(initialValue) {
         trace();
     }
 
@@ -66,7 +72,7 @@
 
     TracedOrdinal& operator=(T other) {
         mData = other;
-        mHasGoneNegative = mHasGoneNegative || std::signbit(mData);
+        mHasGoneNegative = mHasGoneNegative || signbit(mData);
         trace();
         return *this;
     }
@@ -81,7 +87,7 @@
             mNameNegative = mName + "Negative";
         }
 
-        if (!std::signbit(mData)) {
+        if (!signbit(mData)) {
             ATRACE_INT64(mName.c_str(), to_int64(mData));
             if (mHasGoneNegative) {
                 ATRACE_INT64(mNameNegative.c_str(), 0);
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 566d553..ecdeabe 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "LayerTracing"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <filesystem>
+
 #include <SurfaceFlinger.h>
 #include <android-base/stringprintf.h>
 #include <log/log.h>
@@ -29,9 +31,8 @@
 
 namespace android {
 
-LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
-    mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
-}
+LayerTracing::LayerTracing()
+      : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {}
 
 LayerTracing::~LayerTracing() = default;
 
@@ -45,30 +46,39 @@
     return true;
 }
 
-bool LayerTracing::disable(std::string filename) {
+bool LayerTracing::disable(std::string filename, bool writeToFile) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return false;
     }
     mEnabled = false;
-    LayersTraceFileProto fileProto = createTraceFileProto();
-    mBuffer->writeToFile(fileProto, filename);
+    if (writeToFile) {
+        LayersTraceFileProto fileProto = createTraceFileProto();
+        mBuffer->writeToFile(fileProto, filename);
+    }
     mBuffer->reset();
     return true;
 }
 
+void LayerTracing::appendToStream(std::ofstream& out) {
+    std::scoped_lock lock(mTraceLock);
+    LayersTraceFileProto fileProto = createTraceFileProto();
+    mBuffer->appendToStream(fileProto, out);
+    mBuffer->reset();
+}
+
 bool LayerTracing::isEnabled() const {
     std::scoped_lock lock(mTraceLock);
     return mEnabled;
 }
 
-status_t LayerTracing::writeToFile() {
+status_t LayerTracing::writeToFile(std::string filename) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return STATUS_OK;
     }
     LayersTraceFileProto fileProto = createTraceFileProto();
-    return mBuffer->writeToFile(fileProto, FILE_NAME);
+    return mBuffer->writeToFile(fileProto, filename);
 }
 
 void LayerTracing::setTraceFlags(uint32_t flags) {
@@ -84,8 +94,11 @@
 bool LayerTracing::flagIsSet(uint32_t flags) const {
     return (mFlags & flags) == flags;
 }
+uint32_t LayerTracing::getFlags() const {
+    return mFlags;
+}
 
-LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+LayersTraceFileProto LayerTracing::createTraceFileProto() {
     LayersTraceFileProto fileProto;
     fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
                                LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
@@ -101,7 +114,9 @@
     mBuffer->dump(result);
 }
 
-void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId,
+                          LayersProto* layers, std::string hwcDump,
+                          google::protobuf::RepeatedPtrField<DisplayProto>* displays) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return;
@@ -116,22 +131,15 @@
     entry.set_elapsed_realtime_nanos(time);
     const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched";
     entry.set_where(where);
-    LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
-
-    if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
-        mFlinger.dumpOffscreenLayersProto(layers);
-    }
-    entry.mutable_layers()->Swap(&layers);
+    entry.mutable_layers()->Swap(layers);
 
     if (flagIsSet(LayerTracing::TRACE_HWC)) {
-        std::string hwcDump;
-        mFlinger.dumpHwc(hwcDump);
         entry.set_hwc_blob(hwcDump);
     }
     if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         entry.set_excludes_composition_state(true);
     }
-    mFlinger.dumpDisplayProto(entry);
+    entry.mutable_displays()->Swap(displays);
     entry.set_vsync_id(vsyncId);
     mBuffer->emplace(std::move(entry));
 }
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index b32001c..40b0fbe 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -40,14 +40,16 @@
  */
 class LayerTracing {
 public:
-    LayerTracing(SurfaceFlinger& flinger);
+    LayerTracing();
     ~LayerTracing();
     bool enable();
-    bool disable(std::string filename = FILE_NAME);
+    bool disable(std::string filename = FILE_NAME, bool writeToFile = true);
+    void appendToStream(std::ofstream& out);
     bool isEnabled() const;
-    status_t writeToFile();
-    LayersTraceFileProto createTraceFileProto() const;
-    void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId);
+    status_t writeToFile(std::string filename = FILE_NAME);
+    static LayersTraceFileProto createTraceFileProto();
+    void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId, LayersProto* layers,
+                std::string hwcDump, google::protobuf::RepeatedPtrField<DisplayProto>* displays);
 
     enum : uint32_t {
         TRACE_INPUT = 1 << 1,
@@ -60,13 +62,12 @@
     };
     void setTraceFlags(uint32_t flags);
     bool flagIsSet(uint32_t flags) const;
+    uint32_t getFlags() const;
     void setBufferSize(size_t bufferSizeInBytes);
     void dump(std::string&) const;
 
 private:
     static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
-    SurfaceFlinger& mFlinger;
     uint32_t mFlags = TRACE_INPUT;
     mutable std::mutex mTraceLock;
     bool mEnabled GUARDED_BY(mTraceLock) = false;
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index 7e38c55..b41c65b 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -24,6 +24,7 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 #include <chrono>
+#include <fstream>
 #include <queue>
 
 namespace android {
@@ -73,6 +74,19 @@
         return NO_ERROR;
     }
 
+    status_t appendToStream(FileProto& fileProto, std::ofstream& out) {
+        ATRACE_CALL();
+        writeToProto(fileProto);
+        std::string output;
+        if (!fileProto.SerializeToString(&output)) {
+            ALOGE("Could not serialize proto.");
+            return UNKNOWN_ERROR;
+        }
+
+        out << output;
+        return NO_ERROR;
+    }
+
     std::vector<std::string> emplace(std::string&& serializedProto) {
         std::vector<std::string> replacedEntries;
         size_t protoSize = static_cast<size_t>(serializedProto.size());
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index ba08cee..7642122 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -15,15 +15,41 @@
  */
 
 #include <gui/SurfaceComposerClient.h>
-#include <renderengine/mock/FakeExternalTexture.h>
 #include <ui/Fence.h>
 #include <ui/Rect.h>
 
+#include "FrontEnd/LayerCreationArgs.h"
 #include "LayerProtoHelper.h"
 #include "TransactionProtoParser.h"
+#include "TransactionState.h"
+#include "gui/LayerState.h"
 
 namespace android::surfaceflinger {
 
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mEmptyBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+                        uint64_t usage)
+          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    ~FakeExternalTexture() = default;
+};
+
 proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
     proto::TransactionState proto;
     proto.set_pid(t.originPid);
@@ -35,7 +61,7 @@
 
     proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
     for (auto& layerState : t.states) {
-        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state)));
+        proto.mutable_layer_changes()->Add(std::move(toProto(layerState)));
     }
 
     proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
@@ -46,40 +72,22 @@
 }
 
 proto::TransactionState TransactionProtoParser::toProto(
-        const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+        const std::map<uint32_t /* layerId */, TracingLayerState>& states) {
     proto::TransactionState proto;
     proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(states.size()));
     for (auto& [layerId, state] : states) {
         proto::LayerState layerProto = toProto(state);
-        if (layerProto.has_buffer_data()) {
-            proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
-            bufferProto->set_buffer_id(state.bufferId);
-            bufferProto->set_width(state.bufferWidth);
-            bufferProto->set_height(state.bufferHeight);
-            bufferProto->set_pixel_format(
-                    static_cast<proto::LayerState_BufferData_PixelFormat>(state.pixelFormat));
-            bufferProto->set_usage(state.bufferUsage);
-        }
         layerProto.set_has_sideband_stream(state.hasSidebandStream);
-        layerProto.set_layer_id(state.layerId);
-        layerProto.set_parent_id(state.parentId);
-        layerProto.set_relative_parent_id(state.relativeParentId);
-        if (layerProto.has_window_info_handle()) {
-            layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
-        }
         proto.mutable_layer_changes()->Add(std::move(layerProto));
     }
     return proto;
 }
 
-proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) {
+proto::LayerState TransactionProtoParser::toProto(
+        const ResolvedComposerState& resolvedComposerState) {
     proto::LayerState proto;
-    if (layer.surface) {
-        proto.set_layer_id(mMapper->getLayerId(layer.surface));
-    } else {
-        proto.set_layer_id(layer.layerId);
-    }
-
+    auto& layer = resolvedComposerState.state;
+    proto.set_layer_id(resolvedComposerState.layerId);
     proto.set_what(layer.what);
 
     if (layer.what & layer_state_t::ePositionChanged) {
@@ -135,27 +143,13 @@
     }
     if (layer.what & layer_state_t::eBufferChanged) {
         proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
-        if (layer.bufferData->hasBuffer()) {
-            bufferProto->set_buffer_id(layer.bufferData->getId());
-            bufferProto->set_width(layer.bufferData->getWidth());
-            bufferProto->set_height(layer.bufferData->getHeight());
+        if (resolvedComposerState.externalTexture) {
+            bufferProto->set_buffer_id(resolvedComposerState.externalTexture->getId());
+            bufferProto->set_width(resolvedComposerState.externalTexture->getWidth());
+            bufferProto->set_height(resolvedComposerState.externalTexture->getHeight());
             bufferProto->set_pixel_format(static_cast<proto::LayerState_BufferData_PixelFormat>(
-                    layer.bufferData->getPixelFormat()));
-            bufferProto->set_usage(layer.bufferData->getUsage());
-        } else {
-            uint64_t bufferId;
-            uint32_t width;
-            uint32_t height;
-            int32_t pixelFormat;
-            uint64_t usage;
-            mMapper->getGraphicBufferPropertiesFromCache(layer.bufferData->cachedBuffer, &bufferId,
-                                                         &width, &height, &pixelFormat, &usage);
-            bufferProto->set_buffer_id(bufferId);
-            bufferProto->set_width(width);
-            bufferProto->set_height(height);
-            bufferProto->set_pixel_format(
-                    static_cast<proto::LayerState_BufferData_PixelFormat>(pixelFormat));
-            bufferProto->set_usage(usage);
+                    resolvedComposerState.externalTexture->getPixelFormat()));
+            bufferProto->set_usage(resolvedComposerState.externalTexture->getUsage());
         }
         bufferProto->set_frame_number(layer.bufferData->frameNumber);
         bufferProto->set_flags(layer.bufferData->flags.get());
@@ -179,16 +173,10 @@
     }
 
     if (layer.what & layer_state_t::eReparent) {
-        int64_t layerId = layer.parentSurfaceControlForChild
-                ? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
-                : -1;
-        proto.set_parent_id(layerId);
+        proto.set_parent_id(resolvedComposerState.parentId);
     }
     if (layer.what & layer_state_t::eRelativeLayerChanged) {
-        int64_t layerId = layer.relativeLayerSurfaceControl
-                ? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
-                : -1;
-        proto.set_relative_parent_id(layerId);
+        proto.set_relative_parent_id(resolvedComposerState.relativeParentId);
         proto.set_z(layer.z);
     }
 
@@ -207,7 +195,7 @@
             windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
                     gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
             windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
-            proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+            proto::Transform* transformProto = windowInfoProto->mutable_transform();
             transformProto->set_dsdx(inputInfo->transform.dsdx());
             transformProto->set_dtdx(inputInfo->transform.dtdx());
             transformProto->set_dtdy(inputInfo->transform.dtdy());
@@ -216,8 +204,7 @@
             transformProto->set_ty(inputInfo->transform.ty());
             windowInfoProto->set_replace_touchable_region_with_crop(
                     inputInfo->replaceTouchableRegionWithCrop);
-            windowInfoProto->set_crop_layer_id(
-                    mMapper->getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+            windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
         }
     }
     if (layer.what & layer_state_t::eBackgroundColorChanged) {
@@ -289,13 +276,15 @@
     return proto;
 }
 
-proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+proto::LayerCreationArgs TransactionProtoParser::toProto(const LayerCreationArgs& args) {
     proto::LayerCreationArgs proto;
-    proto.set_layer_id(args.layerId);
+    proto.set_layer_id(args.sequence);
     proto.set_name(args.name);
     proto.set_flags(args.flags);
     proto.set_parent_id(args.parentId);
-    proto.set_mirror_from_id(args.mirrorFromId);
+    proto.set_mirror_from_id(args.layerIdToMirror);
+    proto.set_add_to_root(args.addToRoot);
+    proto.set_layer_stack_to_mirror(args.layerStackToMirror.id);
     return proto;
 }
 
@@ -313,15 +302,7 @@
     for (int i = 0; i < layerCount; i++) {
         ResolvedComposerState s;
         s.state.what = 0;
-        fromProto(proto.layer_changes(i), s.state);
-        if (s.state.bufferData) {
-            s.externalTexture = std::make_shared<
-                    renderengine::mock::FakeExternalTexture>(s.state.bufferData->getWidth(),
-                                                             s.state.bufferData->getHeight(),
-                                                             s.state.bufferData->getId(),
-                                                             s.state.bufferData->getPixelFormat(),
-                                                             s.state.bufferData->getUsage());
-        }
+        fromProto(proto.layer_changes(i), s);
         t.states.emplace_back(s);
     }
 
@@ -334,46 +315,47 @@
 }
 
 void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
-                                       TracingLayerCreationArgs& outArgs) {
-    outArgs.layerId = proto.layer_id();
+                                       LayerCreationArgs& outArgs) {
+    outArgs.sequence = proto.layer_id();
+
     outArgs.name = proto.name();
     outArgs.flags = proto.flags();
     outArgs.parentId = proto.parent_id();
-    outArgs.mirrorFromId = proto.mirror_from_id();
+    outArgs.layerIdToMirror = proto.mirror_from_id();
+    outArgs.addToRoot = proto.add_to_root();
+    outArgs.layerStackToMirror.id = proto.layer_stack_to_mirror();
 }
 
 void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
                                             TracingLayerState& outState) {
-    layer_state_t state;
-    fromProto(proto, state);
-    outState.merge(state);
+    ResolvedComposerState resolvedComposerState;
+    fromProto(proto, resolvedComposerState);
+    layer_state_t& state = resolvedComposerState.state;
+    outState.state.merge(state);
+    outState.layerId = resolvedComposerState.layerId;
 
     if (state.what & layer_state_t::eReparent) {
-        outState.parentId = static_cast<int32_t>(proto.parent_id());
+        outState.parentId = resolvedComposerState.parentId;
     }
     if (state.what & layer_state_t::eRelativeLayerChanged) {
-        outState.relativeParentId = static_cast<int32_t>(proto.relative_parent_id());
+        outState.relativeParentId = resolvedComposerState.relativeParentId;
     }
     if (state.what & layer_state_t::eInputInfoChanged) {
-        outState.inputCropId = static_cast<int32_t>(proto.window_info_handle().crop_layer_id());
+        outState.touchCropId = resolvedComposerState.touchCropId;
     }
     if (state.what & layer_state_t::eBufferChanged) {
-        const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
-        outState.bufferId = bufferProto.buffer_id();
-        outState.bufferWidth = bufferProto.width();
-        outState.bufferHeight = bufferProto.height();
-        outState.pixelFormat = bufferProto.pixel_format();
-        outState.bufferUsage = bufferProto.usage();
+        outState.externalTexture = resolvedComposerState.externalTexture;
     }
     if (state.what & layer_state_t::eSidebandStreamChanged) {
         outState.hasSidebandStream = proto.has_sideband_stream();
     }
 }
 
-void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
-    layer.layerId = (int32_t)proto.layer_id();
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+                                       ResolvedComposerState& resolvedComposerState) {
+    auto& layer = resolvedComposerState.state;
+    resolvedComposerState.layerId = proto.layer_id();
     layer.what |= proto.what();
-    layer.surface = mMapper->getLayerHandle(layer.layerId);
 
     if (proto.what() & layer_state_t::ePositionChanged) {
         layer.x = proto.x();
@@ -428,9 +410,15 @@
     if (proto.what() & layer_state_t::eBufferChanged) {
         const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
         layer.bufferData =
-                std::move(mMapper->getGraphicData(bufferProto.buffer_id(), bufferProto.width(),
-                                                  bufferProto.height(), bufferProto.pixel_format(),
-                                                  bufferProto.usage()));
+                std::make_shared<fake::BufferData>(bufferProto.buffer_id(), bufferProto.width(),
+                                                   bufferProto.height(), bufferProto.pixel_format(),
+                                                   bufferProto.usage());
+        resolvedComposerState.externalTexture =
+                std::make_shared<FakeExternalTexture>(layer.bufferData->getWidth(),
+                                                      layer.bufferData->getHeight(),
+                                                      layer.bufferData->getId(),
+                                                      layer.bufferData->getPixelFormat(),
+                                                      layer.bufferData->getUsage());
         layer.bufferData->frameNumber = bufferProto.frame_number();
         layer.bufferData->flags = ftl::Flags<BufferData::BufferDataChange>(bufferProto.flags());
         layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
@@ -454,26 +442,10 @@
     }
 
     if (proto.what() & layer_state_t::eReparent) {
-        int64_t layerId = proto.parent_id();
-        if (layerId == -1) {
-            layer.parentSurfaceControlForChild = nullptr;
-        } else {
-            layer.parentSurfaceControlForChild =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
-                                             mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                             static_cast<int32_t>(layerId), "");
-        }
+        resolvedComposerState.parentId = proto.parent_id();
     }
     if (proto.what() & layer_state_t::eRelativeLayerChanged) {
-        int64_t layerId = proto.relative_parent_id();
-        if (layerId == -1) {
-            layer.relativeLayerSurfaceControl = nullptr;
-        } else {
-            layer.relativeLayerSurfaceControl =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
-                                             mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                             static_cast<int32_t>(layerId), "");
-        }
+        resolvedComposerState.relativeParentId = proto.relative_parent_id();
         layer.z = proto.z();
     }
 
@@ -493,19 +465,13 @@
         inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
                                  windowInfoProto.has_wallpaper());
         inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
-        const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+        const proto::Transform& transformProto = windowInfoProto.transform();
         inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
                                 transformProto.dsdy());
         inputInfo.transform.set(transformProto.tx(), transformProto.ty());
         inputInfo.replaceTouchableRegionWithCrop =
                 windowInfoProto.replace_touchable_region_with_crop();
-        int64_t layerId = windowInfoProto.crop_layer_id();
-        if (layerId != -1) {
-            inputInfo.touchableRegionCropHandle =
-                    mMapper->getLayerHandle(static_cast<int32_t>(layerId));
-        } else {
-            inputInfo.touchableRegionCropHandle = wp<IBinder>();
-        }
+        resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
 
         layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
     }
@@ -577,4 +543,62 @@
     return display;
 }
 
+void asProto(proto::Transform* proto, const ui::Transform& transform) {
+    proto->set_dsdx(transform.dsdx());
+    proto->set_dtdx(transform.dtdx());
+    proto->set_dtdy(transform.dtdy());
+    proto->set_dsdy(transform.dsdy());
+    proto->set_tx(transform.tx());
+    proto->set_ty(transform.ty());
+}
+
+proto::DisplayInfo TransactionProtoParser::toProto(const frontend::DisplayInfo& displayInfo,
+                                                   uint32_t layerStack) {
+    proto::DisplayInfo proto;
+    proto.set_layer_stack(layerStack);
+    proto.set_display_id(displayInfo.info.displayId);
+    proto.set_logical_width(displayInfo.info.logicalWidth);
+    proto.set_logical_height(displayInfo.info.logicalHeight);
+    asProto(proto.mutable_transform_inverse(), displayInfo.info.transform);
+    asProto(proto.mutable_transform(), displayInfo.transform);
+    proto.set_receives_input(displayInfo.receivesInput);
+    proto.set_is_secure(displayInfo.isSecure);
+    proto.set_is_primary(displayInfo.isPrimary);
+    proto.set_is_virtual(displayInfo.isVirtual);
+    proto.set_rotation_flags((int)displayInfo.rotationFlags);
+    proto.set_transform_hint((int)displayInfo.transformHint);
+    return proto;
+}
+
+void fromProto2(ui::Transform& outTransform, const proto::Transform& proto) {
+    outTransform.set(proto.dsdx(), proto.dtdx(), proto.dtdy(), proto.dsdy());
+    outTransform.set(proto.tx(), proto.ty());
+}
+
+frontend::DisplayInfo TransactionProtoParser::fromProto(const proto::DisplayInfo& proto) {
+    frontend::DisplayInfo displayInfo;
+    displayInfo.info.displayId = proto.display_id();
+    displayInfo.info.logicalWidth = proto.logical_width();
+    displayInfo.info.logicalHeight = proto.logical_height();
+    fromProto2(displayInfo.info.transform, proto.transform_inverse());
+    fromProto2(displayInfo.transform, proto.transform());
+    displayInfo.receivesInput = proto.receives_input();
+    displayInfo.isSecure = proto.is_secure();
+    displayInfo.isPrimary = proto.is_primary();
+    displayInfo.isPrimary = proto.is_virtual();
+    displayInfo.rotationFlags = (ui::Transform::RotationFlags)proto.rotation_flags();
+    displayInfo.transformHint = (ui::Transform::RotationFlags)proto.transform_hint();
+    return displayInfo;
+}
+
+void TransactionProtoParser::fromProto(
+        const google::protobuf::RepeatedPtrField<proto::DisplayInfo>& proto,
+        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> outDisplayInfos) {
+    outDisplayInfos.clear();
+    for (const proto::DisplayInfo& displayInfo : proto) {
+        outDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(displayInfo.layer_stack()),
+                                           fromProto(displayInfo));
+    }
+}
+
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 2232bb9..50944fc 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -18,30 +18,17 @@
 #include <gui/fake/BufferData.h>
 #include <layerproto/TransactionProto.h>
 #include <utils/RefBase.h>
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
 
+#include "FrontEnd/LayerCreationArgs.h"
 #include "TransactionState.h"
 
 namespace android::surfaceflinger {
 
-struct TracingLayerCreationArgs {
-    int32_t layerId;
-    std::string name;
-    uint32_t flags = 0;
-    int32_t parentId = -1;
-    int32_t mirrorFromId = -1;
-};
-
-struct TracingLayerState : layer_state_t {
-    uint64_t bufferId;
-    uint32_t bufferHeight;
-    uint32_t bufferWidth;
-    int32_t pixelFormat;
-    uint64_t bufferUsage;
+struct TracingLayerState : ResolvedComposerState {
     bool hasSidebandStream;
-    int32_t parentId;
-    int32_t relativeParentId;
-    int32_t inputCropId;
-    TracingLayerCreationArgs args;
+    LayerCreationArgs args;
 };
 
 class TransactionProtoParser {
@@ -51,40 +38,30 @@
     class FlingerDataMapper {
     public:
         virtual ~FlingerDataMapper() = default;
-        virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
-        virtual int64_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
-        virtual int64_t getLayerId(BBinder* /* layerHandle */) const { return -1; }
         virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
         virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
-        virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
-                                                           uint32_t height, int32_t pixelFormat,
-                                                           uint64_t usage) const {
-            return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
-        }
-        virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
-                                                         uint64_t* /* outBufferId */,
-                                                         uint32_t* /* outWidth */,
-                                                         uint32_t* /* outHeight */,
-                                                         int32_t* /* outPixelFormat */,
-                                                         uint64_t* /* outUsage */) const {}
     };
 
     TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
           : mMapper(std::move(provider)) {}
 
     proto::TransactionState toProto(const TransactionState&);
-    proto::TransactionState toProto(const std::map<int32_t /* layerId */, TracingLayerState>&);
-    proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+    proto::TransactionState toProto(const std::map<uint32_t /* layerId */, TracingLayerState>&);
+    proto::LayerCreationArgs toProto(const LayerCreationArgs& args);
+    proto::LayerState toProto(const ResolvedComposerState&);
+    proto::DisplayInfo toProto(const frontend::DisplayInfo&, uint32_t layerStack);
 
     TransactionState fromProto(const proto::TransactionState&);
     void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
-    void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+    void fromProto(const proto::LayerCreationArgs&, LayerCreationArgs& outArgs);
     std::unique_ptr<FlingerDataMapper> mMapper;
+    frontend::DisplayInfo fromProto(const proto::DisplayInfo&);
+    void fromProto(const google::protobuf::RepeatedPtrField<proto::DisplayInfo>&,
+                   display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> outDisplayInfos);
 
 private:
-    proto::LayerState toProto(const layer_state_t&);
     proto::DisplayState toProto(const DisplayState&);
-    void fromProto(const proto::LayerState&, layer_state_t& out);
+    void fromProto(const proto::LayerState&, ResolvedComposerState& out);
     DisplayState fromProto(const proto::DisplayState&);
 
 };
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index cb5320b..26ed878 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,75 +23,14 @@
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
-#include "ClientCache.h"
+#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
 #include "TransactionTracing.h"
-#include "renderengine/ExternalTexture.h"
 
 namespace android {
 
-// Keeps the binder address as the layer id so we can avoid holding the tracing lock in the
-// binder thread.
-class FlatDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
-    virtual int64_t getLayerId(const sp<IBinder>& layerHandle) const {
-        if (layerHandle == nullptr) {
-            return -1;
-        }
-
-        return reinterpret_cast<int64_t>(layerHandle->localBinder());
-    }
-
-    void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
-                                             uint32_t* outWidth, uint32_t* outHeight,
-                                             int32_t* outPixelFormat,
-                                             uint64_t* outUsage) const override {
-        std::shared_ptr<renderengine::ExternalTexture> buffer =
-                ClientCache::getInstance().get(cachedBuffer);
-        if (!buffer || !buffer->getBuffer()) {
-            *outBufferId = 0;
-            *outWidth = 0;
-            *outHeight = 0;
-            *outPixelFormat = 0;
-            *outUsage = 0;
-            return;
-        }
-
-        *outBufferId = buffer->getId();
-        *outWidth = buffer->getWidth();
-        *outHeight = buffer->getHeight();
-        *outPixelFormat = buffer->getPixelFormat();
-        *outUsage = buffer->getUsage();
-        return;
-    }
-};
-
-class FlingerDataMapper : public FlatDataMapper {
-    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
-
-public:
-    FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
-          : mLayerHandles(layerHandles) {}
-
-    int64_t getLayerId(const sp<IBinder>& layerHandle) const override {
-        if (layerHandle == nullptr) {
-            return -1;
-        }
-        return getLayerId(layerHandle->localBinder());
-    }
-
-    int64_t getLayerId(BBinder* localBinder) const {
-        auto it = mLayerHandles.find(localBinder);
-        if (it == mLayerHandles.end()) {
-            ALOGW("Could not find layer handle %p", localBinder);
-            return -1;
-        }
-        return it->second;
-    }
-};
-
 TransactionTracing::TransactionTracing()
-      : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)),
-        mLockfreeProtoParser(std::make_unique<FlatDataMapper>()) {
+      : mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
     std::scoped_lock lock(mTraceLock);
 
     mBuffer.setSize(mBufferSizeInBytes);
@@ -137,84 +76,77 @@
     auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
                                                    systemTime(SYSTEM_TIME_MONOTONIC));
     proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
+    proto.set_version(TRACING_VERSION);
     return proto;
 }
 
 void TransactionTracing::dump(std::string& result) const {
     std::scoped_lock lock(mTraceLock);
-    base::StringAppendF(&result,
-                        "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
-                        mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
-                        mStartingStates.size());
+    base::StringAppendF(&result, "  queued transactions=%zu created layers=%zu states=%zu\n",
+                        mQueuedTransactions.size(), mCreatedLayers.size(), mStartingStates.size());
     mBuffer.dump(result);
 }
 
 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
-    proto::TransactionState* state =
-            new proto::TransactionState(mLockfreeProtoParser.toProto(transaction));
+    proto::TransactionState* state = new proto::TransactionState(mProtoParser.toProto(transaction));
     mTransactionQueue.push(state);
 }
 
-TransactionTracing::CommittedTransactions&
-TransactionTracing::findOrCreateCommittedTransactionRecord(int64_t vsyncId) {
-    for (auto& pendingTransaction : mPendingTransactions) {
-        if (pendingTransaction.vsyncId == vsyncId) {
-            return pendingTransaction;
-        }
+void TransactionTracing::addCommittedTransactions(
+        int64_t vsyncId, nsecs_t commitTime, frontend::Update& newUpdate,
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+        bool displayInfoChanged) {
+    CommittedUpdates update;
+    update.vsyncId = vsyncId;
+    update.timestamp = commitTime;
+    update.transactionIds.reserve(newUpdate.transactions.size());
+    for (const auto& transaction : newUpdate.transactions) {
+        update.transactionIds.emplace_back(transaction.id);
     }
-
-    CommittedTransactions committedTransactions;
-    committedTransactions.vsyncId = vsyncId;
-    committedTransactions.timestamp = systemTime();
-    mPendingTransactions.emplace_back(committedTransactions);
-    return mPendingTransactions.back();
-}
-
-void TransactionTracing::onLayerAddedToDrawingState(int layerId, int64_t vsyncId) {
-    CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId);
-    committedTransactions.createdLayerIds.emplace_back(layerId);
-}
-
-void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
-                                                  int64_t vsyncId) {
-    CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId);
-    committedTransactions.transactionIds.reserve(transactions.size());
-    for (const auto& transaction : transactions) {
-        committedTransactions.transactionIds.emplace_back(transaction.id);
+    update.displayInfoChanged = displayInfoChanged;
+    if (displayInfoChanged) {
+        update.displayInfos = displayInfos;
     }
+    update.createdLayers = std::move(newUpdate.layerCreationArgs);
+    newUpdate.layerCreationArgs.clear();
+    update.destroyedLayerHandles.reserve(newUpdate.destroyedHandles.size());
+    for (uint32_t handle : newUpdate.destroyedHandles) {
+        update.destroyedLayerHandles.push_back(handle);
+    }
+    mPendingUpdates.emplace_back(update);
     tryPushToTracingThread();
 }
 
 void TransactionTracing::loop() {
     while (true) {
-        std::vector<CommittedTransactions> committedTransactions;
-        std::vector<int32_t> removedLayers;
+        std::vector<CommittedUpdates> committedUpdates;
+        std::vector<uint32_t> destroyedLayers;
         {
             std::unique_lock<std::mutex> lock(mMainThreadLock);
             base::ScopedLockAssertion assumeLocked(mMainThreadLock);
             mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
-                return mDone || !mCommittedTransactions.empty();
+                return mDone || !mUpdates.empty();
             });
             if (mDone) {
-                mCommittedTransactions.clear();
-                mRemovedLayers.clear();
+                mUpdates.clear();
+                mDestroyedLayers.clear();
                 break;
             }
 
-            removedLayers = std::move(mRemovedLayers);
-            mRemovedLayers.clear();
-            committedTransactions = std::move(mCommittedTransactions);
-            mCommittedTransactions.clear();
+            destroyedLayers = std::move(mDestroyedLayers);
+            mDestroyedLayers.clear();
+            committedUpdates = std::move(mUpdates);
+            mUpdates.clear();
         } // unlock mMainThreadLock
 
-        if (!committedTransactions.empty() || !removedLayers.empty()) {
-            addEntry(committedTransactions, removedLayers);
+        if (!committedUpdates.empty() || !destroyedLayers.empty()) {
+            addEntry(committedUpdates, destroyedLayers);
         }
     }
 }
 
-void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
-                                  const std::vector<int32_t>& removedLayers) {
+void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
+                                  const std::vector<uint32_t>& destroyedLayers) {
     ATRACE_CALL();
     std::scoped_lock lock(mTraceLock);
     std::vector<std::string> removedEntries;
@@ -222,59 +154,27 @@
 
     while (auto incomingTransaction = mTransactionQueue.pop()) {
         auto transaction = *incomingTransaction;
-        int32_t layerCount = transaction.layer_changes_size();
-        for (int i = 0; i < layerCount; i++) {
-            auto layer = transaction.mutable_layer_changes(i);
-            layer->set_layer_id(
-                mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(layer->layer_id())));
-            if ((layer->what() & layer_state_t::eReparent) && layer->parent_id() != -1) {
-                layer->set_parent_id(
-                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                        layer->parent_id())));
-            }
-
-            if ((layer->what() & layer_state_t::eRelativeLayerChanged) &&
-                layer->relative_parent_id() != -1) {
-                layer->set_relative_parent_id(
-                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                        layer->relative_parent_id())));
-            }
-
-            if (layer->has_window_info_handle() &&
-                layer->window_info_handle().crop_layer_id() != -1) {
-                auto input = layer->mutable_window_info_handle();
-                input->set_crop_layer_id(
-                        mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                            input->crop_layer_id())));
-            }
-        }
         mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
         delete incomingTransaction;
     }
-    for (const CommittedTransactions& entry : committedTransactions) {
-        entryProto.set_elapsed_realtime_nanos(entry.timestamp);
-        entryProto.set_vsync_id(entry.vsyncId);
+    for (const CommittedUpdates& update : committedUpdates) {
+        entryProto.set_elapsed_realtime_nanos(update.timestamp);
+        entryProto.set_vsync_id(update.vsyncId);
         entryProto.mutable_added_layers()->Reserve(
-                static_cast<int32_t>(entry.createdLayerIds.size()));
+                static_cast<int32_t>(update.createdLayers.size()));
 
-        for (const int32_t& id : entry.createdLayerIds) {
-            auto it = mCreatedLayers.find(id);
-            if (it != mCreatedLayers.end()) {
-                entryProto.mutable_added_layers()->Add(std::move(it->second));
-                mCreatedLayers.erase(it);
-            } else {
-                ALOGW("Could not created layer with id %d", id);
-            }
+        for (const auto& args : update.createdLayers) {
+            entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
         }
 
-        entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
-        for (auto& removedLayer : removedLayers) {
-            entryProto.mutable_removed_layers()->Add(removedLayer);
-            mCreatedLayers.erase(removedLayer);
+        entryProto.mutable_destroyed_layers()->Reserve(
+                static_cast<int32_t>(destroyedLayers.size()));
+        for (auto& destroyedLayer : destroyedLayers) {
+            entryProto.mutable_destroyed_layers()->Add(destroyedLayer);
         }
         entryProto.mutable_transactions()->Reserve(
-                static_cast<int32_t>(entry.transactionIds.size()));
-        for (const uint64_t& id : entry.transactionIds) {
+                static_cast<int32_t>(update.transactionIds.size()));
+        for (const uint64_t& id : update.transactionIds) {
             auto it = mQueuedTransactions.find(id);
             if (it != mQueuedTransactions.end()) {
                 entryProto.mutable_transactions()->Add(std::move(it->second));
@@ -284,13 +184,21 @@
             }
         }
 
-        entryProto.mutable_removed_layer_handles()->Reserve(
-                static_cast<int32_t>(mRemovedLayerHandles.size()));
-        for (auto& [handle, layerId] : mRemovedLayerHandles) {
-            entryProto.mutable_removed_layer_handles()->Add(layerId);
-            mLayerHandles.erase(handle);
+        entryProto.mutable_destroyed_layer_handles()->Reserve(
+                static_cast<int32_t>(update.destroyedLayerHandles.size()));
+        for (auto layerId : update.destroyedLayerHandles) {
+            entryProto.mutable_destroyed_layer_handles()->Add(layerId);
         }
-        mRemovedLayerHandles.clear();
+
+        entryProto.set_displays_changed(update.displayInfoChanged);
+        if (update.displayInfoChanged) {
+            entryProto.mutable_displays()->Reserve(
+                    static_cast<int32_t>(update.displayInfos.size()));
+            for (auto& [layerStack, displayInfo] : update.displayInfos) {
+                entryProto.mutable_displays()->Add(
+                        std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
+            }
+        }
 
         std::string serializedProto;
         entryProto.SerializeToString(&serializedProto);
@@ -311,7 +219,7 @@
 }
 
 void TransactionTracing::flush(int64_t vsyncId) {
-    while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+    while (!mPendingUpdates.empty() || !mPendingDestroyedLayers.empty()) {
         tryPushToTracingThread();
     }
     std::unique_lock<std::mutex> lock(mTraceLock);
@@ -325,54 +233,21 @@
     });
 }
 
-void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
-                                      uint32_t flags, int parentId) {
-    std::scoped_lock lock(mTraceLock);
-    TracingLayerCreationArgs args{layerId, name, flags, parentId, -1 /* mirrorFromId */};
-    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
-        ALOGW("Duplicate handles found. %p", layerHandle);
-    }
-    mLayerHandles[layerHandle] = layerId;
-    mCreatedLayers[layerId] = mProtoParser.toProto(args);
-}
-
-void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
-                                            const std::string& name, int mirrorFromId) {
-    std::scoped_lock lock(mTraceLock);
-    TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
-    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
-        ALOGW("Duplicate handles found. %p", layerHandle);
-    }
-    mLayerHandles[layerHandle] = layerId;
-    mCreatedLayers[layerId] = mProtoParser.toProto(args);
-}
-
 void TransactionTracing::onLayerRemoved(int32_t layerId) {
-    mPendingRemovedLayers.emplace_back(layerId);
+    mPendingDestroyedLayers.emplace_back(layerId);
     tryPushToTracingThread();
 }
 
-void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
-    std::scoped_lock lock(mTraceLock);
-    auto it = mLayerHandles.find(layerHandle);
-    if (it == mLayerHandles.end()) {
-        ALOGW("handle not found. %p", layerHandle);
-        return;
-    }
-    mRemovedLayerHandles.emplace_back(layerHandle, it->second);
-}
-
 void TransactionTracing::tryPushToTracingThread() {
     // Try to acquire the lock from main thread.
     if (mMainThreadLock.try_lock()) {
         // We got the lock! Collect any pending transactions and continue.
-        mCommittedTransactions.insert(mCommittedTransactions.end(),
-                                      std::make_move_iterator(mPendingTransactions.begin()),
-                                      std::make_move_iterator(mPendingTransactions.end()));
-        mPendingTransactions.clear();
-        mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
-                              mPendingRemovedLayers.end());
-        mPendingRemovedLayers.clear();
+        mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
+                        std::make_move_iterator(mPendingUpdates.end()));
+        mPendingUpdates.clear();
+        mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
+                                mPendingDestroyedLayers.end());
+        mPendingDestroyedLayers.clear();
         mTransactionsAvailableCv.notify_one();
         mMainThreadLock.unlock();
     } else {
@@ -394,24 +269,28 @@
     // Merge layer states to starting transaction state.
     for (const proto::TransactionState& transaction : removedEntry.transactions()) {
         for (const proto::LayerState& layerState : transaction.layer_changes()) {
-            auto it = mStartingStates.find((int32_t)layerState.layer_id());
+            auto it = mStartingStates.find(layerState.layer_id());
             if (it == mStartingStates.end()) {
-                ALOGW("Could not find layer id %d", (int32_t)layerState.layer_id());
+                ALOGW("Could not find layer id %d", layerState.layer_id());
                 continue;
             }
             mProtoParser.mergeFromProto(layerState, it->second);
         }
     }
 
-    for (const int32_t removedLayerHandleId : removedEntry.removed_layer_handles()) {
-        mRemovedLayerHandlesAtStart.insert(removedLayerHandleId);
+    for (const uint32_t destroyedLayerHandleId : removedEntry.destroyed_layer_handles()) {
+        mRemovedLayerHandlesAtStart.insert(destroyedLayerHandleId);
     }
 
     // Clean up stale starting states since the layer has been removed and the buffer does not
     // contain any references to the layer.
-    for (const int32_t removedLayerId : removedEntry.removed_layers()) {
-        mStartingStates.erase(removedLayerId);
-        mRemovedLayerHandlesAtStart.erase(removedLayerId);
+    for (const uint32_t destroyedLayerId : removedEntry.destroyed_layers()) {
+        mStartingStates.erase(destroyedLayerId);
+        mRemovedLayerHandlesAtStart.erase(destroyedLayerId);
+    }
+
+    if (removedEntry.displays_changed()) {
+        mProtoParser.fromProto(removedEntry.displays(), mStartingDisplayInfos);
     }
 }
 
@@ -434,10 +313,15 @@
     transactionProto.set_post_time(mStartingTimestamp);
     entryProto->mutable_transactions()->Add(std::move(transactionProto));
 
-    entryProto->mutable_removed_layer_handles()->Reserve(
+    entryProto->mutable_destroyed_layer_handles()->Reserve(
             static_cast<int32_t>(mRemovedLayerHandlesAtStart.size()));
-    for (const int32_t removedLayerHandleId : mRemovedLayerHandlesAtStart) {
-        entryProto->mutable_removed_layer_handles()->Add(removedLayerHandleId);
+    for (const uint32_t destroyedLayerHandleId : mRemovedLayerHandlesAtStart) {
+        entryProto->mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
+    }
+
+    entryProto->mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
+    for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
+        entryProto->mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
     }
 }
 
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index ae01d3c..f27e7a9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,8 +25,12 @@
 #include <mutex>
 #include <thread>
 
-#include "RingBuffer.h"
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
 #include "LocklessStack.h"
+#include "RingBuffer.h"
 #include "TransactionProtoParser.h"
 
 using namespace android::surfaceflinger;
@@ -55,22 +59,22 @@
     ~TransactionTracing();
 
     void addQueuedTransaction(const TransactionState&);
-    void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+    void addCommittedTransactions(
+            int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+            bool displayInfoChanged);
     status_t writeToFile(std::string filename = FILE_NAME);
     void setBufferSize(size_t bufferSizeInBytes);
-    void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
-                      int parentId);
-    void onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
-                            int mirrorFromId);
     void onLayerRemoved(int layerId);
-    void onHandleRemoved(BBinder* layerHandle);
-    void onLayerAddedToDrawingState(int layerId, int64_t vsyncId);
     void dump(std::string&) const;
     static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
     static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+    // version 1 - switching to support new frontend
+    static constexpr auto TRACING_VERSION = 1;
 
 private:
     friend class TransactionTracingTest;
+    friend class SurfaceFlinger;
 
     static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
 
@@ -83,16 +87,12 @@
     LocklessStack<proto::TransactionState> mTransactionQueue;
     nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
     std::unordered_map<int, proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
-    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+    std::map<uint32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mStartingDisplayInfos
             GUARDED_BY(mTraceLock);
-    std::vector<std::pair<BBinder* /* layerHandle */, int32_t /* layerId */>> mRemovedLayerHandles
-            GUARDED_BY(mTraceLock);
-    std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
-    std::set<int32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
-    TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
-    // Parses the transaction to proto without holding any tracing locks so we can generate proto
-    // in the binder thread without any contention.
-    TransactionProtoParser mLockfreeProtoParser;
+
+    std::set<uint32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
+    TransactionProtoParser mProtoParser;
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
     // otherwise will push data to temporary container.
@@ -101,27 +101,29 @@
     bool mDone GUARDED_BY(mMainThreadLock) = false;
     std::condition_variable mTransactionsAvailableCv;
     std::condition_variable mTransactionsAddedToBufferCv;
-    struct CommittedTransactions {
+    struct CommittedUpdates {
         std::vector<uint64_t> transactionIds;
-        std::vector<int32_t> createdLayerIds;
+        std::vector<LayerCreationArgs> createdLayers;
+        std::vector<uint32_t> destroyedLayerHandles;
+        bool displayInfoChanged;
+        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
         int64_t vsyncId;
         int64_t timestamp;
     };
-    std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
-    std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+    std::vector<CommittedUpdates> mUpdates GUARDED_BY(mMainThreadLock);
+    std::vector<CommittedUpdates> mPendingUpdates; // only accessed by main thread
 
-    std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
-    std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+    std::vector<uint32_t /* layerId */> mDestroyedLayers GUARDED_BY(mMainThreadLock);
+    std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
 
     proto::TransactionTraceFile createTraceFileProto() const;
     void loop();
-    void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
-                  const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+    void addEntry(const std::vector<CommittedUpdates>& committedTransactions,
+                  const std::vector<uint32_t>& removedLayers) EXCLUDES(mTraceLock);
     int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
     void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
     void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
     void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
-    CommittedTransactions& findOrCreateCommittedTransactionRecord(int64_t vsyncId);
     // TEST
     // Wait until all the committed transactions for the specified vsync id are added to the buffer.
     void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 31f4723..0ea421b 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -14,175 +14,32 @@
  * limitations under the License.
  */
 
+#include <ios>
+#include <memory>
+#include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/RequestedLayerState.h"
+#include "Tracing/LayerTracing.h"
+#include "TransactionState.h"
+#include "cutils/properties.h"
 #undef LOG_TAG
 #define LOG_TAG "LayerTraceGenerator"
 //#define LOG_NDEBUG 0
 
-#include <TestableSurfaceFlinger.h>
 #include <Tracing/TransactionProtoParser.h>
-#include <binder/IPCThreadState.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
 #include <gui/LayerState.h>
 #include <log/log.h>
-#include <mock/MockEventThread.h>
 #include <renderengine/ExternalTexture.h>
-#include <renderengine/mock/RenderEngine.h>
 #include <utils/String16.h>
+#include <filesystem>
+#include <fstream>
 #include <string>
+#include "LayerProtoHelper.h"
 
 #include "LayerTraceGenerator.h"
 
 namespace android {
-
-class Factory final : public surfaceflinger::Factory {
-public:
-    ~Factory() = default;
-
-    std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
-
-    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            Fps /*currentRefreshRate*/) override {
-        return std::make_unique<scheduler::FakePhaseOffsets>();
-    }
-
-    sp<StartPropertySetThread> createStartPropertySetThread(
-            bool /* timestampPropertyValue */) override {
-        return sp<StartPropertySetThread>();
-    }
-
-    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& /* creationArgs */) override {
-        return sp<DisplayDevice>();
-    }
-
-    sp<GraphicBuffer> createGraphicBuffer(uint32_t /* width */, uint32_t /* height */,
-                                          PixelFormat /* format */, uint32_t /* layerCount */,
-                                          uint64_t /* usage */,
-                                          std::string /* requestorName */) override {
-        return sp<GraphicBuffer>();
-    }
-
-    void createBufferQueue(sp<IGraphicBufferProducer>* /* outProducer */,
-                           sp<IGraphicBufferConsumer>* /* outConsumer */,
-                           bool /* consumerIsSurfaceFlinger */) override {}
-
-    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
-            const sp<IGraphicBufferProducer>& /* producer */) override {
-        return nullptr;
-    }
-
-    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
-        return compositionengine::impl::createCompositionEngine();
-    }
-
-    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) {
-        return sp<Layer>::make(args);
-    }
-
-    sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
-
-    sp<LayerFE> createLayerFE(const std::string& layerName) { return sp<LayerFE>::make(layerName); }
-
-    std::unique_ptr<FrameTracer> createFrameTracer() override {
-        return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
-    }
-
-    std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
-        return std::make_unique<testing::NiceMock<mock::FrameTimeline>>(timeStats,
-                                                                        surfaceFlingerPid);
-    }
-};
-
-class FakeExternalTexture : public renderengine::ExternalTexture {
-    const sp<GraphicBuffer> mNullBuffer = nullptr;
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint64_t mId;
-    PixelFormat mPixelFormat;
-    uint64_t mUsage;
-
-public:
-    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
-                        uint64_t usage)
-          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
-    const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
-    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
-        return getId() == other.getId();
-    }
-    uint32_t getWidth() const override { return mWidth; }
-    uint32_t getHeight() const override { return mHeight; }
-    uint64_t getId() const override { return mId; }
-    PixelFormat getPixelFormat() const override { return mPixelFormat; }
-    uint64_t getUsage() const override { return mUsage; }
-    ~FakeExternalTexture() = default;
-};
-
-class MockSurfaceFlinger : public SurfaceFlinger {
-public:
-    MockSurfaceFlinger(Factory& factory)
-          : SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
-    std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
-            BufferData& bufferData, const char* /* layerName */,
-            uint64_t /* transactionId */) override {
-        return std::make_shared<FakeExternalTexture>(bufferData.getWidth(), bufferData.getHeight(),
-                                                     bufferData.getId(),
-                                                     bufferData.getPixelFormat(),
-                                                     bufferData.getUsage());
-    };
-
-    // b/220017192 migrate from transact codes to ISurfaceComposer apis
-    void setLayerTracingFlags(int32_t flags) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(flags);
-        transact(1033, data, &reply, 0 /* flags */);
-    }
-
-    void setLayerTraceSize(int32_t sizeInKb) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(sizeInKb);
-        transact(1029, data, &reply, 0 /* flags */);
-    }
-
-    void startLayerTracing(int64_t traceStartTime) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(1);
-        data.writeInt64(traceStartTime);
-        transact(1025, data, &reply, 0 /* flags */);
-    }
-
-    void stopLayerTracing(const char* tracePath) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(2);
-        data.writeCString(tracePath);
-        transact(1025, data, &reply, 0 /* flags */);
-    }
-};
-
-class TraceGenFlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
-    std::unordered_map<int32_t /*layerId*/, sp<IBinder> /* handle */> mLayerHandles;
-    sp<IBinder> getLayerHandle(int32_t layerId) const override {
-        if (layerId == -1) {
-            ALOGE("Error: Called with layer=%d", layerId);
-            return nullptr;
-        }
-        auto it = mLayerHandles.find(layerId);
-        if (it == mLayerHandles.end()) {
-            ALOGE("Error: Could not find handle for layer=%d", layerId);
-            return nullptr;
-        }
-        return it->second;
-    }
-};
+using namespace ftl::flag_operators;
 
 bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
                                    const char* outputLayersTracePath) {
@@ -191,82 +48,108 @@
         return false;
     }
 
-    Factory factory;
-    sp<MockSurfaceFlinger> flingerPtr = sp<MockSurfaceFlinger>::make(factory);
-    TestableSurfaceFlinger flinger(flingerPtr);
-    flinger.setupRenderEngine(
-            std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>());
-    flinger.setupMockScheduler({.useNiceMock = true});
+    TransactionProtoParser parser(std::make_unique<TransactionProtoParser::FlingerDataMapper>());
 
-    Hwc2::mock::Composer* composerPtr = new testing::NiceMock<Hwc2::mock::Composer>();
-    flinger.setupComposer(std::unique_ptr<Hwc2::Composer>(composerPtr));
-    flinger.mutableMaxRenderTargetSize() = 16384;
+    // frontend
+    frontend::LayerLifecycleManager lifecycleManager;
+    frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
+    frontend::LayerSnapshotBuilder snapshotBuilder;
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
 
-    flingerPtr->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
-    flingerPtr->setLayerTraceSize(512 * 1024); // 512MB buffer size
-    flingerPtr->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
-    std::unique_ptr<TraceGenFlingerDataMapper> mapper =
-            std::make_unique<TraceGenFlingerDataMapper>();
-    TraceGenFlingerDataMapper* dataMapper = mapper.get();
-    TransactionProtoParser parser(std::move(mapper));
+    renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    bool supportsBlur = atoi(value);
+
+    LayerTracing layerTracing;
+    layerTracing.setTraceFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
+    // 10MB buffer size (large enough to hold a single entry)
+    layerTracing.setBufferSize(10 * 1024 * 1024);
+    layerTracing.enable();
+    layerTracing.writeToFile(outputLayersTracePath);
+    std::ofstream out(outputLayersTracePath, std::ios::binary | std::ios::app);
 
     ALOGD("Generating %d transactions...", traceFile.entry_size());
     for (int i = 0; i < traceFile.entry_size(); i++) {
+        // parse proto
         proto::TransactionTraceEntry entry = traceFile.entry(i);
         ALOGV("    Entry %04d/%04d for time=%" PRId64 " vsyncid=%" PRId64
               " layers +%d -%d handles -%d transactions=%d",
               i, traceFile.entry_size(), entry.elapsed_realtime_nanos(), entry.vsync_id(),
-              entry.added_layers_size(), entry.removed_layers_size(),
-              entry.removed_layer_handles_size(), entry.transactions_size());
+              entry.added_layers_size(), entry.destroyed_layers_size(),
+              entry.destroyed_layer_handles_size(), entry.transactions_size());
 
+        std::vector<std::unique_ptr<frontend::RequestedLayerState>> addedLayers;
+        addedLayers.reserve((size_t)entry.added_layers_size());
         for (int j = 0; j < entry.added_layers_size(); j++) {
-            // create layers
-            TracingLayerCreationArgs tracingArgs;
-            parser.fromProto(entry.added_layers(j), tracingArgs);
-
-            gui::CreateSurfaceResult outResult;
-            LayerCreationArgs args(flinger.flinger(), nullptr /* client */, tracingArgs.name,
-                                   tracingArgs.flags, LayerMetadata(),
-                                   std::make_optional<int32_t>(tracingArgs.layerId));
-
-            if (tracingArgs.mirrorFromId == -1) {
-                sp<IBinder> parentHandle = nullptr;
-                if ((tracingArgs.parentId != -1) &&
-                    (dataMapper->mLayerHandles.find(tracingArgs.parentId) ==
-                     dataMapper->mLayerHandles.end())) {
-                    args.addToRoot = false;
-                } else if (tracingArgs.parentId != -1) {
-                    parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
-                }
-                flinger.createLayer(args, parentHandle, outResult);
-            } else {
-                sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
-                flinger.mirrorLayer(args, mirrorFromHandle, outResult);
-            }
-            LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId,
-                                "Could not create layer expected:%d actual:%d", tracingArgs.layerId,
-                                outResult.layerId);
-            dataMapper->mLayerHandles[tracingArgs.layerId] = outResult.handle;
+            LayerCreationArgs args;
+            parser.fromProto(entry.added_layers(j), args);
+            addedLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
         }
 
+        std::vector<TransactionState> transactions;
+        transactions.reserve((size_t)entry.transactions_size());
         for (int j = 0; j < entry.transactions_size(); j++) {
             // apply transactions
             TransactionState transaction = parser.fromProto(entry.transactions(j));
-            flinger.setTransactionStateInternal(transaction);
+            transactions.emplace_back(std::move(transaction));
         }
 
-        const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos());
-        const auto vsyncId = VsyncId{entry.vsync_id()};
-        flinger.commit(frameTime, vsyncId);
-
-        for (int j = 0; j < entry.removed_layer_handles_size(); j++) {
-            dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j));
+        std::vector<uint32_t> destroyedHandles;
+        destroyedHandles.reserve((size_t)entry.destroyed_layer_handles_size());
+        for (int j = 0; j < entry.destroyed_layer_handles_size(); j++) {
+            destroyedHandles.push_back(entry.destroyed_layer_handles(j));
         }
+
+        bool displayChanged = entry.displays_changed();
+        if (displayChanged) {
+            parser.fromProto(entry.displays(), displayInfos);
+        }
+
+        // apply updates
+        lifecycleManager.addLayers(std::move(addedLayers));
+        lifecycleManager.applyTransactions(transactions);
+        lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true);
+
+        if (lifecycleManager.getGlobalChanges().test(
+                    frontend::RequestedLayerState::Changes::Hierarchy)) {
+            hierarchyBuilder.update(lifecycleManager.getLayers(),
+                                    lifecycleManager.getDestroyedLayers());
+        }
+
+        frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
+                                                  .layerLifecycleManager = lifecycleManager,
+                                                  .displays = displayInfos,
+                                                  .displayChanges = displayChanged,
+                                                  .globalShadowSettings = globalShadowSettings,
+                                                  .supportsBlur = supportsBlur,
+                                                  .forceFullDamage = false,
+                                                  .supportedLayerGenericMetadata = {},
+                                                  .genericLayerMetadataKeyMap = {}};
+        snapshotBuilder.update(args);
+
+        bool visibleRegionsDirty = lifecycleManager.getGlobalChanges().any(
+                frontend::RequestedLayerState::Changes::VisibleRegion |
+                frontend::RequestedLayerState::Changes::Hierarchy |
+                frontend::RequestedLayerState::Changes::Visibility);
+
+        ALOGV("    layers:%04zu snapshots:%04zu changes:%s", lifecycleManager.getLayers().size(),
+              snapshotBuilder.getSnapshots().size(),
+              lifecycleManager.getGlobalChanges().string().c_str());
+
+        lifecycleManager.commitChanges();
+
+        LayersProto layersProto = LayerProtoFromSnapshotGenerator(snapshotBuilder, displayInfos, {},
+                                                                  layerTracing.getFlags())
+                                          .generate(hierarchyBuilder.getHierarchy());
+        auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
+        layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
+                            &layersProto, {}, &displayProtos);
+        layerTracing.appendToStream(out);
     }
-
-    flingerPtr->stopLayerTracing(outputLayersTracePath);
+    layerTracing.disable("", /*writeToFile=*/false);
+    out.close();
     ALOGD("End of generating trace file. File written to %s", outputLayersTracePath);
-    dataMapper->mLayerHandles.clear();
     return true;
 }
 
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index 9f9ae48..c440c19 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -53,9 +53,6 @@
     ALOGD("Generating %s...", outputLayersTracePath);
     std::cout << "Generating " << outputLayersTracePath << "\n";
 
-    // sink any log spam from the stubbed surfaceflinger
-    __android_log_set_logger([](const struct __android_log_message* /* log_message */) {});
-
     if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
         std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
         return -1;
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 40d06a8..2daea25 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <mutex>
 #include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
 #include "renderengine/ExternalTexture.h"
 
 #include <gui/LayerState.h>
@@ -39,6 +40,10 @@
     ResolvedComposerState() = default;
     ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
     std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+    uint32_t layerId = UNASSIGNED_LAYER_ID;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+    uint32_t touchCropId = UNASSIGNED_LAYER_ID;
 };
 
 struct TransactionState {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 292083b..73a7cae 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -25,20 +25,14 @@
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 
-struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener,
-                                                                 DeathRecipient {
-    explicit WindowInfosReportedListener(
-            size_t callbackCount,
-            const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                     SpHash<gui::IWindowInfosReportedListener>>&
-                    windowInfosReportedListeners)
+struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
+                                            IBinder::DeathRecipient {
+    WindowInfosReportedListenerInvoker(size_t callbackCount,
+                                       WindowInfosReportedListenerSet windowInfosReportedListeners)
           : mCallbacksPending(callbackCount),
-            mWindowInfosReportedListeners(windowInfosReportedListeners) {}
+            mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
 
     binder::Status onWindowInfosReported() override {
-        // TODO(b/222421815) There could potentially be callbacks that we don't need to wait for
-        // before calling the WindowInfosReportedListeners coming from InputWindowCommands. Filter
-        // the list of callbacks down to those from system server.
         if (--mCallbacksPending == 0) {
             for (const auto& listener : mWindowInfosReportedListeners) {
                 sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -54,9 +48,7 @@
 
 private:
     std::atomic<size_t> mCallbacksPending;
-    std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                       SpHash<gui::IWindowInfosReportedListener>>
-            mWindowInfosReportedListeners;
+    WindowInfosReportedListenerSet mWindowInfosReportedListeners;
 };
 
 void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
@@ -82,38 +74,76 @@
 }
 
 void WindowInfosListenerInvoker::windowInfosChanged(
-        const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
-        const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                 SpHash<gui::IWindowInfosReportedListener>>&
-                windowInfosReportedListeners) {
-    ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+        std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
+        WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall) {
+    reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
+    auto callListeners = [this, windowInfos = std::move(windowInfos),
+                          displayInfos = std::move(displayInfos),
+                          reportedListeners = std::move(reportedListeners)]() mutable {
+        ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+        {
+            std::scoped_lock lock(mListenersMutex);
+            for (const auto& [_, listener] : mWindowInfosListeners) {
+                windowInfosListeners.push_back(listener);
+            }
+        }
+
+        auto reportedInvoker =
+                sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners.size(),
+                                                             std::move(reportedListeners));
+
+        for (const auto& listener : windowInfosListeners) {
+            sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+            // linkToDeath is used here to ensure that the windowInfosReportedListeners
+            // are called even if one of the windowInfosListeners dies before
+            // calling onWindowInfosReported.
+            asBinder->linkToDeath(reportedInvoker);
+
+            auto status =
+                    listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
+            if (!status.isOk()) {
+                reportedInvoker->onWindowInfosReported();
+            }
+        }
+    };
+
     {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mWindowInfosListeners) {
-            windowInfosListeners.push_back(listener);
+        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);
+            return;
         }
+
+        mWindowInfosChangedDelayed = nullptr;
+        mActiveMessageCount++;
+    }
+    callListeners();
+}
+
+binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
+    std::function<void()> callListeners;
+
+    {
+        std::scoped_lock lock{mMessagesMutex};
+        mActiveMessageCount--;
+        if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
+            return binder::Status::ok();
+        }
+
+        mActiveMessageCount++;
+        callListeners = std::move(mWindowInfosChangedDelayed);
+        mWindowInfosChangedDelayed = nullptr;
     }
 
-    auto windowInfosReportedListener = windowInfosReportedListeners.empty()
-            ? nullptr
-            : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(),
-                                                    windowInfosReportedListeners);
-    for (const auto& listener : windowInfosListeners) {
-        sp<IBinder> asBinder = IInterface::asBinder(listener);
-
-        // linkToDeath is used here to ensure that the windowInfosReportedListeners
-        // are called even if one of the windowInfosListeners dies before
-        // calling onWindowInfosReported.
-        if (windowInfosReportedListener) {
-            asBinder->linkToDeath(windowInfosReportedListener);
-        }
-
-        auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
-                                                     windowInfosReportedListener);
-        if (windowInfosReportedListener && !status.isOk()) {
-            windowInfosReportedListener->onWindowInfosReported();
-        }
-    }
+    callListeners();
+    return binder::Status::ok();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index d60a9c4..bfe036e 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,34 +23,40 @@
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <binder/IBinder.h>
 #include <ftl/small_map.h>
+#include <gui/SpHash.h>
 #include <utils/Mutex.h>
 
 namespace android {
 
-class SurfaceFlinger;
+using WindowInfosReportedListenerSet =
+        std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+                           gui::SpHash<gui::IWindowInfosReportedListener>>;
 
-class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+                                   public IBinder::DeathRecipient {
 public:
     void addWindowInfosListener(sp<gui::IWindowInfosListener>);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
-    void windowInfosChanged(const std::vector<gui::WindowInfo>&,
-                            const std::vector<gui::DisplayInfo>&,
-                            const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                                                     SpHash<gui::IWindowInfosReportedListener>>&
-                                    windowInfosReportedListeners);
+    void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
+                            WindowInfosReportedListenerSet windowInfosReportedListeners,
+                            bool forceImmediateCall);
+
+    binder::Status onWindowInfosReported() override;
 
 protected:
     void binderDied(const wp<IBinder>& who) override;
 
 private:
-    struct WindowInfosReportedListener;
-
     std::mutex mListenersMutex;
 
     static constexpr size_t kStaticCapacity = 3;
     ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
+
+    std::mutex mMessagesMutex;
+    uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
+    std::function<void()> mWindowInfosChangedDelayed GUARDED_BY(mMessagesMutex);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index f27f53b..6074bb7 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -402,9 +402,8 @@
     SurfaceFlinger *flinger() { return mFlinger.get(); }
     scheduler::TestableScheduler *scheduler() { return mScheduler; }
 
-    // Allow reading display state without locking, as if called on the SF main thread.
-    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->onInitializeDisplays();
+    void initializeDisplays() {
+        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->initializeDisplays());
     }
 
     void setGlobalShadowSettings(FuzzedDataProvider *fdp) {
@@ -453,8 +452,7 @@
 
         LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
         mFlinger->dumpOffscreenLayersProto(layersProto);
-        LayersTraceProto layersTraceProto{};
-        mFlinger->dumpDisplayProto(layersTraceProto);
+        mFlinger->dumpDisplayProto();
 
         result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpHwc(result);
@@ -542,7 +540,7 @@
                 mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()),
                                         fdp->ConsumeBool());
 
-        onInitializeDisplays();
+        initializeDisplays();
         mFlinger->getPhysicalDisplayToken(physicalDisplayId);
 
         mFlinger->mStartPropertySetThread =
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index c088e7b..4304259 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -25,6 +25,7 @@
 #include <gui/WindowInfo.h>
 #include <renderengine/mock/FakeExternalTexture.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/Transform.h>
 
 #include <FuzzableDataspaces.h>
 #include <surfaceflinger_fuzzers_utils.h>
@@ -42,6 +43,7 @@
     void invokeEffectLayer();
     LayerCreationArgs createLayerCreationArgs(TestableSurfaceFlinger* flinger, sp<Client> client);
     Rect getFuzzedRect();
+    ui::Transform getFuzzedTransform();
     FrameTimelineInfo getFuzzedFrameTimelineInfo();
 
 private:
@@ -54,6 +56,12 @@
                 mFdp.ConsumeIntegral<int32_t>() /*bottom*/);
 }
 
+ui::Transform LayerFuzzer::getFuzzedTransform() {
+    return ui::Transform(mFdp.ConsumeIntegral<int32_t>() /*orientation*/,
+                         mFdp.ConsumeIntegral<int32_t>() /*width*/,
+                         mFdp.ConsumeIntegral<int32_t>() /*height*/);
+}
+
 FrameTimelineInfo LayerFuzzer::getFuzzedFrameTimelineInfo() {
     FrameTimelineInfo ftInfo;
     ftInfo.vsyncId = mFdp.ConsumeIntegral<int64_t>();
@@ -166,7 +174,7 @@
                               {mFdp.ConsumeIntegral<int32_t>(),
                                mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/,
                               mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(),
-                              mFdp.ConsumeBool());
+                              mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect());
     layerArea.render([]() {} /*drawLayers*/);
 
     if (!ownsHandle) {
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 3598308..e9add2e 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -138,6 +138,8 @@
   float requested_corner_radius = 56;
 
   RectProto destination_frame = 57;
+
+  uint32 original_id = 58;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 4c6a9cf..2c4eb10 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -40,6 +40,7 @@
     /* offset between real-time clock and elapsed time clock in nanoseconds.
        Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */
     fixed64 real_to_elapsed_time_offset_nanos = 3;
+    uint32 version = 4;
 }
 
 message TransactionTraceEntry {
@@ -47,18 +48,47 @@
     int64 vsync_id = 2;
     repeated TransactionState transactions = 3;
     repeated LayerCreationArgs added_layers = 4;
-    repeated int32 removed_layers = 5;
+    repeated uint32 destroyed_layers = 5;
     repeated DisplayState added_displays = 6;
     repeated int32 removed_displays = 7;
-    repeated int32 removed_layer_handles = 8;
+    repeated uint32 destroyed_layer_handles = 8;
+    bool displays_changed = 9;
+    repeated DisplayInfo displays = 10;
+}
+
+message DisplayInfo {
+    uint32 layer_stack = 1;
+    int32 display_id = 2;
+    int32 logical_width = 3;
+    int32 logical_height = 4;
+    Transform transform_inverse = 5;
+    Transform transform = 6;
+    bool receives_input = 7;
+    bool is_secure = 8;
+    bool is_primary = 9;
+    bool is_virtual = 10;
+    int32 rotation_flags = 11;
+    int32 transform_hint = 12;
+
 }
 
 message LayerCreationArgs {
-    int32 layer_id = 1;
+    uint32 layer_id = 1;
     string name = 2;
     uint32 flags = 3;
-    int32 parent_id = 4;
-    int32 mirror_from_id = 5;
+    uint32 parent_id = 4;
+    uint32 mirror_from_id = 5;
+    bool add_to_root = 6;
+    uint32 layer_stack_to_mirror = 7;
+}
+
+message Transform {
+    float dsdx = 1;
+    float dtdx = 2;
+    float dtdy = 3;
+    float dsdy = 4;
+    float tx = 5;
+    float ty = 6;
 }
 
 message TransactionState {
@@ -74,7 +104,7 @@
 
 // Keep insync with layer_state_t
 message LayerState {
-    int64 layer_id = 1;
+    uint32 layer_id = 1;
     // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
     // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
     // 32 bits and there's no nice way to put 64bit constants into .proto files.
@@ -164,8 +194,8 @@
     Matrix22 matrix = 11;
     float corner_radius = 12;
     uint32 background_blur_radius = 13;
-    int64 parent_id = 14;
-    int64 relative_parent_id = 15;
+    uint32 parent_id = 14;
+    uint32 relative_parent_id = 15;
 
     float alpha = 16;
     message Color3 {
@@ -220,14 +250,6 @@
     ColorTransformProto color_transform = 25;
     repeated BlurRegion blur_regions = 26;
 
-    message Transform {
-        float dsdx = 1;
-        float dtdx = 2;
-        float dtdy = 3;
-        float dsdy = 4;
-        float tx = 5;
-        float ty = 6;
-    }
     message WindowInfo {
         uint32 layout_params_flags = 1;
         int32 layout_params_type = 2;
@@ -236,7 +258,7 @@
         bool focusable = 5;
         bool has_wallpaper = 6;
         float global_scale_factor = 7;
-        int64 crop_layer_id = 8;
+        uint32 crop_layer_id = 8;
         bool replace_touchable_region_with_crop = 9;
         RectProto touchable_region_crop = 10;
         Transform transform = 11;
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 6d12aa7..62b539a 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -125,6 +125,9 @@
     ],
     cpp_std: "experimental",
     gnu_extensions: false,
+    data: [
+        ":SurfaceFlinger_test",
+    ],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index d71486f..f4a8f03 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/android_filesystem_config.h>
+#include <cstdint>
 #include <future>
 
 namespace android {
@@ -65,14 +66,14 @@
     }
 };
 
-std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
-                                                 std::vector<WindowInfo> windowInfos) {
-    for (WindowInfo windowInfo : windowInfos) {
+const WindowInfo* findMatchingWindowInfo(const WindowInfo& targetWindowInfo,
+                                         const std::vector<WindowInfo>& windowInfos) {
+    for (const WindowInfo& windowInfo : windowInfos) {
         if (windowInfo.token == targetWindowInfo.token) {
-            return windowInfo;
+            return &windowInfo;
         }
     }
-    return std::nullopt;
+    return nullptr;
 }
 
 TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
@@ -93,14 +94,14 @@
             .apply();
 
     auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+        return findMatchingWindowInfo(windowInfo, windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
 
     Transaction().reparent(surfaceControl, nullptr).apply();
 
     auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return !findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+        return !findMatchingWindowInfo(windowInfo, windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
 }
@@ -132,8 +133,7 @@
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
 
-    Rect touchableRegions(0, 0, 50, 50);
-    windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
+    windowInfo.addTouchableRegion({0, 0, 50, 50});
     Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
 
     auto windowIsPresentAndTouchableRegionMatches =
@@ -142,7 +142,10 @@
                 if (!foundWindowInfo) {
                     return false;
                 }
-                return foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion);
+
+                auto touchableRegion =
+                        foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion);
+                return touchableRegion.hasSameRects(windowInfo.touchableRegion);
             };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
 }
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 5f9214c..7355c35 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -20,6 +20,7 @@
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <unordered_map>
 
 #include <LayerTraceGenerator.h>
 #include <Tracing/TransactionProtoParser.h>
@@ -84,9 +85,9 @@
 std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
 
 struct LayerInfo {
-    int id;
+    int32_t id;
     std::string name;
-    int parent;
+    int32_t parent;
     int z;
     uint64_t curr_frame;
     float x;
@@ -143,16 +144,27 @@
     expectedLayers.reserve(static_cast<size_t>(expectedLastEntry.layers().layers_size()));
     for (int i = 0; i < expectedLastEntry.layers().layers_size(); i++) {
         auto layer = expectedLastEntry.layers().layers(i);
-        expectedLayers.push_back(getLayerInfoFromProto(layer));
+        LayerInfo layerInfo = getLayerInfoFromProto(layer);
+        expectedLayers.push_back(layerInfo);
     }
     std::sort(expectedLayers.begin(), expectedLayers.end(), compareById);
 
+    std::unordered_map<int32_t /* snapshotId*/, int32_t /*layerId*/> snapshotIdToLayerId;
     std::vector<LayerInfo> actualLayers;
     actualLayers.reserve(static_cast<size_t>(actualLastEntry.layers().layers_size()));
     for (int i = 0; i < actualLastEntry.layers().layers_size(); i++) {
         auto layer = actualLastEntry.layers().layers(i);
-        actualLayers.push_back(getLayerInfoFromProto(layer));
+        LayerInfo layerInfo = getLayerInfoFromProto(layer);
+        snapshotIdToLayerId[layerInfo.id] = static_cast<int32_t>(layer.original_id());
+        actualLayers.push_back(layerInfo);
     }
+
+    for (auto& layer : actualLayers) {
+        layer.id = snapshotIdToLayerId[layer.id];
+        auto it = snapshotIdToLayerId.find(layer.parent);
+        layer.parent = it == snapshotIdToLayerId.end() ? -1 : it->second;
+    }
+
     std::sort(actualLayers.begin(), actualLayers.end(), compareById);
 
     size_t i = 0;
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
index 9e4005c..296d2fd 100644
--- a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
index 16a91ee..ae54415 100644
--- a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
index 8356ae7..8d03df4 100644
--- a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
index cd62ab8..022861c 100644
--- a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 012a4ad..df3ffd2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -107,9 +107,9 @@
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
         "SurfaceFlinger_HdrOutputControlTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
+        "SurfaceFlinger_InitializeDisplaysTest.cpp",
         "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
         "SurfaceFlinger_NotifyPowerBoostTest.cpp",
-        "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
         "SurfaceFlinger_PowerHintTest.cpp",
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index abd7789..d26ef3c 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -489,7 +489,7 @@
 
     auto displayFrame0 = getDisplayFrame(0);
     EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
-    EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
+    EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown | JankType::DisplayHAL);
     EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
     EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
 }
@@ -2259,6 +2259,7 @@
     mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
     mFrameTimeline->setSfPresent(80, validPresentFence);
 
+    erroneousPresentFence2->signalForTest(2);
     validPresentFence->signalForTest(80);
 
     addEmptyDisplayFrame();
@@ -2268,14 +2269,14 @@
         EXPECT_EQ(displayFrame->getActuals().presentTime, 26);
         EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
         EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
-        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
     }
     {
         auto displayFrame = getDisplayFrame(1);
         EXPECT_EQ(displayFrame->getActuals().presentTime, 60);
         EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
         EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
-        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
     }
     {
         auto displayFrame = getDisplayFrame(2);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 77dc868..ddf3363 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -17,12 +17,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "FrontEnd/LayerHandle.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
 #include "LayerHierarchyTest.h"
-#include "gui/SurfaceComposerClient.h"
 
 #define UPDATE_AND_VERIFY(HIERARCHY)  \
     ({                                \
@@ -207,7 +204,8 @@
     reparentRelativeLayer(11, 2);
     UPDATE_AND_VERIFY(hierarchyBuilder);
 
-    reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+    // This calls setLayer
+    removeRelativeZ(11);
     UPDATE_AND_VERIFY(hierarchyBuilder);
 
     std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
@@ -418,7 +416,7 @@
     EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
 
     // remove relative parent so layer becomes onscreen again
-    reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+    removeRelativeZ(11);
     UPDATE_AND_VERIFY(hierarchyBuilder);
 
     expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index b9a6159..5b3c7ef 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -17,11 +17,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "FrontEnd/LayerHandle.h"
+#include "Client.h" // temporarily needed for LayerCreationArgs
+#include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
-#include "gui/SurfaceComposerClient.h"
 
 namespace android::surfaceflinger::frontend {
 
@@ -51,20 +50,21 @@
         createLayer(1221, 122);
     }
 
-    LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent,
-                                 wp<IBinder> mirror) {
-        LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+    LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+                                 uint32_t layerIdToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
         args.addToRoot = canBeRoot;
-        args.parentHandle = parent;
-        args.mirrorLayerHandle = mirror;
+        args.parentId = parentId;
+        args.layerIdToMirror = layerIdToMirror;
         return args;
     }
 
-    LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStack) {
-        LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+    LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
         args.addToRoot = true;
-        args.parentHandle.clear();
-        args.layerStackToMirror = layerStack;
+        args.layerStackToMirror = layerStackToMirror;
         return args;
     }
 
@@ -90,17 +90,14 @@
     }
 
     virtual void createRootLayer(uint32_t id) {
-        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
-        mHandles[id] = handle;
         std::vector<std::unique_ptr<RequestedLayerState>> layers;
         layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
         mLifecycleManager.addLayers(std::move(layers));
     }
 
     void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
-        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
-        mHandles[id] = handle;
         std::vector<std::unique_ptr<RequestedLayerState>> layers;
         layers.emplace_back(std::make_unique<RequestedLayerState>(
                 createDisplayMirrorArgs(/*id=*/id, layerStack)));
@@ -108,62 +105,56 @@
     }
 
     virtual void createLayer(uint32_t id, uint32_t parentId) {
-        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
-        mHandles[id] = handle;
         std::vector<std::unique_ptr<RequestedLayerState>> layers;
         layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
-                           /*mirror=*/nullptr)));
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
         mLifecycleManager.addLayers(std::move(layers));
     }
 
-    void reparentLayer(uint32_t id, uint32_t newParentId) {
+    std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
         transactions.back().states.push_back({});
-
-        if (newParentId == UNASSIGNED_LAYER_ID) {
-            transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
-        } else {
-            auto parentHandle = mHandles[newParentId];
-            transactions.back().states.front().state.parentSurfaceControlForChild =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
-                                             static_cast<int32_t>(newParentId), "Test");
-        }
+        transactions.back().states.front().parentId = newParentId;
         transactions.back().states.front().state.what = layer_state_t::eReparent;
-        transactions.back().states.front().state.surface = mHandles[id];
-        mLifecycleManager.applyTransactions(transactions);
+        transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
+        transactions.back().states.front().layerId = id;
+        return transactions;
+    }
+
+    void reparentLayer(uint32_t id, uint32_t newParentId) {
+        mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
+    }
+
+    std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().relativeParentId = relativeParentId;
+        transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+        transactions.back().states.front().layerId = id;
+        return transactions;
     }
 
     void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+        mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
+    }
+
+    void removeRelativeZ(uint32_t id) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
         transactions.back().states.push_back({});
-
-        if (relativeParentId == UNASSIGNED_LAYER_ID) {
-            transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
-        } else {
-            auto parentHandle = mHandles[relativeParentId];
-            transactions.back().states.front().state.relativeLayerSurfaceControl =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
-                                             static_cast<int32_t>(relativeParentId), "test");
-            transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
-        }
-        transactions.back().states.front().state.surface = mHandles[id];
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().layerId = id;
         mLifecycleManager.applyTransactions(transactions);
     }
 
-    virtual void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
-        auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
-        auto mirrorHandle =
-                (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
-
-        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
-        mHandles[id] = handle;
+    virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
         std::vector<std::unique_ptr<RequestedLayerState>> layers;
         layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
-                           /*mirror=*/mHandles[layerToMirror])));
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/layerIdToMirror)));
         mLifecycleManager.addLayers(std::move(layers));
     }
 
@@ -173,7 +164,7 @@
         transactions.back().states.push_back({});
         transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
         transactions.back().states.front().state.bgColor.a = alpha;
-        transactions.back().states.front().state.surface = mHandles[id];
+        transactions.back().states.front().layerId = id;
         mLifecycleManager.applyTransactions(transactions);
     }
 
@@ -196,16 +187,19 @@
                 mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     }
 
-    void setZ(uint32_t id, int32_t z) {
+    std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
         transactions.back().states.push_back({});
 
         transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         transactions.back().states.front().state.z = z;
-        mLifecycleManager.applyTransactions(transactions);
+        return transactions;
+    }
+
+    void setZ(uint32_t id, int32_t z) {
+        mLifecycleManager.applyTransactions(setZTransaction(id, z));
     }
 
     void setCrop(uint32_t id, const Rect& crop) {
@@ -214,8 +208,7 @@
         transactions.back().states.push_back({});
 
         transactions.back().states.front().state.what = layer_state_t::eCropChanged;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         transactions.back().states.front().state.crop = crop;
         mLifecycleManager.applyTransactions(transactions);
     }
@@ -228,8 +221,7 @@
         transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
         transactions.back().states.front().state.flags = flags;
         transactions.back().states.front().state.mask = mask;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         mLifecycleManager.applyTransactions(transactions);
     }
 
@@ -239,8 +231,7 @@
         transactions.back().states.push_back({});
 
         transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         transactions.back().states.front().state.color.a = static_cast<half>(alpha);
         mLifecycleManager.applyTransactions(transactions);
     }
@@ -257,8 +248,7 @@
         transactions.back().states.push_back({});
         transactions.back().states.front().state.what = layer_state_t::eColorChanged;
         transactions.back().states.front().state.color.rgb = rgb;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         mLifecycleManager.applyTransactions(transactions);
     }
 
@@ -268,8 +258,7 @@
         transactions.back().states.push_back({});
 
         transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
         mLifecycleManager.applyTransactions(transactions);
     }
@@ -280,8 +269,7 @@
         transactions.back().states.push_back({});
 
         transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
-        transactions.back().states.front().state.surface = mHandles[id];
-        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().layerId = id;
         transactions.back().states.front().state.windowInfoHandle =
                 sp<gui::WindowInfoHandle>::make();
         auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
@@ -291,7 +279,6 @@
     }
 
     LayerLifecycleManager mLifecycleManager;
-    std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 99c1d23..14b8e4b 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -17,25 +17,14 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "FrontEnd/LayerHandle.h"
 #include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
-#include "gui/SurfaceComposerClient.h"
+#include "LayerHierarchyTest.h"
+#include "TransactionState.h"
 
 using namespace android::surfaceflinger;
 
 namespace android::surfaceflinger::frontend {
 
-namespace {
-LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
-    LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
-    args.addToRoot = canBeRoot;
-    args.parentHandle = parent;
-    args.mirrorLayerHandle = mirror;
-    return args;
-}
-} // namespace
-
 // To run test:
 /**
  mp :libsurfaceflinger_unittest && adb sync; adb shell \
@@ -66,69 +55,24 @@
     std::unordered_set<uint32_t> mActualLayersDestroyed;
 };
 
-class LayerLifecycleManagerTest : public testing::Test {
+class LayerLifecycleManagerTest : public LayerHierarchyTestBase {
 protected:
     std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
-        return std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr));
+        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
+                                                                /*parent=*/UNASSIGNED_LAYER_ID,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
     }
 
     std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
-        mHandles[parentId] = sp<LayerHandle>::make(parentId);
         return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
-                                                                /*parent=*/mHandles[parentId],
-                                                                /*mirror=*/nullptr));
-    }
-
-    TransactionState reparentLayer(uint32_t id, uint32_t newParentId) {
-        TransactionState transaction;
-        transaction.states.push_back({});
-
-        if (newParentId == UNASSIGNED_LAYER_ID) {
-            transaction.states.front().state.parentSurfaceControlForChild = nullptr;
-        } else {
-            transaction.states.front().state.parentSurfaceControlForChild =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
-                                             sp<LayerHandle>::make(newParentId),
-                                             static_cast<int32_t>(newParentId), "Test");
-        }
-        transaction.states.front().state.what = layer_state_t::eReparent;
-        transaction.states.front().state.surface = sp<LayerHandle>::make(id);
-        return transaction;
-    }
-
-    TransactionState setLayer(uint32_t id, int32_t z) {
-        TransactionState transaction;
-        transaction.states.push_back({});
-        transaction.states.front().state.z = z;
-        transaction.states.front().state.what = layer_state_t::eLayerChanged;
-        transaction.states.front().state.surface = sp<LayerHandle>::make(id);
-        return transaction;
-    }
-
-    TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) {
-        TransactionState transaction;
-        transaction.states.push_back({});
-
-        if (relativeParentId == UNASSIGNED_LAYER_ID) {
-            transaction.states.front().state.relativeLayerSurfaceControl = nullptr;
-        } else {
-            transaction.states.front().state.relativeLayerSurfaceControl =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
-                                             sp<LayerHandle>::make(relativeParentId),
-                                             static_cast<int32_t>(relativeParentId), "Test");
-        }
-        transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged;
-        transaction.states.front().state.surface = sp<LayerHandle>::make(id);
-        return transaction;
+                                                                parentId,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
     }
 
     RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
                                                 uint32_t layerId) {
         return lifecycleManager.getLayerFromId(layerId);
     }
-
-    std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
 };
 
 TEST_F(LayerLifecycleManagerTest, addLayers) {
@@ -153,16 +97,7 @@
     std::vector<std::unique_ptr<RequestedLayerState>> layers;
     layers.emplace_back(rootLayer(1));
     lifecycleManager.addLayers(std::move(layers));
-
-    std::vector<TransactionState> transactions;
-    transactions.emplace_back();
-    transactions.back().states.push_back({});
-    transactions.back().states.front().state.z = 2;
-    transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
-    sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
-    transactions.back().states.front().state.surface = handle;
-    lifecycleManager.applyTransactions(transactions);
-    transactions.clear();
+    lifecycleManager.applyTransactions(setZTransaction(1, 2));
 
     auto& managedLayers = lifecycleManager.getLayers();
     ASSERT_EQ(managedLayers.size(), 1u);
@@ -177,11 +112,12 @@
     EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
 
     // apply transactions that do not affect the hierarchy
+    std::vector<TransactionState> transactions;
     transactions.emplace_back();
     transactions.back().states.push_back({});
     transactions.back().states.front().state.backgroundBlurRadius = 22;
     transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
-    transactions.back().states.front().state.surface = handle;
+    transactions.back().states.front().layerId = 1;
     lifecycleManager.applyTransactions(transactions);
     EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     lifecycleManager.commitChanges();
@@ -232,7 +168,7 @@
     listener->expectLayersAdded({1, 2, 3});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
     lifecycleManager.commitChanges();
     listener->expectLayersAdded({});
     listener->expectLayersDestroyed({});
@@ -257,7 +193,7 @@
     listener->expectLayersAdded({1, 2, 3, 4});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
     lifecycleManager.onHandlesDestroyed({3});
     lifecycleManager.commitChanges();
     listener->expectLayersAdded({});
@@ -278,7 +214,7 @@
     listener->expectLayersAdded({1, 2, 3, 4});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
     lifecycleManager.onHandlesDestroyed({3, 4});
     lifecycleManager.commitChanges();
     listener->expectLayersAdded({});
@@ -300,9 +236,9 @@
     listener->expectLayersAdded({1, 2, 3, 4});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({makeRelative(4, 1)});
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
     EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
-    lifecycleManager.applyTransactions({reparentLayer(4, 2)});
+    lifecycleManager.applyTransactions(reparentLayerTransaction(4, 2));
     EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
 
     lifecycleManager.commitChanges();
@@ -325,9 +261,9 @@
     listener->expectLayersAdded({1, 2, 3, 4});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({makeRelative(4, 1)});
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
     EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
-    lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)});
+    lifecycleManager.applyTransactions(reparentLayerTransaction(4, UNASSIGNED_LAYER_ID));
     EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
 
     lifecycleManager.commitChanges();
@@ -350,9 +286,9 @@
     listener->expectLayersAdded({1, 2, 3, 4});
     listener->expectLayersDestroyed({});
 
-    lifecycleManager.applyTransactions({makeRelative(4, 1)});
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
     EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
-    lifecycleManager.applyTransactions({setLayer(4, 1)});
+    lifecycleManager.applyTransactions(setZTransaction(4, 1));
     EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
 
     lifecycleManager.commitChanges();
@@ -374,8 +310,7 @@
     transactions.back().states.push_back({});
     transactions.back().states.front().state.bgColor.a = 0.5;
     transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
-    sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
-    transactions.back().states.front().state.surface = handle;
+    transactions.back().states.front().layerId = 1;
     lifecycleManager.applyTransactions(transactions);
 
     auto& managedLayers = lifecycleManager.getLayers();
@@ -403,13 +338,12 @@
     transactions.back().states.push_back({});
     transactions.back().states.front().state.bgColor.a = 0.5;
     transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
-    sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
-    transactions.back().states.front().state.surface = handle;
+    transactions.back().states.front().layerId = 1;
     transactions.emplace_back();
     transactions.back().states.push_back({});
     transactions.back().states.front().state.bgColor.a = 0;
     transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
-    transactions.back().states.front().state.surface = handle;
+    transactions.back().states.front().layerId = 1;
 
     lifecycleManager.applyTransactions(transactions);
 
@@ -437,8 +371,7 @@
     transactions.back().states.push_back({});
     transactions.back().states.front().state.bgColor.a = 0.5;
     transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
-    sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
-    transactions.back().states.front().state.surface = handle;
+    transactions.back().states.front().layerId = 1;
     transactions.emplace_back();
     lifecycleManager.applyTransactions(transactions);
     lifecycleManager.onHandlesDestroyed({1});
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index db0b907..b8c4781 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -17,11 +17,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "FrontEnd/LayerHandle.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
 #include "FrontEnd/LayerSnapshotBuilder.h"
-#include "Layer.h"
 #include "LayerHierarchyTest.h"
 
 #define UPDATE_AND_VERIFY(BUILDER, ...)                                    \
@@ -268,7 +266,7 @@
     transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
     transactions.back().states.front().state.metadata = LayerMetadata();
     transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
-    transactions.back().states.front().state.surface = mHandles[1];
+    transactions.back().states.front().layerId = 1;
     transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
     mLifecycleManager.applyTransactions(transactions);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -297,8 +295,7 @@
             ANATIVEWINDOW_FRAME_RATE_EXACT;
     transactions.back().states.front().state.changeFrameRateStrategy =
             ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
-    transactions.back().states.front().state.surface = mHandles[11];
-    transactions.back().states.front().state.layerId = static_cast<int32_t>(11);
+    transactions.back().states.front().layerId = 11;
     mLifecycleManager.applyTransactions(transactions);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
 
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index f4d052d..63ed87b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1455,6 +1455,24 @@
         lr.name = "ExplicitExactOrMultiple 29.97 Hz";
         EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
     }
+
+    // Test that 29.97 will choose 30 if 59.94 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60);
+
+        lr.desiredRefreshRate = 29.97_Hz;
+        lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+        EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId());
+    }
+
+    // Test that 59.94 will choose 60 if 59.94 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode60, kMode30Frac, kMode30), kModeId60);
+
+        lr.desiredRefreshRate = 59.94_Hz;
+        lr.name = "ExplicitExactOrMultiple 59.94 Hz";
+        EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+    }
 }
 
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
@@ -2981,6 +2999,12 @@
     layers[0].name = "Test layer";
     layers[0].vote = LayerVoteType::Min;
     EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    constexpr FpsRanges kCappedAt60 = {{30_Hz, 90_Hz}, {30_Hz, 60_Hz}};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {DisplayModeId(kModeId60), kCappedAt60, kCappedAt60}));
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, frameRateIsCappedByPolicy) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index fd1fd47..e176546 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -119,7 +119,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(*mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -159,7 +159,7 @@
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
 
-    mFlinger.onActiveDisplayChanged(*mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
@@ -195,7 +195,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(*mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -238,7 +238,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(*mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
@@ -284,9 +284,43 @@
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
 }
 
-TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
+    if (!arg->getDesiredActiveMode()) {
+        *result_listener << "No desired active mode";
+        return false;
+    }
+
+    if (arg->getDesiredActiveMode()->modeOpt->modePtr->getId() != modeId) {
+        *result_listener << "Unexpected desired active mode " << modeId;
+        return false;
+    }
+
+    if (!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
+        *result_listener << "VsyncModulator did not shift to early phase";
+        return false;
+    }
+
+    return true;
+}
+
+MATCHER_P(ModeSettledTo, modeId, "") {
+    if (const auto desiredOpt = arg->getDesiredActiveMode()) {
+        *result_listener << "Unsettled desired active mode "
+                         << desiredOpt->modeOpt->modePtr->getId();
+        return false;
+    }
+
     ftl::FakeGuard guard(kMainThreadContext);
 
+    if (arg->getActiveMode().modePtr->getId() != modeId) {
+        *result_listener << "Settled to unexpected active mode " << modeId;
+        return false;
+    }
+
+    return true;
+}
+
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
     constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
     constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
 
@@ -309,13 +343,13 @@
 
     const auto& innerDisplay = mDisplay;
 
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
-    EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
-    EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+    mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
 
-    mFlinger.onActiveDisplayChanged(*innerDisplay);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -327,12 +361,8 @@
                                                   mock::createDisplayModeSpecs(kModeId60.value(),
                                                                                false, 0.f, 120.f)));
 
-    // Transition on the inner display.
-    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
-
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
@@ -342,31 +372,18 @@
 
     mFlinger.commit();
 
-    // Transition on the inner display.
-    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
-
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     mFlinger.commit();
 
-    // Transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+    mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
 
-    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()->modeOpt->modePtr->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(kOuterDisplayHwcId,
@@ -375,22 +392,13 @@
 
     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()->modeOpt->modePtr->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     mFlinger.commit();
 
-    // No transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
-
-    // Transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
similarity index 93%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index 98644aa..fc5f2b0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -22,9 +22,9 @@
 namespace android {
 namespace {
 
-class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+class InitializeDisplaysTest : public DisplayTransactionTest {};
 
-TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) {
     using Case = SimplePrimaryDisplayCase;
 
     // --------------------------------------------------------------------
@@ -52,7 +52,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.onInitializeDisplays();
+    FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays());
 
     // --------------------------------------------------------------------
     // Postconditions
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index d4b4434..f1a5fc4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -102,7 +102,6 @@
     }
 
     auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
-    auto& mutableVsyncModulator() { return *mVsyncModulator; }
     auto& mutableLayerHistory() { return mLayerHistory; }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6334ec8..fc9e653 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -419,10 +419,7 @@
         return mFlinger->setDisplayStateLocked(s);
     }
 
-    // Allow reading display state without locking, as if called on the SF main thread.
-    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->onInitializeDisplays();
-    }
+    void initializeDisplays() FTL_FAKE_GUARD(kMainThreadContext) { mFlinger->initializeDisplays(); }
 
     auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
 
@@ -505,10 +502,11 @@
         return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
     }
 
-    void onActiveDisplayChanged(const DisplayDevice& activeDisplay) {
+    void onActiveDisplayChanged(const DisplayDevice* inactiveDisplayPtr,
+                                const DisplayDevice& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
         ftl::FakeGuard guard(kMainThreadContext);
-        mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
+        mFlinger->onActiveDisplayChangedLocked(inactiveDisplayPtr, activeDisplay);
     }
 
     auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index c78148f..d4e2357 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -199,7 +199,7 @@
 
     void modulateVsync() {
         static_cast<void>(
-                mFlinger.mutableScheduler().mutableVsyncModulator().onRefreshRateChangeInitiated());
+                mFlinger.mutableScheduler().vsyncModulator().onRefreshRateChangeInitiated());
     }
 
     bool mHasListenerCallbacks = false;
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index b6427c0..3dea189 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -19,6 +19,7 @@
 #include <limits> // std::numeric_limits
 
 #include <gui/SurfaceComposerClient.h>
+#include "LayerProtoHelper.h"
 
 #include "Tracing/TransactionProtoParser.h"
 
@@ -27,7 +28,6 @@
 namespace android {
 
 TEST(TransactionProtoParserTest, parse) {
-    const sp<IBinder> layerHandle = sp<BBinder>::make();
     const sp<IBinder> displayHandle = sp<BBinder>::make();
     TransactionState t1;
     t1.originPid = 1;
@@ -37,7 +37,6 @@
     t1.postTime = 5;
 
     layer_state_t layer;
-    layer.layerId = 6;
     layer.what = std::numeric_limits<uint64_t>::max();
     layer.what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
     layer.x = 7;
@@ -48,10 +47,9 @@
     for (uint32_t i = 0; i < layerCount; i++) {
         ResolvedComposerState s;
         if (i == 1) {
-            layer.parentSurfaceControlForChild =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42,
-                                             "#42");
+            s.parentId = 42;
         }
+        s.layerId = 6 + i;
         s.state = layer;
         t1.states.emplace_back(s);
     }
@@ -72,18 +70,10 @@
 
     class TestMapper : public TransactionProtoParser::FlingerDataMapper {
     public:
-        sp<IBinder> layerHandle;
         sp<IBinder> displayHandle;
 
-        TestMapper(sp<IBinder> layerHandle, sp<IBinder> displayHandle)
-              : layerHandle(layerHandle), displayHandle(displayHandle) {}
+        TestMapper(sp<IBinder> displayHandle) : displayHandle(displayHandle) {}
 
-        sp<IBinder> getLayerHandle(int32_t id) const override {
-            return (id == 42) ? layerHandle : nullptr;
-        }
-        int64_t getLayerId(const sp<IBinder>& handle) const override {
-            return (handle == layerHandle) ? 42 : -1;
-        }
         sp<IBinder> getDisplayHandle(int32_t id) const {
             return (id == 43) ? displayHandle : nullptr;
         }
@@ -92,7 +82,7 @@
         }
     };
 
-    TransactionProtoParser parser(std::make_unique<TestMapper>(layerHandle, displayHandle));
+    TransactionProtoParser parser(std::make_unique<TestMapper>(displayHandle));
 
     proto::TransactionState proto = parser.toProto(t1);
     TransactionState t2 = parser.fromProto(proto);
@@ -105,8 +95,8 @@
     ASSERT_EQ(t1.states.size(), t2.states.size());
     ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
     ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
-    ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
-              t2.states[1].state.parentSurfaceControlForChild->getHandle());
+    ASSERT_EQ(t1.states[1].layerId, t2.states[1].layerId);
+    ASSERT_EQ(t1.states[1].parentId, t2.states[1].parentId);
 
     ASSERT_EQ(t1.displays.size(), t2.displays.size());
     ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 482c3a8..82aac7e 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -18,7 +18,13 @@
 #include <gtest/gtest.h>
 
 #include <gui/SurfaceComposerClient.h>
+#include <cstdint>
+#include "Client.h"
 
+#include <layerproto/LayerProtoHeader.h>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
+#include "Tracing/LayerTracing.h"
 #include "Tracing/RingBuffer.h"
 #include "Tracing/TransactionTracing.h"
 
@@ -42,14 +48,15 @@
     }
 
     void queueAndCommitTransaction(int64_t vsyncId) {
+        frontend::Update update;
         TransactionState transaction;
         transaction.id = static_cast<uint64_t>(vsyncId * 3);
         transaction.originUid = 1;
         transaction.originPid = 2;
         mTracing.addQueuedTransaction(transaction);
         std::vector<TransactionState> transactions;
-        transactions.emplace_back(transaction);
-        mTracing.addCommittedTransactions(transactions, vsyncId);
+        update.transactions.emplace_back(transaction);
+        mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
         flush(vsyncId);
     }
 
@@ -57,13 +64,25 @@
                      const std::vector<TransactionState>& expectedTransactions,
                      int64_t expectedVsyncId) {
         EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
-        EXPECT_EQ(actualProto.transactions().size(),
+        ASSERT_EQ(actualProto.transactions().size(),
                   static_cast<int32_t>(expectedTransactions.size()));
         for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
             EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
                       expectedTransactions[i].originPid);
         }
     }
+
+    LayerCreationArgs getLayerCreationArgs(uint32_t layerId, uint32_t parentId,
+                                           uint32_t layerIdToMirror, uint32_t flags,
+                                           bool addToRoot) {
+        LayerCreationArgs args;
+        args.sequence = layerId;
+        args.parentId = parentId;
+        args.layerIdToMirror = layerIdToMirror;
+        args.flags = flags;
+        args.addToRoot = addToRoot;
+        return args;
+    }
 };
 
 TEST_F(TransactionTracingTest, addTransactions) {
@@ -79,57 +98,59 @@
 
     // Split incoming transactions into two and commit them in reverse order to test out of order
     // commits.
-    std::vector<TransactionState> firstTransactionSet =
-            std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
     int64_t firstTransactionSetVsyncId = 42;
-    mTracing.addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+    frontend::Update firstUpdate;
+    firstUpdate.transactions =
+            std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+    mTracing.addCommittedTransactions(firstTransactionSetVsyncId, 0, firstUpdate, {}, false);
 
     int64_t secondTransactionSetVsyncId = 43;
-    std::vector<TransactionState> secondTransactionSet =
+    frontend::Update secondUpdate;
+    secondUpdate.transactions =
             std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
-    mTracing.addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+    mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
     flush(secondTransactionSetVsyncId);
 
     proto::TransactionTraceFile proto = writeToProto();
-    EXPECT_EQ(proto.entry().size(), 2);
-    verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
-    verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
+    ASSERT_EQ(proto.entry().size(), 2);
+    verifyEntry(proto.entry(0), firstUpdate.transactions, firstTransactionSetVsyncId);
+    verifyEntry(proto.entry(1), secondUpdate.transactions, secondTransactionSetVsyncId);
 }
 
 class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        // add layers
         mTracing.setBufferSize(SMALL_BUFFER_SIZE);
-        const sp<IBinder> fakeLayerHandle = sp<BBinder>::make();
-        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
-                              123 /* flags */, -1 /* parentId */);
-        const sp<IBinder> fakeChildLayerHandle = sp<BBinder>::make();
-        mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
-                              456 /* flags */, mParentLayerId);
 
-        // add some layer transaction
+        // add layers and add some layer transaction
         {
+            frontend::Update update;
+            update.layerCreationArgs.emplace_back(std::move(
+                    getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+                                         /*addToRoot=*/true)));
+            update.layerCreationArgs.emplace_back(std::move(
+                    getLayerCreationArgs(mChildLayerId, mParentLayerId,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
+                                         /*addToRoot=*/true)));
             TransactionState transaction;
             transaction.id = 50;
             ResolvedComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            layerState.layerId = mParentLayerId;
             layerState.state.what = layer_state_t::eLayerChanged;
             layerState.state.z = 42;
             transaction.states.emplace_back(layerState);
             ResolvedComposerState childState;
-            childState.state.surface = fakeChildLayerHandle;
+            childState.layerId = mChildLayerId;
             childState.state.what = layer_state_t::eLayerChanged;
             childState.state.z = 43;
             transaction.states.emplace_back(childState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
+            update.transactions.emplace_back(transaction);
             VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
-            mTracing.onLayerAddedToDrawingState(mParentLayerId, VSYNC_ID_FIRST_LAYER_CHANGE);
-            mTracing.onLayerAddedToDrawingState(mChildLayerId, VSYNC_ID_FIRST_LAYER_CHANGE);
-            mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(VSYNC_ID_FIRST_LAYER_CHANGE, 0, update, {}, false);
+
             flush(VSYNC_ID_FIRST_LAYER_CHANGE);
         }
 
@@ -139,17 +160,17 @@
             TransactionState transaction;
             transaction.id = 51;
             ResolvedComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            layerState.layerId = mParentLayerId;
             layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
             layerState.state.z = 41;
             layerState.state.x = 22;
             transaction.states.emplace_back(layerState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
+            frontend::Update update;
+            update.transactions.emplace_back(transaction);
             VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
-            mTracing.addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(VSYNC_ID_SECOND_LAYER_CHANGE, 0, update, {}, false);
             flush(VSYNC_ID_SECOND_LAYER_CHANGE);
         }
 
@@ -163,8 +184,8 @@
         queueAndCommitTransaction(++mVsyncId);
     }
 
-    int mParentLayerId = 1;
-    int mChildLayerId = 2;
+    uint32_t mParentLayerId = 1;
+    uint32_t mChildLayerId = 2;
     int64_t mVsyncId = 0;
     int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
     int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
@@ -232,42 +253,42 @@
 class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        // add layers
         mTracing.setBufferSize(SMALL_BUFFER_SIZE);
-        const sp<IBinder> fakeLayerHandle = sp<BBinder>::make();
-        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
-                              123 /* flags */, -1 /* parentId */);
-        const sp<IBinder> fakeMirrorLayerHandle = sp<BBinder>::make();
-        mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
-                                    mLayerId);
-        mTracing.onLayerAddedToDrawingState(mLayerId, mVsyncId);
-        mTracing.onLayerAddedToDrawingState(mMirrorLayerId, mVsyncId);
 
-        // add some layer transaction
+        // add layers and some layer transaction
         {
+            frontend::Update update;
+            update.layerCreationArgs.emplace_back(
+                    getLayerCreationArgs(mLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+                                         /*addToRoot=*/true));
+            update.layerCreationArgs.emplace_back(
+                    getLayerCreationArgs(mMirrorLayerId, UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/mLayerId, /*flags=*/0,
+                                         /*addToRoot=*/false));
+
             TransactionState transaction;
             transaction.id = 50;
             ResolvedComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            layerState.layerId = mLayerId;
             layerState.state.what = layer_state_t::eLayerChanged;
             layerState.state.z = 42;
             transaction.states.emplace_back(layerState);
             ResolvedComposerState mirrorState;
-            mirrorState.state.surface = fakeMirrorLayerHandle;
+            mirrorState.layerId = mMirrorLayerId;
             mirrorState.state.what = layer_state_t::eLayerChanged;
             mirrorState.state.z = 43;
             transaction.states.emplace_back(mirrorState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
-            mTracing.addCommittedTransactions(transactions, mVsyncId);
+            update.transactions.emplace_back(transaction);
+            mTracing.addCommittedTransactions(mVsyncId, 0, update, {}, false);
             flush(mVsyncId);
         }
     }
 
-    int mLayerId = 5;
-    int mMirrorLayerId = 55;
+    uint32_t mLayerId = 5;
+    uint32_t mMirrorLayerId = 55;
     int64_t mVsyncId = 0;
     int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
     int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
@@ -286,4 +307,24 @@
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
 }
+
+// Verify we can write the layers traces by entry to reduce mem pressure
+// on the system when generating large traces.
+TEST(LayerTraceTest, canStreamLayersTrace) {
+    LayersTraceFileProto inProto = LayerTracing::createTraceFileProto();
+    inProto.add_entry();
+    inProto.add_entry();
+
+    std::string output;
+    inProto.SerializeToString(&output);
+    LayersTraceFileProto inProto2 = LayerTracing::createTraceFileProto();
+    inProto2.add_entry();
+    std::string output2;
+    inProto2.SerializeToString(&output2);
+
+    LayersTraceFileProto outProto;
+    outProto.ParseFromString(output + output2);
+    // magic?
+    EXPECT_EQ(outProto.entry().size(), 3);
+}
 } // namespace android
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 1bff50d..dec3b20 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -514,7 +514,6 @@
         case VK_FORMAT_R8_UNORM:
             native_format = android::PIXEL_FORMAT_R_8;
             break;
-        // TODO: Do we need to query for VK_EXT_rgba10x6_formats here?
         case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
             native_format = android::PIXEL_FORMAT_RGBA_10101010;
             break;
@@ -805,9 +804,23 @@
         }
     }
 
-    // TODO query VK_EXT_rgba10x6_formats support
+    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)) {
+    if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
         all_formats.emplace_back(
             VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                                VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});