Merge "[layertracegenerator] stream layer trace to file" into udc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c71c4a0..097bc53 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,6 +14,7 @@
                include/powermanager/
                libs/binder/fuzzer/
                libs/binder/
+               libs/binderdebug/
                libs/binderthreadstate/
                libs/graphicsenv/
                libs/gui/
@@ -40,3 +41,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/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..be4ca43 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());
 }
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/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..3f19371 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*",
     ],
 }
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/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/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/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/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/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 9f54087..d563cfc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -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;
@@ -231,21 +233,8 @@
     return std::move(sockClient);
 }
 
-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 +243,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());
@@ -321,7 +314,7 @@
             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 +344,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);
 
@@ -651,6 +650,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";
@@ -668,7 +693,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));
@@ -681,6 +706,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;
 }
@@ -702,7 +731,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));
@@ -735,8 +764,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));
@@ -755,19 +783,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) {
@@ -1222,7 +1244,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);
@@ -1318,7 +1340,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));
     }
 };
@@ -1326,7 +1348,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)));
@@ -1342,7 +1364,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>();
@@ -1391,7 +1413,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: {
@@ -1470,7 +1492,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();
@@ -1575,7 +1597,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();
         }
@@ -1647,7 +1669,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;
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index d129661..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) {
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 ca5a117..a9736d5 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -116,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/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/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/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/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/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index 9b2dde7..e2023a6 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,21 +204,40 @@
      * 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 recovery_map destination of the decoded recovery map.
+     * @param compressed_jpegr_image compressed JPEGR image.
+     * @param dest destination of the uncompressed JPEGR image.
+     * @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,
                          jr_exif_ptr exif = nullptr,
                          jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR,
-                         jr_uncompressed_ptr recovery_map = nullptr);
+                         jr_uncompressed_ptr recovery_map = nullptr,
+                         jr_metadata_ptr metadata = nullptr);
 
     /*
     * Gets Info from JPEGR file without decoding it.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
index a381743..dd06fa2 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
@@ -45,7 +45,7 @@
     #define Endian_SwapBE16(n) (n)
 #endif
 
-struct jpegr_metadata;
+struct jpegr_metadata_struct;
 /*
  * Mutable data structure. Holds information for metadata.
  */
@@ -87,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.
@@ -156,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/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index f8763c6..e197bf0 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -107,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;
@@ -176,7 +176,7 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -235,7 +235,7 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -288,7 +288,7 @@
     return ERROR_JPEGR_RESOLUTION_MISMATCH;
   }
 
-  jpegr_metadata metadata;
+  jpegr_metadata_struct metadata;
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct map;
@@ -332,7 +332,8 @@
                             jr_uncompressed_ptr dest,
                             jr_exif_ptr exif,
                             jpegr_output_format output_format,
-                            jr_uncompressed_ptr recovery_map) {
+                            jr_uncompressed_ptr recovery_map,
+                            jr_metadata_ptr metadata) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -387,6 +388,18 @@
     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;
   }
@@ -417,13 +430,8 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  jpegr_metadata metadata;
-  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,
+                               dest));
   return NO_ERROR;
 }
 
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/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index 0a7d20a..be4b972 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -174,7 +174,7 @@
 }
 
 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 +189,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 +476,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..cf6a034 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -554,8 +554,8 @@
 
 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);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
@@ -573,8 +573,8 @@
   }
 
   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);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
       float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
@@ -592,8 +592,8 @@
   }
 
   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);
     for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
@@ -659,8 +659,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 +717,8 @@
 }
 
 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 };
 
   EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack());
   EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack());
@@ -981,8 +981,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/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/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/vts/OWNERS b/services/gpuservice/vts/OWNERS
index e789052..a63de1c 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 653544
+kocdemir@google.com
 paulthomson@google.com
 pbaiget@google.com
 lfy@google.com
diff --git a/services/inputflinger/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..3f1ff30 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,8 +54,7 @@
 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;
@@ -56,7 +69,7 @@
     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/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 13e4d0c..b12a009 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"
@@ -83,10 +85,11 @@
     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);
@@ -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) {
@@ -379,7 +381,7 @@
             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/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index dc0454d..f39e004 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -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/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 19a79d7..2ff26ed 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 {
@@ -39,18 +41,19 @@
     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;
         }
+        mScalingFactor = scalingFactor.value_or(1.0f);
         info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                             res * mScalingFactor);
+                             res.value_or(0.0f) * mScalingFactor);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 3d60bfd..ba08823 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);
@@ -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..043a895 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 {
@@ -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 96c163d..a943a03 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -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;
@@ -1160,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() {
@@ -2691,8 +2674,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();
@@ -2898,8 +2880,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();
@@ -2925,8 +2906,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");
@@ -2988,8 +2968,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;
@@ -3025,8 +3004,7 @@
             down = false;
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        const auto [x, y] = mPointerController->getPosition();
 
         mPointerGesture.currentGestureIdBits.clear();
         mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3193,8 +3171,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.
@@ -3509,8 +3487,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);
@@ -3548,9 +3525,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);
@@ -3597,8 +3573,7 @@
     }
     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;
@@ -3819,7 +3794,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..df66846 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>
 
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index d636d44..bc9f4c0 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -98,7 +98,6 @@
         case kGestureTypePinch:
             return handlePinch(when, readTime, gesture);
         default:
-            // TODO(b/251196347): handle more gesture types.
             return {};
     }
 }
@@ -111,8 +110,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 +135,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();
@@ -202,18 +200,19 @@
                                                      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 +223,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 +241,14 @@
         return {};
     }
 
-    float xCursorPosition, yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    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 +258,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 +294,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 +304,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 +320,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);
 
@@ -347,8 +344,8 @@
 [[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
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..ad311f9 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -45,9 +45,8 @@
     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 +58,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 +67,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..6d7bbeb 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -34,7 +34,7 @@
     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 +42,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;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 9c624ba..bbf7e8e 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) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index eb58d0b..cdd2439 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>
@@ -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());
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/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 546121d..5d7bf4b 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -225,14 +225,23 @@
 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/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/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index b25b731..3dd89ba 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -104,16 +104,6 @@
         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/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 5e7c259..8a45093 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -28,14 +28,9 @@
                              const LayerHierarchy::TraversalPath& path)
       : path(path) {
     static uint32_t sUniqueSequenceId = 0;
-    // 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++;
+    // 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;
     sequence = static_cast<int32_t>(state.id);
     name = state.name;
     textureName = state.textureName;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 7b1ff27..3997a0a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -68,7 +68,6 @@
     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;
 
@@ -87,6 +86,7 @@
 
 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,8 +120,16 @@
     void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
                           const Args& args);
 
-    std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
-                       LayerHierarchy::TraversalPathHash>
+    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>
             mIdToSnapshot;
     std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
     LayerSnapshot mRootSnapshot;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0f2af2f..9521371 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3168,6 +3168,7 @@
 }
 
 bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
     mDrawingState.surfaceDamageRegion = surfaceDamage;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2fb122c..f7e1969 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -848,6 +848,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,
@@ -885,10 +890,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);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index b5ae1a7..5c91b91 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -15,8 +15,6 @@
  */
 
 // 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"
@@ -250,88 +248,47 @@
     outRegion.bottom = proto.bottom();
 }
 
-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);
-            }
-        }
-    }
-
-    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);
-    }
-
-    // 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);
-        }
-    }
-
-    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) {
+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;
-    LayerProto* layerProto = mLayersProto.add_layers();
-    const frontend::RequestedLayerState& layer = *root.getLayer();
-    frontend::LayerSnapshot* snapshot = getSnapshot(path, layer);
-    LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
+    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) {
-        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);
+        if (variant == Variant::Attached || variant == Variant::Detached) {
+            layerProto->add_children(child->getLayer()->id);
         } else if (variant == Variant::Relative) {
-            mChildToRelativeParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
-            layerProto->add_relatives(childSnapshot->uniqueSequence);
+            layerProto->add_relatives(child->getLayer()->id);
         }
     }
 
-    if (mTraceFlags & LayerTracing::TRACE_COMPOSITION) {
-        auto it = mLegacyLayers.find(layer.id);
-        if (it != mLegacyLayers.end()) {
+    auto parent = root.getParent();
+    if (parent && parent->getLayer()) {
+        layerProto->set_parent(parent->getLayer()->id);
+    } else {
+        layerProto->set_parent(-1);
+    }
+
+    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);
+    }
+
+    if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+        auto it = legacyLayers.find(layer.id);
+        if (it != legacyLayers.end()) {
             it->second->writeCompositionStateToProto(layerProto);
         }
     }
@@ -341,10 +298,7 @@
         if (variant == Variant::Detached) {
             continue;
         }
-        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
-                                                                          child->getLayer()->id,
-                                                                          variant);
-        writeHierarchyToProto(*child, path);
+        writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags);
     }
 }
 
@@ -391,7 +345,6 @@
     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");
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index b84a49b..38d73f6 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -25,9 +25,6 @@
 #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 {
@@ -61,6 +58,11 @@
     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);
@@ -68,38 +70,5 @@
             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
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8ce479a..c9d17d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2641,7 +2641,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);
@@ -5789,9 +5791,17 @@
         return layersProto;
     }
 
-    return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, {},
-                                           traceFlags)
-            .generate(mLayerHierarchyBuilder.getHierarchy());
+    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;
 }
 
 google::protobuf::RepeatedPtrField<DisplayProto> SurfaceFlinger::dumpDisplayProto() const {
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 0ea421b..68a6cfc 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -138,10 +138,29 @@
               lifecycleManager.getGlobalChanges().string().c_str());
 
         lifecycleManager.commitChanges();
+        // write layers trace
+        auto tracingFlags = LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS;
+        std::unordered_set<uint64_t> stackIdsToSkip;
+        if ((tracingFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+            for (const auto& displayInfo : displayInfos) {
+                if (displayInfo.second.isVirtual) {
+                    stackIdsToSkip.insert(displayInfo.first.id);
+                }
+            }
+        }
 
-        LayersProto layersProto = LayerProtoFromSnapshotGenerator(snapshotBuilder, displayInfos, {},
-                                                                  layerTracing.getFlags())
-                                          .generate(hierarchyBuilder.getHierarchy());
+        const frontend::LayerHierarchy& root = hierarchyBuilder.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, snapshotBuilder, {},
+                                                    tracingFlags);
+        }
+
         auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
         layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
                             &layersProto, {}, &displayProtos);
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index e9add2e..3598308 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -138,8 +138,6 @@
   float requested_corner_radius = 56;
 
   RectProto destination_frame = 57;
-
-  uint32 original_id = 58;
 }
 
 message PositionProto {
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/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 7355c35..5f9214c 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -20,7 +20,6 @@
 #include <fstream>
 #include <iostream>
 #include <string>
-#include <unordered_map>
 
 #include <LayerTraceGenerator.h>
 #include <Tracing/TransactionProtoParser.h>
@@ -85,9 +84,9 @@
 std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
 
 struct LayerInfo {
-    int32_t id;
+    int id;
     std::string name;
-    int32_t parent;
+    int parent;
     int z;
     uint64_t curr_frame;
     float x;
@@ -144,27 +143,16 @@
     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);
-        LayerInfo layerInfo = getLayerInfoFromProto(layer);
-        expectedLayers.push_back(layerInfo);
+        expectedLayers.push_back(getLayerInfoFromProto(layer));
     }
     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);
-        LayerInfo layerInfo = getLayerInfoFromProto(layer);
-        snapshotIdToLayerId[layerInfo.id] = static_cast<int32_t>(layer.original_id());
-        actualLayers.push_back(layerInfo);
+        actualLayers.push_back(getLayerInfoFromProto(layer));
     }
-
-    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/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);