Merge "Use external texture for buffer release." into udc-dev
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 69ca793..95f5c03 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -317,12 +317,6 @@
     write /sys/kernel/debug/tracing/tracing_on 1
     write /sys/kernel/tracing/tracing_on 1
 
-on late-init && property:ro.boot.fastboot.boottrace=enabled
-    setprop debug.atrace.tags.enableflags 802922
-    setprop persist.traced.enable 0
-    write /sys/kernel/debug/tracing/tracing_on 1
-    write /sys/kernel/tracing/tracing_on 1
-
 # Only create the tracing instance if persist.mm_events.enabled
 # Attempting to remove the tracing instance after it has been created
 # will likely fail with EBUSY as it would be in use by traced_probes.
@@ -411,6 +405,103 @@
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
     chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
 
+# Handle hyp tracing instance
+on late-init && property:ro.boot.hypervisor.vm.supported=1
+
+# Hypervisor tracing instance doesn't support changing trace_clock
+    chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock
+    chmod 0440 /sys/kernel/tracing/hyp/trace_clock
+
+    chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb
+    chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb
+
+    chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on
+    chmod 0660 /sys/kernel/tracing/hyp/tracing_on
+
+# Tracing disabled by default
+    write /sys/kernel/debug/tracing/hyp/tracing_on 0
+    write /sys/kernel/tracing/hyp/tracing_on 0
+
+# Read and truncate the hyp trace.
+    chmod 0660 /sys/kernel/debug/tracing/hyp/trace
+    chmod 0660 /sys/kernel/tracing/hyp/trace
+
+# Read and truncate the per-CPU kernel trace.
+# Cannot use wildcards in .rc files. Update this if there is a phone with
+# TODO(b/249050813, ioffe): introduce per-cpu wildcard
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace
+    chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace
+    chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace
+
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page
+    chmod 0440 /sys/kernel/tracing/hyp/events/header_page
+
+# Hyp events start here
+
+# hyp_enter event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id
+
+# hyp_exit event
+    chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable
+    chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable
+# TODO(b/249050813): should this be handled in kernel?
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format
+    chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id
+    chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id
+
+
 on property:persist.debug.atrace.boottrace=1
     start boottrace
 
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 5f56531..ab81ecf 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -3,3 +3,4 @@
 gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
+smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a48313a..23cdd10 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2062,6 +2062,8 @@
                SEC_TO_MSEC(10));
     RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
     if (include_sensitive_info) {
         // Contains raw IP addresses, omit from reports on user builds.
         RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 34ea759..ce3d669 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -442,6 +442,16 @@
 static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
         const std::string& location, bool read_write, bool is_secondary_dex) {
     std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
+    if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) {
+        // ART Service doesn't use flock and instead assumes profile files are
+        // immutable, so ensure we don't open a file for writing when it's
+        // active.
+        // TODO(b/251921228): Normally installd isn't called at all in that
+        // case, but OTA is still an exception that uses the legacy code.
+        LOG(ERROR) << "Opening ref profile " << profile
+                   << " for writing is unsafe when ART Service is enabled.";
+        return invalid_unique_fd();
+    }
     return open_profile(
         uid,
         profile,
@@ -450,14 +460,13 @@
 }
 
 static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
-        const std::string& location, bool read_write, bool is_secondary_dex) {
+                                                        const std::string& location,
+                                                        bool is_secondary_dex) {
     std::string profile_path = create_reference_profile_path(package_name, location,
                                                              is_secondary_dex);
-    unique_fd ufd = open_profile(
-        uid,
-        profile_path,
-        read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
-        S_IRUSR | S_IWUSR | S_IRGRP);  // so that ART can also read it when apps run.
+    unique_fd ufd = open_profile(uid, profile_path, O_RDONLY,
+                                 S_IRUSR | S_IWUSR |
+                                         S_IRGRP); // so that ART can also read it when apps run.
 
     return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
         clear_profile(path);
@@ -1104,8 +1113,7 @@
             location = profile_name;
         }
     }
-    return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
-                                                 is_secondary_dex);
+    return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex);
 }
 
 // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..bf2c0d1 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
         // This is different from the normal installd. We only do the base
         // directory, the rest will be created on demand when each app is compiled.
         if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
-            LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+            PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
             return false;
         }
 
@@ -460,7 +460,7 @@
         // this tool will wipe the OTA artifact cache and try again (for robustness after
         // a failed OTA with remaining cache artifacts).
         if (access(apk_path, F_OK) != 0) {
-            LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+            PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
             return true;
         }
 
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
 namespace android {
 namespace installd {
 
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
 static void CloseDescriptor(int fd) {
     if (fd >= 0) {
         int result = close(fd);
@@ -82,6 +86,27 @@
     }
 }
 
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+    for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+        const char* fstype = kTryMountFsTypes[i];
+        int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+        if (mount_result == 0) {
+            return true;
+        }
+        if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+            // Only try the next fstype if mounting failed due to the current one
+            // being invalid.
+            LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+                         << fstype << " - trying " << kTryMountFsTypes[i + 1];
+        } else {
+            PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+                        << fstype;
+            return false;
+        }
+    }
+    __builtin_unreachable();
+}
+
 static void TryExtraMount(const char* name, const char* slot, const char* target) {
     std::string partition_name = StringPrintf("%s%s", name, slot);
 
@@ -91,12 +116,7 @@
         if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
             std::string path;
             if (dm.GetDmDevicePathByName(partition_name, &path)) {
-                int mount_result = mount(path.c_str(),
-                                         target,
-                                         "ext4",
-                                         MS_RDONLY,
-                                         /* data */ nullptr);
-                if (mount_result == 0) {
+                if (TryMountWithFstypes(path.c_str(), target)) {
                     return;
                 }
             }
@@ -105,12 +125,7 @@
 
     // Fall back and attempt a direct mount.
     std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
-    int mount_result = mount(block_device.c_str(),
-                             target,
-                             "ext4",
-                             MS_RDONLY,
-                             /* data */ nullptr);
-    UNUSED(mount_result);
+    (void)TryMountWithFstypes(block_device.c_str(), target);
 }
 
 // Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
 
 i=0
 while ((i<MAXIMUM_PACKAGES)) ; do
+  DONE=$(cmd otadexopt done)
+  if [ "$DONE" = "OTA complete." ] ; then
+    break
+  fi
+
   DEXOPT_PARAMS=$(cmd otadexopt next)
 
   /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
   PROGRESS=$(cmd otadexopt progress)
   print -u${STATUS_FD} "global_progress $PROGRESS"
 
-  DONE=$(cmd otadexopt done)
-  if [ "$DONE" = "OTA incomplete." ] ; then
-    sleep 1
-    i=$((i+1))
-    continue
-  fi
-  break
+  sleep 1
+  i=$((i+1))
 done
 
 DONE=$(cmd otadexopt done)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index d5ca725..5e8ef5d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -75,7 +75,7 @@
     ProcessState::initWithDriver("/dev/vndbinder");
 #endif
 #ifndef __ANDROID__
-    setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+    setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
 #endif
     sp<IServiceManager> sm = defaultServiceManager();
     fflush(stdout);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 07809e2..4da0cd6 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -228,8 +228,13 @@
 #endif  // !VENDORSERVICEMANAGER
 
 ServiceManager::Service::~Service() {
-    if (!hasClients) {
-        // only expected to happen on process death
+    if (hasClients) {
+        // only expected to happen on process death, we don't store the service
+        // name this late (it's in the map that holds this service), but if it
+        // is happening, we might want to change 'unlinkToDeath' to explicitly
+        // clear this bit so that we can abort in other cases, where it would
+        // mean inconsistent logic in servicemanager (unexpected and tested, but
+        // the original lazy service impl here had that bug).
         LOG(WARNING) << "a service was removed when there are clients";
     }
 }
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index ce30b41..165a868 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -561,6 +561,7 @@
         case OMX_IndexConfigPriority:                   return "ConfigPriority";
         case OMX_IndexConfigOperatingRate:              return "ConfigOperatingRate";
         case OMX_IndexParamConsumerUsageBits:           return "ParamConsumerUsageBits";
+        case OMX_IndexParamConsumerUsageBits64:         return "ParamConsumerUsageBits64";
         case OMX_IndexConfigLatency:                    return "ConfigLatency";
         default:                                        return asString((OMX_INDEXTYPE)i, def);
     }
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 0af40dd..5ddd719 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -105,6 +105,7 @@
     OMX_IndexConfigLowLatency,                      /**< reference: OMX_CONFIG_BOOLEANTYPE */
     OMX_IndexConfigAndroidTunnelPeek,               /**< reference: OMX_CONFIG_BOOLEANTYPE */
     OMX_IndexConfigAndroidTunnelPeekLegacyMode,     /**< reference: OMX_CONFIG_BOOLEANTYPE */
+    OMX_IndexParamConsumerUsageBits64,              /**< reference: OMX_PARAM_U64TYPE */
     OMX_IndexExtOtherEndUnused,
 
     /* Time configurations */
diff --git a/include/android/OWNERS b/include/android/OWNERS
new file mode 100644
index 0000000..38f9c55
--- /dev/null
+++ b/include/android/OWNERS
@@ -0,0 +1 @@
+per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index b5e6f65..c67310e 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -235,7 +235,7 @@
     KeyedVector<int32_t, Key*> mKeys;
     KeyboardType mType;
     std::string mLoadFileName;
-    bool mLayoutOverlayApplied;
+    bool mLayoutOverlayApplied = false;
 
     std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
     std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 68ebf75..de8ddca 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <unordered_map>
 
+#include <android-base/result.h>
 #include <android-base/thread_annotations.h>
 #include <android/sysprop/InputProperties.sysprop.h>
 #include <input/Input.h>
@@ -66,21 +67,27 @@
      * checkEnableMotionPredition: the function to check whether the prediction should run. Used to
      * provide an additional way of turning prediction on and off. Can be toggled at runtime.
      */
-    MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath = nullptr,
+    MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
                     std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
-    void record(const MotionEvent& event);
-    std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp);
+    /**
+     * Record the actual motion received by the view. This event will be used for calculating the
+     * predictions.
+     *
+     * @return empty result if the event was processed correctly, error if the event is not
+     * consistent with the previously recorded events.
+     */
+    android::base::Result<void> record(const MotionEvent& event);
+    std::unique_ptr<MotionEvent> predict(nsecs_t timestamp);
     bool isPredictionAvailable(int32_t deviceId, int32_t source);
 
 private:
     const nsecs_t mPredictionTimestampOffsetNanos;
-    const std::string mModelPath;
     const std::function<bool()> mCheckMotionPredictionEnabled;
 
     std::unique_ptr<TfLiteMotionPredictorModel> mModel;
-    // Buffers/events for each device seen by record().
-    std::unordered_map</*deviceId*/ int32_t, TfLiteMotionPredictorBuffers> mDeviceBuffers;
-    std::unordered_map</*deviceId*/ int32_t, MotionEvent> mLastEvents;
+
+    std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
+    std::optional<MotionEvent> mLastEvent;
 };
 
 } // namespace android
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 54e2851..7de551b41 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -99,7 +99,7 @@
 class TfLiteMotionPredictorModel {
 public:
     // Creates a model from an encoded Flatbuffer model.
-    static std::unique_ptr<TfLiteMotionPredictorModel> create(const char* modelPath);
+    static std::unique_ptr<TfLiteMotionPredictorModel> create();
 
     ~TfLiteMotionPredictorModel();
 
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 2e70304..51b97165 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -16,6 +16,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/scopeguard.h>
 #include <android-base/unique_fd.h>
 #include <binder/RecordedTransaction.h>
 #include <sys/mman.h>
@@ -176,13 +177,33 @@
     RecordedTransaction t;
     ChunkDescriptor chunk;
     const long pageSize = sysconf(_SC_PAGE_SIZE);
+    struct stat fileStat;
+    if (fstat(fd.get(), &fileStat) != 0) {
+        LOG(ERROR) << "Unable to get file information";
+        return std::nullopt;
+    }
+
+    off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+    if (fdCurrentPosition == -1) {
+        LOG(ERROR) << "Invalid offset in file descriptor.";
+        return std::nullopt;
+    }
     do {
+        if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
+            LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+            return std::nullopt;
+        }
         transaction_checksum_t checksum = 0;
         if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
             LOG(ERROR) << "Failed to read chunk descriptor.";
             return std::nullopt;
         }
-        off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+
+        fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+        if (fdCurrentPosition == -1) {
+            LOG(ERROR) << "Invalid offset in file descriptor.";
+            return std::nullopt;
+        }
         off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
         off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
 
@@ -194,14 +215,24 @@
         size_t chunkPayloadSize =
                 chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
 
+        if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
+            LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+            return std::nullopt;
+        }
+
         if (PADDING8(chunkPayloadSize) != 0) {
             LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
             return std::nullopt;
         }
 
-        transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>(
-                mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED,
-                     fd.get(), mmapPageAlignedStart));
+        size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
+        void* mappedMemory =
+                mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
+        auto mmap_guard = android::base::make_scope_guard(
+                [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
+
+        transaction_checksum_t* payloadMap =
+                reinterpret_cast<transaction_checksum_t*>(mappedMemory);
         payloadMap += mmapPayloadStartOffset /
                 sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
                                                 // page-alignment
@@ -218,7 +249,12 @@
             LOG(ERROR) << "Checksum failed.";
             return std::nullopt;
         }
-        lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+
+        fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+        if (fdCurrentPosition == -1) {
+            LOG(ERROR) << "Invalid offset in file descriptor.";
+            return std::nullopt;
+        }
 
         switch (chunk.chunkType) {
             case HEADER_CHUNK: {
@@ -255,7 +291,7 @@
                 break;
             default:
                 LOG(INFO) << "Unrecognized chunk.";
-                continue;
+                break;
         }
     } while (chunk.chunkType != END_CHUNK);
 
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ce6ef2b..233a8e4 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -90,16 +90,16 @@
     return mMaxIncomingThreads;
 }
 
-void RpcSession::setMaxOutgoingThreads(size_t threads) {
+void RpcSession::setMaxOutgoingConnections(size_t connections) {
     RpcMutexLockGuard _l(mMutex);
     LOG_ALWAYS_FATAL_IF(mStartedSetup,
                         "Must set max outgoing threads before setting up connections");
-    mMaxOutgoingThreads = threads;
+    mMaxOutgoingConnections = connections;
 }
 
 size_t RpcSession::getMaxOutgoingThreads() {
     RpcMutexLockGuard _l(mMutex);
-    return mMaxOutgoingThreads;
+    return mMaxOutgoingConnections;
 }
 
 bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
@@ -558,11 +558,11 @@
         return status;
     }
 
-    size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
-    ALOGI_IF(outgoingThreads != numThreadsAvailable,
+    size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections);
+    ALOGI_IF(outgoingConnections != numThreadsAvailable,
              "Server hints client to start %zu outgoing threads, but client will only start %zu "
              "because it is preconfigured to start at most %zu outgoing threads.",
-             numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+             numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections);
 
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once - the other side should be responsible for setting
@@ -571,10 +571,10 @@
     // any requests at all.
 
     // we've already setup one client
-    LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
-                   "incoming threads",
-                   outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
-    for (size_t i = 0; i + 1 < outgoingThreads; i++) {
+    LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: "
+                   "%zu) and %zu incoming threads",
+                   outgoingConnections, numThreadsAvailable, mMaxIncomingThreads);
+    for (size_t i = 0; i + 1 < outgoingConnections; i++) {
         if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
     }
 
@@ -932,7 +932,8 @@
                   (session->server()
                            ? "This is a server session, so see RpcSession::setMaxIncomingThreads "
                              "for the corresponding client"
-                           : "This is a client session, so see RpcSession::setMaxOutgoingThreads "
+                           : "This is a client session, so see "
+                             "RpcSession::setMaxOutgoingConnections "
                              "for this client or RpcServer::setMaxThreads for the corresponding "
                              "server"));
             return WOULD_BLOCK;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b27f102..2b0e5ba 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -557,13 +557,12 @@
             .parcelDataSize = static_cast<uint32_t>(data.dataSize()),
     };
 
-    constexpr size_t kWaitMaxUs = 1000000;
-    constexpr size_t kWaitLogUs = 10000;
-    size_t waitUs = 0;
-
     // Oneway calls have no sync point, so if many are sent before, whether this
     // is a twoway or oneway transaction, they may have filled up the socket.
     // So, make sure we drain them before polling
+    constexpr size_t kWaitMaxUs = 1000000;
+    constexpr size_t kWaitLogUs = 10000;
+    size_t waitUs = 0;
 
     iovec iovs[]{
             {&command, sizeof(RpcWireHeader)},
@@ -591,8 +590,9 @@
                 },
                 rpcFields->mFds.get());
         status != OK) {
-        // TODO(b/167966510): need to undo onBinderLeaving - we know the
-        // refcount isn't successfully transferred.
+        // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
+        // errors here, then we may need to undo the binder-sent counts for the transaction as
+        // well as for the binder objects in the Parcel
         return status;
     }
 
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 194254a..2b67f03 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -159,8 +159,8 @@
     LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
 
     auto rpcSession = RpcSession::make();
-    if (options.maxOutgoingThreads.has_value()) {
-        rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+    if (options.maxOutgoingConnections.has_value()) {
+        rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections);
     }
 
     if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index c78f870..55167a7 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -224,12 +224,12 @@
 //    }
 // Resources are cleaned up when the object is destroyed.
 //
-// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
-// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
-// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
-// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are
+// instantiated, depending on how many the service on the device is configured with.
+// Hence, only |maxOutgoingConnections| calls can be made simultaneously.
+// See also RpcSession::setMaxOutgoingConnections.
 struct RpcDelegateServiceManagerOptions {
-    std::optional<size_t> maxOutgoingThreads;
+    std::optional<size_t> maxOutgoingConnections;
 };
 sp<IServiceManager> createRpcDelegateServiceManager(
         const RpcDelegateServiceManagerOptions& options);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 25193a3..1001b64 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -119,7 +119,10 @@
     [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
 
     /**
-     * This must be called before adding a client session.
+     * This must be called before adding a client session. This corresponds
+     * to the number of incoming connections to RpcSession objects in the
+     * server, which will correspond to the number of outgoing connections
+     * in client RpcSession objects.
      *
      * If this is not specified, this will be a single-threaded server.
      *
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 40faf2c..0750ccf 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -54,8 +54,6 @@
  */
 class RpcSession final : public virtual RefBase {
 public:
-    static constexpr size_t kDefaultMaxOutgoingThreads = 10;
-
     // Create an RpcSession with default configuration (raw sockets).
     static sp<RpcSession> make();
 
@@ -67,26 +65,30 @@
     /**
      * Set the maximum number of incoming threads allowed to be made (for things like callbacks).
      * By default, this is 0. This must be called before setting up this connection as a client.
-     * Server sessions will inherits this value from RpcServer.
+     * Server sessions will inherits this value from RpcServer. Each thread will serve a
+     * connection to the remote RpcSession.
      *
      * If this is called, 'shutdown' on this session must also be called.
      * Otherwise, a threadpool will leak.
      *
-     * TODO(b/189955605): start these dynamically
+     * TODO(b/189955605): start these lazily - currently all are started
      */
     void setMaxIncomingThreads(size_t threads);
     size_t getMaxIncomingThreads();
 
     /**
-     * Set the maximum number of outgoing threads allowed to be made.
-     * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
-     * connection as a client.
+     * Set the maximum number of outgoing connections allowed to be made.
+     * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up
+     * this connection as a client.
      *
-     * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
-     * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
-     * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+     * For an RpcSession client, if you are connecting to a server which starts N threads,
+     * then this must be set to >= N. If you set the maximum number of outgoing connections
+     * to 1, but the server requests 10, then it would be considered an error. If you set a
+     * maximum number of connections to 10, and the server requests 1, then only 1 will be
+     * created. This API is used to limit the amount of resources a server can request you
+     * create.
      */
-    void setMaxOutgoingThreads(size_t threads);
+    void setMaxOutgoingConnections(size_t connections);
     size_t getMaxOutgoingThreads();
 
     /**
@@ -219,6 +221,8 @@
     friend RpcState;
     explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
 
+    static constexpr size_t kDefaultMaxOutgoingConnections = 10;
+
     // internal version of setProtocolVersion that
     // optionally skips the mStartedSetup check
     [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted);
@@ -368,7 +372,7 @@
 
     bool mStartedSetup = false;
     size_t mMaxIncomingThreads = 0;
-    size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
+    size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections;
     std::optional<uint32_t> mProtocolVersion;
     FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
 
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 42d226b..a157792 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -126,11 +126,11 @@
 void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
                                                 ARpcSession_FileDescriptorTransportMode mode);
 
-// Sets the maximum number of incoming threads.
+// Sets the maximum number of incoming threads, to service connections.
 void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
 
-// Sets the maximum number of outgoing threads.
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads);
+// Sets the maximum number of outgoing connections.
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections);
 
 // Decrements the refcount of the underlying RpcSession object.
 void ARpcSession_free(ARpcSession* session);
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index daff8c1..a167f23 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -265,8 +265,8 @@
     session->setMaxIncomingThreads(threads);
 }
 
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) {
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) {
     auto session = handleToStrongPointer<RpcSession>(handle);
-    session->setMaxOutgoingThreads(threads);
+    session->setMaxOutgoingConnections(connections);
 }
 }
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index f68612c..d833b83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,11 +26,11 @@
 
 #pragma once
 
+#include <android/binder_status.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
-
-#include <android/binder_status.h>
+#include <uchar.h>
 
 struct AIBinder;
 typedef struct AIBinder AIBinder;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 5b2532a..882f1d6 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -52,7 +52,7 @@
 constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
 constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
 
-constexpr unsigned int kShutdownWaitTime = 10;
+constexpr unsigned int kShutdownWaitTime = 11;
 constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
 
 class MyTestFoo : public IFoo {
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index afd414a..d36ebac 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -21,6 +21,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -72,6 +73,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -129,6 +131,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    product_available: true,
 
     // Currently necessary for host builds
     // TODO(b/31559095): bionic on host should define this
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 0b517cf..28c5390 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -75,11 +75,14 @@
         };
     }
 
-    /// Sets the maximum number of outgoing threads.
-    pub fn set_max_outgoing_threads(&self, threads: usize) {
+    /// Sets the maximum number of outgoing connections.
+    pub fn set_max_outgoing_connections(&self, connections: usize) {
         // SAFETY - Only passes the 'self' pointer as an opaque handle.
         unsafe {
-            binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads)
+            binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
+                self.as_ptr(),
+                connections,
+            )
         };
     }
 
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 6f686fb..5557168 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -209,8 +209,8 @@
     }
 
     /// Mark this binder object with local stability, which is vendor if we are
-    /// building for the VNDK and system otherwise.
-    #[cfg(any(vendor_ndk, android_vndk))]
+    /// building for android_vendor and system otherwise.
+    #[cfg(android_vendor)]
     fn mark_local_stability(&mut self) {
         unsafe {
             // Safety: Self always contains a valid `AIBinder` pointer, so
@@ -220,8 +220,8 @@
     }
 
     /// Mark this binder object with local stability, which is vendor if we are
-    /// building for the VNDK and system otherwise.
-    #[cfg(not(any(vendor_ndk, android_vndk)))]
+    /// building for android_vendor and system otherwise.
+    #[cfg(not(android_vendor))]
     fn mark_local_stability(&mut self) {
         unsafe {
             // Safety: Self always contains a valid `AIBinder` pointer, so
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 464da60..77a5fa8 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -66,7 +66,7 @@
 void initHostRpcServiceManagerOnce() {
     static std::once_flag gSmOnce;
     std::call_once(gSmOnce, [] {
-        setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+        setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
     });
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 955c650..8974ad7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -507,7 +507,13 @@
     }
 
     EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+
+    // b/268232063 - succeeds ~0.08% of the time
+    {
+        auto ret = IPCThreadState::self()->freeze(pid, true, 0);
+        EXPECT_TRUE(ret == -EAGAIN || ret == OK);
+    }
+
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
     EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 
@@ -1370,7 +1376,7 @@
         }));
     }
 
-    data.writeInt32(100);
+    data.writeInt32(500);
     // Give a chance for all threads to be used
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
 
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 84c93dd..b520f4f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -237,9 +237,13 @@
             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;
 }
@@ -350,7 +354,7 @@
     for (const auto& session : sessions) {
         CHECK(session->setProtocolVersion(clientVersion));
         session->setMaxIncomingThreads(options.numIncomingConnections);
-        session->setMaxOutgoingThreads(options.numOutgoingConnections);
+        session->setMaxOutgoingConnections(options.numOutgoingConnections);
         session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
 
         switch (socketType) {
@@ -435,8 +439,7 @@
     for (auto& t : ts) t.join();
 }
 
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
-                                        size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
     size_t epochMsBefore = epochMillis();
 
     std::vector<std::thread> ts;
@@ -462,7 +465,7 @@
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -475,7 +478,7 @@
     constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
     auto proc = createRpcTestSocketServerProcess(
             {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
-    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
 }
 
 TEST_P(BinderRpc, ThreadingStressTest) {
@@ -483,9 +486,9 @@
         GTEST_SKIP() << "This test requires multiple threads";
     }
 
-    constexpr size_t kNumClientThreads = 10;
-    constexpr size_t kNumServerThreads = 10;
-    constexpr size_t kNumCalls = 100;
+    constexpr size_t kNumClientThreads = 5;
+    constexpr size_t kNumServerThreads = 5;
+    constexpr size_t kNumCalls = 50;
 
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
 
@@ -544,6 +547,8 @@
         GTEST_SKIP() << "This test requires multiple threads";
     }
 
+    constexpr size_t kNumServerThreads = 3;
+
     // This test forces a oneway transaction to be queued by issuing two
     // `blockingSendFdOneway` calls, then drains the queue by issuing two
     // `blockingRecvFd` calls.
@@ -552,7 +557,7 @@
     // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
 
     auto proc = createRpcTestSocketServerProcess({
-            .numThreads = 3,
+            .numThreads = kNumServerThreads,
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
                     {RpcSession::FileDescriptorTransportMode::UNIX},
@@ -573,6 +578,8 @@
     EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
     CHECK(android::base::ReadFdToString(fdB.get(), &result));
     EXPECT_EQ(result, "b");
+
+    saturateThreadPool(kNumServerThreads, proc.rootIface);
 }
 
 TEST_P(BinderRpc, OnewayCallQueueing) {
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index b3bb5eb..63b56a3 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -45,9 +45,13 @@
             std::to_string(serverVersion);
     if (singleThreaded) {
         ret += "_single_threaded";
+    } else {
+        ret += "_multi_threaded";
     }
     if (noKernel) {
         ret += "_no_kernel";
+    } else {
+        ret += "_with_kernel";
     }
     return ret;
 }
@@ -71,7 +75,7 @@
         auto session = android::RpcSession::make(std::move(factory));
 
         EXPECT_TRUE(session->setProtocolVersion(clientVersion));
-        session->setMaxOutgoingThreads(options.numOutgoingConnections);
+        session->setMaxOutgoingConnections(options.numOutgoingConnections);
         session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
 
         status = session->setupPreconnectedClient({}, [&]() {
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index 8ea948c..a881582 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -104,3 +104,42 @@
     defaults: ["binder_fuzz_defaults"],
     srcs: ["MemoryDealerFuzz.cpp"],
 }
+
+cc_fuzz {
+    name: "binder_recordedTransactionFileFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["RecordedTransactionFileFuzz.cpp"],
+    corpus: [
+        "recorded_transaction_corpus/*",
+    ],
+}
+
+cc_fuzz {
+    name: "binder_recordedTransactionFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["RecordedTransactionFuzz.cpp"],
+    target: {
+        android: {
+            shared_libs: [
+                "libcutils",
+                "libutils",
+                "libbase",
+                "libbinder",
+            ],
+            static_libs: ["libbinder_random_parcel"],
+        },
+        host: {
+            static_libs: [
+                "libcutils",
+                "liblog",
+                "libutils",
+                "libbase",
+                "libbinder",
+                "libbinder_random_parcel",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
new file mode 100644
index 0000000..73790fa
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <filesystem>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::FILE* intermediateFile = std::tmpfile();
+    fwrite(data, sizeof(uint8_t), size, intermediateFile);
+    rewind(intermediateFile);
+    int fileNumber = fileno(intermediateFile);
+
+    android::base::unique_fd fd(fileNumber);
+
+    auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
+
+    std::fclose(intermediateFile);
+
+    if (transaction.has_value()) {
+        intermediateFile = std::tmpfile();
+
+        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+        std::fclose(intermediateFile);
+    }
+
+    return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
new file mode 100644
index 0000000..943fb9f
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <fuzzbinder/random_parcel.h>
+#include <filesystem>
+#include <string>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::fillRandomParcel;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+    android::String16 interfaceName =
+            android::String16(provider.ConsumeRandomLengthString().c_str());
+
+    uint32_t code = provider.ConsumeIntegral<uint32_t>();
+    uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+    time_t sec = provider.ConsumeIntegral<time_t>();
+    long nsec = provider.ConsumeIntegral<long>();
+    timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec};
+    android::status_t transactionStatus = provider.ConsumeIntegral<android::status_t>();
+
+    std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
+            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+
+    // same options so that FDs and binders could be shared in both Parcels
+    android::RandomParcelOptions options;
+
+    android::Parcel p0, p1;
+    fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+    fillRandomParcel(&p1, std::move(provider), &options);
+
+    auto transaction =
+            android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags,
+                                                                     timestamp, p0, p1,
+                                                                     transactionStatus);
+
+    if (transaction.has_value()) {
+        std::FILE* intermediateFile = std::tmpfile();
+        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+        std::fclose(intermediateFile);
+    }
+
+    return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
new file mode 100644
index 0000000..79442078
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
Binary files differ
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
new file mode 100644
index 0000000..658addb
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
Binary files differ
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 29924ff..96dcce1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -11,7 +11,7 @@
     name: "fakeservicemanager_defaults",
     host_supported: true,
     srcs: [
-        "ServiceManager.cpp",
+        "FakeServiceManager.cpp",
     ],
 
     shared_libs: [
@@ -28,7 +28,7 @@
 cc_library {
     name: "libfakeservicemanager",
     defaults: ["fakeservicemanager_defaults"],
-    export_include_dirs: ["include/fakeservicemanager"],
+    export_include_dirs: ["include"],
 }
 
 cc_test_host {
@@ -38,5 +38,5 @@
         "test_sm.cpp",
     ],
     static_libs: ["libgmock"],
-    local_include_dirs: ["include/fakeservicemanager"],
+    local_include_dirs: ["include"],
 }
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
similarity index 66%
rename from libs/fakeservicemanager/ServiceManager.cpp
rename to libs/fakeservicemanager/FakeServiceManager.cpp
index 1109ad8..3272bbc 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
 
 namespace android {
 
-ServiceManager::ServiceManager() {}
+FakeServiceManager::FakeServiceManager() {}
 
-sp<IBinder> ServiceManager::getService( const String16& name) const {
+sp<IBinder> FakeServiceManager::getService( const String16& name) const {
     // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
     return checkService(name);
 }
 
-sp<IBinder> ServiceManager::checkService( const String16& name) const {
+sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
     auto it = mNameToService.find(name);
     if (it == mNameToService.end()) {
         return nullptr;
@@ -33,7 +33,7 @@
     return it->second;
 }
 
-status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
                                 bool /*allowIsolated*/,
                                 int /*dumpsysFlags*/) {
     if (service == nullptr) {
@@ -43,7 +43,7 @@
     return NO_ERROR;
 }
 
-Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
     Vector<String16> services;
     for (auto const& [name, service] : mNameToService) {
         (void) service;
@@ -52,19 +52,19 @@
   return services;
 }
 
-IBinder* ServiceManager::onAsBinder() {
+IBinder* FakeServiceManager::onAsBinder() {
     return nullptr;
 }
 
-sp<IBinder> ServiceManager::waitForService(const String16& name) {
+sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
     return checkService(name);
 }
 
-bool ServiceManager::isDeclared(const String16& name) {
+bool FakeServiceManager::isDeclared(const String16& name) {
     return mNameToService.find(name) != mNameToService.end();
 }
 
-Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
     Vector<String16> out;
     const String16 prefix = name + String16("/");
     for (const auto& [registeredName, service] : mNameToService) {
@@ -76,38 +76,38 @@
     return out;
 }
 
-std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) {
     (void)name;
     return std::nullopt;
 }
 
-Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) {
     (void)apexName;
     return {};
 }
 
-std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo(
         const String16& name) {
     (void)name;
     return std::nullopt;
 }
 
-status_t ServiceManager::registerForNotifications(const String16&,
+status_t FakeServiceManager::registerForNotifications(const String16&,
                                                   const sp<LocalRegistrationCallback>&) {
     return INVALID_OPERATION;
 }
 
-status_t ServiceManager::unregisterForNotifications(const String16&,
+status_t FakeServiceManager::unregisterForNotifications(const String16&,
                                                 const sp<LocalRegistrationCallback>&) {
     return INVALID_OPERATION;
 }
 
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() {
     std::vector<IServiceManager::ServiceDebugInfo> ret;
     return ret;
 }
 
-void ServiceManager::clear() {
+void FakeServiceManager::clear() {
     mNameToService.clear();
 }
 }  // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
similarity index 96%
rename from libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index ba6bb7d..97add24 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -28,9 +28,9 @@
  * A local host simple implementation of IServiceManager, that does not
  * communicate over binder.
 */
-class ServiceManager : public IServiceManager {
+class FakeServiceManager : public IServiceManager {
 public:
-    ServiceManager();
+    FakeServiceManager();
 
     sp<IBinder> getService( const String16& name) const override;
 
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 8682c1c..6fc21c6 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -21,14 +21,14 @@
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
 
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
 
 using android::sp;
 using android::BBinder;
 using android::IBinder;
 using android::OK;
 using android::status_t;
-using android::ServiceManager;
+using android::FakeServiceManager;
 using android::String16;
 using android::IServiceManager;
 using testing::ElementsAre;
@@ -45,19 +45,19 @@
 }
 
 TEST(AddService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
 }
 
 TEST(AddService, SadNullBinder) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
 }
 
 TEST(AddService, HappyOverExistingService) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -65,7 +65,7 @@
 }
 
 TEST(AddService, HappyClearAddedService) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
     EXPECT_NE(sm->getService(String16("foo")), nullptr);
@@ -74,7 +74,7 @@
 }
 
 TEST(GetService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -84,13 +84,13 @@
 }
 
 TEST(GetService, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->getService(String16("foo")), nullptr);
 }
 
 TEST(ListServices, AllServices) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
@@ -109,13 +109,13 @@
 }
 
 TEST(WaitForService, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
 }
 
 TEST(WaitForService, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -125,13 +125,13 @@
 }
 
 TEST(IsDeclared, NonExistant) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
 
     EXPECT_FALSE(sm->isDeclared(String16("foo")));
 }
 
 TEST(IsDeclared, HappyHappy) {
-    auto sm = new ServiceManager();
+    auto sm = new FakeServiceManager();
     sp<IBinder> service = getBinder();
 
     EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index 347c4e0..1db8cbe 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,10 +1,4 @@
-abdolrashidi@google.com
-cclao@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 ianelliott@google.com
-lfy@google.com
 lpy@google.com
-romanl@google.com
-vantablack@google.com
-yuxinhu@google.com
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 99bf6ba..46fb068 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -15,8 +15,10 @@
  */
 
 // #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <gui/Choreographer.h>
+#include <gui/TraceUtils.h>
 #include <jni.h>
 
 #undef LOG_TAG
@@ -297,6 +299,8 @@
     mLastVsyncEventData = vsyncEventData;
     for (const auto& cb : callbacks) {
         if (cb.vsyncCallback != nullptr) {
+            ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
+                          vsyncEventData.preferredVsyncId());
             const ChoreographerFrameCallbackDataImpl frameCallbackData =
                     createFrameCallbackData(timestamp);
             registerStartTime();
@@ -306,8 +310,10 @@
                              cb.data);
             mInCallback = false;
         } else if (cb.callback64 != nullptr) {
+            ATRACE_FORMAT("AChoreographer_frameCallback64");
             cb.callback64(timestamp, cb.data);
         } else if (cb.callback != nullptr) {
+            ATRACE_FORMAT("AChoreographer_frameCallback");
             cb.callback(timestamp, cb.data);
         }
     }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0f138ca..2f5830d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1363,7 +1363,8 @@
         (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
         (mask & layer_state_t::eEnableBackpressure) ||
         (mask & layer_state_t::eIgnoreDestinationFrame) ||
-        (mask & layer_state_t::eLayerIsDisplayDecoration)) {
+        (mask & layer_state_t::eLayerIsDisplayDecoration) ||
+        (mask & layer_state_t::eLayerIsRefreshRateIndicator)) {
         s->what |= layer_state_t::eFlagsChanged;
     }
     s->flags &= ~mask;
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index 23f0921..76c60c2 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,6 +23,9 @@
 
 namespace android::gui {
 
+static_assert(VsyncEventData::kFrameTimelinesLength == 7,
+              "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)");
+
 int64_t VsyncEventData::preferredVsyncId() const {
     return frameTimelines[preferredFrameTimelineIndex].vsyncId;
 }
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 29fb989..6e3be5c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -160,6 +160,7 @@
         // This is needed to maintain compatibility for SurfaceView scaling behavior.
         // See SurfaceView scaling behavior for more details.
         eIgnoreDestinationFrame = 0x400,
+        eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR
     };
 
     enum {
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
 
 #define LOG_TAG "Surface"
 
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
 #include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
 
 namespace android {
 namespace view {
 
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+                                                       Parcel* _Nonnull parcel) {
+    int value;
+    int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+    if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+        ALOGE("Error: ANativeWindow is not backed by Surface");
+        return STATUS_BAD_VALUE;
+    }
+    // Use a android::view::Surface to parcelize the window
+    android::view::Surface shimSurface;
+    shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+    shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+    return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+        const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+    // Use a android::view::Surface to unparcel the window
+    android::view::Surface shimSurface;
+    status_t ret = shimSurface.readFromParcel(parcel);
+    if (ret != OK) {
+        ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+        return STATUS_BAD_VALUE;
+    }
+    auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+                                              shimSurface.surfaceControlHandle);
+    ANativeWindow* anw = surface.get();
+    ANativeWindow_acquire(anw);
+    *outWindow = anw;
+    return STATUS_OK;
+}
+
+#endif
+
 status_t Surface::writeToParcel(Parcel* parcel) const {
     return writeToParcel(parcel, false);
 }
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 7d11ef2..b4151c6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -35,7 +35,6 @@
 namespace android {
 namespace {
 
-const char DEFAULT_MODEL_PATH[] = "/system/etc/motion_predictor_model.fb";
 const int64_t PREDICTION_INTERVAL_NANOS =
         12500000 / 3; // TODO(b/266747937): Get this from the model.
 
@@ -62,48 +61,58 @@
 
 // --- MotionPredictor ---
 
-MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath,
+MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
                                  std::function<bool()> checkMotionPredictionEnabled)
       : mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
-        mModelPath(modelPath == nullptr ? DEFAULT_MODEL_PATH : modelPath),
         mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
 
-void MotionPredictor::record(const MotionEvent& event) {
+android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
+    if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
+        // We still have an active gesture for another device. The provided MotionEvent is not
+        // consistent the previous gesture.
+        LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but "
+                   << __func__ << " is called with " << event;
+        return android::base::Error()
+                << "Inconsistent event stream: still have an active gesture from device "
+                << mLastEvent->getDeviceId() << ", but received " << event;
+    }
     if (!isPredictionAvailable(event.getDeviceId(), event.getSource())) {
         ALOGE("Prediction not supported for device %d's %s source", event.getDeviceId(),
               inputEventSourceToString(event.getSource()).c_str());
-        return;
+        return {};
     }
 
     // Initialise the model now that it's likely to be used.
     if (!mModel) {
-        mModel = TfLiteMotionPredictorModel::create(mModelPath.c_str());
+        mModel = TfLiteMotionPredictorModel::create();
     }
 
-    TfLiteMotionPredictorBuffers& buffers =
-            mDeviceBuffers.try_emplace(event.getDeviceId(), mModel->inputLength()).first->second;
+    if (mBuffers == nullptr) {
+        mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+    }
 
     const int32_t action = event.getActionMasked();
-    if (action == AMOTION_EVENT_ACTION_UP) {
+    if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
         ALOGD_IF(isDebug(), "End of event stream");
-        buffers.reset();
-        return;
+        mBuffers->reset();
+        mLastEvent.reset();
+        return {};
     } else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
         ALOGD_IF(isDebug(), "Skipping unsupported %s action",
                  MotionEvent::actionToString(action).c_str());
-        return;
+        return {};
     }
 
     if (event.getPointerCount() != 1) {
         ALOGD_IF(isDebug(), "Prediction not supported for multiple pointers");
-        return;
+        return {};
     }
 
     const int32_t toolType = event.getPointerProperties(0)->toolType;
     if (toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS) {
         ALOGD_IF(isDebug(), "Prediction not supported for non-stylus tool: %s",
                  motionToolTypeToString(toolType));
-        return;
+        return {};
     }
 
     for (size_t i = 0; i <= event.getHistorySize(); ++i) {
@@ -111,100 +120,98 @@
             continue;
         }
         const PointerCoords* coords = event.getHistoricalRawPointerCoords(0, i);
-        buffers.pushSample(event.getHistoricalEventTime(i),
-                           {
-                                   .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
-                                   .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
-                                   .pressure = event.getHistoricalPressure(0, i),
-                                   .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT, 0,
-                                                                        i),
-                                   .orientation = event.getHistoricalOrientation(0, i),
-                           });
+        mBuffers->pushSample(event.getHistoricalEventTime(i),
+                             {
+                                     .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+                                     .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
+                                     .pressure = event.getHistoricalPressure(0, i),
+                                     .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT,
+                                                                          0, i),
+                                     .orientation = event.getHistoricalOrientation(0, i),
+                             });
     }
 
-    mLastEvents.try_emplace(event.getDeviceId())
-            .first->second.copyFrom(&event, /*keepHistory=*/false);
+    if (!mLastEvent) {
+        mLastEvent = MotionEvent();
+    }
+    mLastEvent->copyFrom(&event, /*keepHistory=*/false);
+    return {};
 }
 
-std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) {
-    std::vector<std::unique_ptr<MotionEvent>> predictions;
-
-    for (const auto& [deviceId, buffer] : mDeviceBuffers) {
-        if (!buffer.isReady()) {
-            continue;
-        }
-
-        LOG_ALWAYS_FATAL_IF(!mModel);
-        buffer.copyTo(*mModel);
-        LOG_ALWAYS_FATAL_IF(!mModel->invoke());
-
-        // Read out the predictions.
-        const std::span<const float> predictedR = mModel->outputR();
-        const std::span<const float> predictedPhi = mModel->outputPhi();
-        const std::span<const float> predictedPressure = mModel->outputPressure();
-
-        TfLiteMotionPredictorSample::Point axisFrom = buffer.axisFrom().position;
-        TfLiteMotionPredictorSample::Point axisTo = buffer.axisTo().position;
-
-        if (isDebug()) {
-            ALOGD("deviceId: %d", deviceId);
-            ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
-            ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
-            ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
-            ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
-            ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
-            ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
-            ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
-            ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
-            ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
-            ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
-        }
-
-        const MotionEvent& event = mLastEvents[deviceId];
-        bool hasPredictions = false;
-        std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
-        int64_t predictionTime = buffer.lastTimestamp();
-        const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
-
-        for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
-            const TfLiteMotionPredictorSample::Point point =
-                    convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
-            // TODO(b/266747654): Stop predictions if confidence is < some threshold.
-
-            ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
-            PointerCoords coords;
-            coords.clear();
-            coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
-            coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
-            // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
-            coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
-
-            predictionTime += PREDICTION_INTERVAL_NANOS;
-            if (i == 0) {
-                hasPredictions = true;
-                prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
-                                       event.getDisplayId(), INVALID_HMAC,
-                                       AMOTION_EVENT_ACTION_MOVE, event.getActionButton(),
-                                       event.getFlags(), event.getEdgeFlags(), event.getMetaState(),
-                                       event.getButtonState(), event.getClassification(),
-                                       event.getTransform(), event.getXPrecision(),
-                                       event.getYPrecision(), event.getRawXCursorPosition(),
-                                       event.getRawYCursorPosition(), event.getRawTransform(),
-                                       event.getDownTime(), predictionTime, event.getPointerCount(),
-                                       event.getPointerProperties(), &coords);
-            } else {
-                prediction->addSample(predictionTime, &coords);
-            }
-
-            axisFrom = axisTo;
-            axisTo = point;
-        }
-        // TODO(b/266747511): Interpolate to futureTime?
-        if (hasPredictions) {
-            predictions.push_back(std::move(prediction));
-        }
+std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
+    if (mBuffers == nullptr || !mBuffers->isReady()) {
+        return nullptr;
     }
-    return predictions;
+
+    LOG_ALWAYS_FATAL_IF(!mModel);
+    mBuffers->copyTo(*mModel);
+    LOG_ALWAYS_FATAL_IF(!mModel->invoke());
+
+    // Read out the predictions.
+    const std::span<const float> predictedR = mModel->outputR();
+    const std::span<const float> predictedPhi = mModel->outputPhi();
+    const std::span<const float> predictedPressure = mModel->outputPressure();
+
+    TfLiteMotionPredictorSample::Point axisFrom = mBuffers->axisFrom().position;
+    TfLiteMotionPredictorSample::Point axisTo = mBuffers->axisTo().position;
+
+    if (isDebug()) {
+        ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
+        ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
+        ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
+        ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
+        ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
+        ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
+        ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
+        ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
+        ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
+        ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
+    }
+
+    LOG_ALWAYS_FATAL_IF(!mLastEvent);
+    const MotionEvent& event = *mLastEvent;
+    bool hasPredictions = false;
+    std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
+    int64_t predictionTime = mBuffers->lastTimestamp();
+    const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
+
+    for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
+        const TfLiteMotionPredictorSample::Point point =
+                convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
+        // TODO(b/266747654): Stop predictions if confidence is < some threshold.
+
+        ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
+        PointerCoords coords;
+        coords.clear();
+        coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
+        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
+        // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
+        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
+
+        predictionTime += PREDICTION_INTERVAL_NANOS;
+        if (i == 0) {
+            hasPredictions = true;
+            prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
+                                   event.getDisplayId(), INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE,
+                                   event.getActionButton(), event.getFlags(), event.getEdgeFlags(),
+                                   event.getMetaState(), event.getButtonState(),
+                                   event.getClassification(), event.getTransform(),
+                                   event.getXPrecision(), event.getYPrecision(),
+                                   event.getRawXCursorPosition(), event.getRawYCursorPosition(),
+                                   event.getRawTransform(), event.getDownTime(), predictionTime,
+                                   event.getPointerCount(), event.getPointerProperties(), &coords);
+        } else {
+            prediction->addSample(predictionTime, &coords);
+        }
+
+        axisFrom = axisTo;
+        axisTo = point;
+    }
+    // TODO(b/266747511): Interpolate to futureTime?
+    if (!hasPredictions) {
+        return nullptr;
+    }
+    return prediction;
 }
 
 bool MotionPredictor::isPredictionAvailable(int32_t /*deviceId*/, int32_t source) {
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 10510d6..691e87c 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -30,6 +30,7 @@
 #include <type_traits>
 #include <utility>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/mapped_file.h>
 #define ATRACE_TAG ATRACE_TAG_INPUT
@@ -60,6 +61,14 @@
 constexpr char OUTPUT_PHI[] = "phi";
 constexpr char OUTPUT_PRESSURE[] = "pressure";
 
+std::string getModelPath() {
+#if defined(__ANDROID__)
+    return "/system/etc/motion_predictor_model.fb";
+#else
+    return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+#endif
+}
+
 // A TFLite ErrorReporter that logs to logcat.
 class LoggingErrorReporter : public tflite::ErrorReporter {
 public:
@@ -206,9 +215,9 @@
     mInputOrientation.pushBack(orientation);
 }
 
-std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create(
-        const char* modelPath) {
-    const int fd = open(modelPath, O_RDONLY);
+std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() {
+    const std::string modelPath = getModelPath();
+    const int fd = open(modelPath.c_str(), O_RDONLY);
     if (fd == -1) {
         PLOG(FATAL) << "Could not read model from " << modelPath;
     }
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 37faf91..42bdf57 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -43,6 +43,13 @@
         "-Werror",
         "-Wno-unused-parameter",
     ],
+    sanitize: {
+        undefined: true,
+        all_undefined: true,
+        diag: {
+            undefined: true,
+        },
+    },
     shared_libs: [
         "libbase",
         "libbinder",
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index ce87c86..c61efbf 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -30,13 +30,6 @@
 using ::testing::SizeIs;
 using ::testing::UnorderedElementsAre;
 
-const char MODEL_PATH[] =
-#if defined(__ANDROID__)
-        "/system/etc/motion_predictor_model.fb";
-#else
-        "motion_predictor_model.fb";
-#endif
-
 constexpr int32_t DOWN = AMOTION_EVENT_ACTION_DOWN;
 constexpr int32_t MOVE = AMOTION_EVENT_ACTION_MOVE;
 constexpr int32_t UP = AMOTION_EVENT_ACTION_UP;
@@ -73,83 +66,74 @@
 }
 
 TEST(MotionPredictorTest, IsPredictionAvailable) {
-    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
                               []() { return true /*enable prediction*/; });
     ASSERT_TRUE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
     ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
 }
 
 TEST(MotionPredictorTest, Offset) {
-    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, MODEL_PATH,
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
                               []() { return true /*enable prediction*/; });
     predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
     predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
-    std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
-    ASSERT_EQ(1u, predicted.size());
-    ASSERT_GE(predicted[0]->getEventTime(), 41);
+    std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+    ASSERT_NE(nullptr, predicted);
+    ASSERT_GE(predicted->getEventTime(), 41);
 }
 
 TEST(MotionPredictorTest, FollowsGesture) {
-    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
                               []() { return true /*enable prediction*/; });
 
     // MOVE without a DOWN is ignored.
     predictor.record(getMotionEvent(MOVE, 1, 3, 10ms));
-    EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+    EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
 
     predictor.record(getMotionEvent(DOWN, 2, 5, 20ms));
     predictor.record(getMotionEvent(MOVE, 2, 7, 30ms));
     predictor.record(getMotionEvent(MOVE, 3, 9, 40ms));
-    EXPECT_THAT(predictor.predict(50 * NSEC_PER_MSEC), SizeIs(1));
+    EXPECT_NE(nullptr, predictor.predict(50 * NSEC_PER_MSEC));
 
     predictor.record(getMotionEvent(UP, 4, 11, 50ms));
-    EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+    EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
 }
 
-TEST(MotionPredictorTest, MultipleDevicesTracked) {
-    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+TEST(MotionPredictorTest, MultipleDevicesNotSupported) {
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
                               []() { return true /*enable prediction*/; });
 
-    predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0));
-    predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0));
-    predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0));
-    predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0));
+    ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0)).ok());
 
-    predictor.record(getMotionEvent(DOWN, 100, 300, 0ms, /*deviceId=*/1));
-    predictor.record(getMotionEvent(MOVE, 100, 300, 10ms, /*deviceId=*/1));
-    predictor.record(getMotionEvent(MOVE, 200, 500, 20ms, /*deviceId=*/1));
-    predictor.record(getMotionEvent(MOVE, 300, 700, 30ms, /*deviceId=*/1));
+    ASSERT_FALSE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+    ASSERT_FALSE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
+}
 
-    {
-        std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
-        ASSERT_EQ(2u, predicted.size());
+TEST(MotionPredictorTest, IndividualGesturesFromDifferentDevicesAreSupported) {
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+                              []() { return true /*enable prediction*/; });
 
-        // Order of the returned vector is not guaranteed.
-        std::vector<int32_t> seenDeviceIds;
-        for (const auto& prediction : predicted) {
-            seenDeviceIds.push_back(prediction->getDeviceId());
-        }
-        EXPECT_THAT(seenDeviceIds, UnorderedElementsAre(0, 1));
-    }
+    ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(UP, 2, 5, 30ms, /*deviceId=*/0)).ok());
 
-    // End the gesture for device 0.
-    predictor.record(getMotionEvent(UP, 4, 9, 40ms, /*deviceId=*/0));
-    predictor.record(getMotionEvent(MOVE, 400, 900, 40ms, /*deviceId=*/1));
-
-    {
-        std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
-        ASSERT_EQ(1u, predicted.size());
-        ASSERT_EQ(predicted[0]->getDeviceId(), 1);
-    }
+    // Now, send a gesture from a different device. Since we have no active gesture, the new gesture
+    // should be processed correctly.
+    ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+    ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
 }
 
 TEST(MotionPredictorTest, FlagDisablesPrediction) {
-    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+    MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
                               []() { return false /*disable prediction*/; });
     predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
     predictor.record(getMotionEvent(MOVE, 0, 1, 35ms));
-    std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
-    ASSERT_EQ(0u, predicted.size());
+    std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+    ASSERT_EQ(nullptr, predicted);
     ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
     ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
 }
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index 454f2aa..6e76ac1 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -21,7 +21,6 @@
 #include <iterator>
 #include <string>
 
-#include <android-base/file.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/TfLiteMotionPredictor.h>
@@ -33,14 +32,6 @@
 using ::testing::ElementsAre;
 using ::testing::FloatNear;
 
-std::string getModelPath() {
-#if defined(__ANDROID__)
-    return "/system/etc/motion_predictor_model.fb";
-#else
-    return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
-#endif
-}
-
 TEST(TfLiteMotionPredictorTest, BuffersReadiness) {
     TfLiteMotionPredictorBuffers buffers(/*inputLength=*/5);
     ASSERT_FALSE(buffers.isReady());
@@ -92,8 +83,7 @@
 }
 
 TEST(TfLiteMotionPredictorTest, BuffersCopyTo) {
-    std::unique_ptr<TfLiteMotionPredictorModel> model =
-            TfLiteMotionPredictorModel::create(getModelPath().c_str());
+    std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
     TfLiteMotionPredictorBuffers buffers(model->inputLength());
 
     buffers.pushSample(/*timestamp=*/1,
@@ -137,8 +127,7 @@
 }
 
 TEST(TfLiteMotionPredictorTest, ModelInputOutputLength) {
-    std::unique_ptr<TfLiteMotionPredictorModel> model =
-            TfLiteMotionPredictorModel::create(getModelPath().c_str());
+    std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
     ASSERT_GT(model->inputLength(), 0u);
 
     const int inputLength = model->inputLength();
@@ -155,8 +144,7 @@
 }
 
 TEST(TfLiteMotionPredictorTest, ModelOutput) {
-    std::unique_ptr<TfLiteMotionPredictorModel> model =
-            TfLiteMotionPredictorModel::create(getModelPath().c_str());
+    std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
     TfLiteMotionPredictorBuffers buffers(model->inputLength());
 
     buffers.pushSample(/*timestamp=*/1, {.position = {.x = 100, .y = 200}, .pressure = 0.2});
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
index 133af5b..6ace354 100644
--- a/libs/jpegrecoverymap/OWNERS
+++ b/libs/jpegrecoverymap/OWNERS
@@ -1,4 +1,3 @@
 arifdikici@google.com
-deakin@google.com
 dichenzhang@google.com
 kyslov@google.com
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index 5455ba6..a433e8a 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -38,6 +38,14 @@
   JPEGR_TF_SRGB = 3,
 } jpegr_transfer_function;
 
+// Target output formats for decoder
+typedef enum {
+  JPEGR_OUTPUT_SDR,          // SDR in RGBA_8888 color format
+  JPEGR_OUTPUT_HDR_LINEAR,   // HDR in F16 color format (linear)
+  JPEGR_OUTPUT_HDR_PQ,       // HDR in RGBA_1010102 color format (PQ transfer function)
+  JPEGR_OUTPUT_HDR_HLG,      // HDR in RGBA_1010102 color format (HLG transfer function)
+} jpegr_output_format;
+
 struct jpegr_info_struct {
     size_t width;
     size_t height;
@@ -195,20 +203,15 @@
      * @param compressed_jpegr_image compressed JPEGR image
      * @param dest destination of the uncompressed JPEGR image
      * @param exif destination of the decoded EXIF metadata.
-     * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
-     *                    the primary image which is SDR. Setting of request_sdr and input source
-     *                    (HDR or SDR) can be found in the table below:
-     *                    |  input source  |  request_sdr  |  output of decoding  |
-     *                    |       HDR      |     true      |          SDR         |
-     *                    |       HDR      |     false     |          HDR         |
-     *                    |       SDR      |     true      |          SDR         |
-     *                    |       SDR      |     false     |          SDR         |
+     * @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.
      * @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,
-                         bool request_sdr = false);
+                         jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR);
 
     /*
     * Gets Info from JPEGR file without decoding it.
@@ -249,12 +252,16 @@
      * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
      * @param uncompressed_recovery_map uncompressed recovery map
      * @param metadata JPEG/R metadata extracted from XMP.
+     * @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 dest reconstructed HDR image
      * @return NO_ERROR if calculation succeeds, error code if error occurs.
      */
     status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                               jr_uncompressed_ptr uncompressed_recovery_map,
                               jr_metadata_ptr metadata,
+                              jpegr_output_format output_format,
                               jr_uncompressed_ptr dest);
 
 private:
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index c12cee9..8b5318f 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -115,6 +115,14 @@
   return temp /= rhs;
 }
 
+inline uint16_t floatToHalf(float f) {
+  uint32_t x = *((uint32_t*)&f);
+  uint16_t h = ((x >> 16) & 0x8000)
+             | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+             | ((x >> 13) & 0x03ff);
+  return h;
+}
+
 constexpr size_t kRecoveryFactorPrecision = 10;
 constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
 struct RecoveryLUT {
@@ -392,6 +400,13 @@
  */
 uint32_t colorToRgba1010102(Color e_gamma);
 
+/*
+ * Convert from Color to F16.
+ *
+ * Alpha always set to 1.0.
+ */
+uint64_t colorToRgbaF16(Color e_gamma);
+
 } // namespace android::jpegrecoverymap
 
 #endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index c22020a..79b1ae3 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -351,14 +351,14 @@
 status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                             jr_uncompressed_ptr dest,
                             jr_exif_ptr exif,
-                            bool request_sdr) {
+                            jpegr_output_format output_format) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
   // TODO: fill EXIF data
   (void) exif;
 
-  if (request_sdr) {
+  if (output_format == JPEGR_OUTPUT_SDR) {
     JpegDecoderHelper jpeg_decoder;
     if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
                                       true)) {
@@ -404,7 +404,7 @@
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
-  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, output_format, dest));
   return NO_ERROR;
 }
 
@@ -639,6 +639,7 @@
 status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                  jr_uncompressed_ptr uncompressed_recovery_map,
                                  jr_metadata_ptr metadata,
+                                 jpegr_output_format output_format,
                                  jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_recovery_map == nullptr
@@ -654,18 +655,12 @@
 
   JobQueue jobQueue;
   std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
-                                       metadata, dest, &jobQueue, &idwTable,
+                                       metadata, dest, &jobQueue, &idwTable, output_format,
                                        &recoveryLUT]() -> void {
     const float hdr_ratio = metadata->maxContentBoost;
     size_t width = uncompressed_yuv_420_image->width;
     size_t height = uncompressed_yuv_420_image->height;
 
-#if USE_HLG_OETF_LUT
-    ColorTransformFn hdrOetf = hlgOetfLUT;
-#else
-    ColorTransformFn hdrOetf = hlgOetf;
-#endif
-
     size_t rowStart, rowEnd;
     while (jobQueue.dequeueJob(rowStart, rowEnd)) {
       for (size_t y = rowStart; y < rowEnd; ++y) {
@@ -693,11 +688,44 @@
 #else
           Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
 #endif
-          Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
-          uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
-
+          rgb_hdr = rgb_hdr / metadata->maxContentBoost;
           size_t pixel_idx = x + y * width;
-          reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
+
+          switch (output_format) {
+            case JPEGR_OUTPUT_HDR_LINEAR:
+            {
+              uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
+              reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
+              break;
+            }
+            case JPEGR_OUTPUT_HDR_HLG:
+            {
+#if USE_HLG_OETF_LUT
+              ColorTransformFn hdrOetf = hlgOetfLUT;
+#else
+              ColorTransformFn hdrOetf = hlgOetf;
+#endif
+              Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+              uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+              reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+              break;
+            }
+            case JPEGR_OUTPUT_HDR_PQ:
+            {
+#if USE_HLG_OETF_LUT
+              ColorTransformFn hdrOetf = pqOetfLUT;
+#else
+              ColorTransformFn hdrOetf = pqOetf;
+#endif
+              Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+              uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+              reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+              break;
+            }
+            default:
+            {}
+              // Should be impossible to hit after input validation.
+          }
         }
       }
     }
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 7812e18..20c32ed 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -631,4 +631,11 @@
        | (0x3 << 30);  // Set alpha to 1.0
 }
 
+uint64_t colorToRgbaF16(Color e_gamma) {
+  return (uint64_t) floatToHalf(e_gamma.r)
+       | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
+       | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
+       | (((uint64_t) floatToHalf(1.0f)) << 48);
+}
+
 } // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index df212e1..0a7d20a 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -152,7 +152,7 @@
 
   timerStart(&applyRecMapTime);
   for (auto i = 0; i < kProfileCount; i++) {
-      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
+      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest));
   }
   timerStop(&applyRecMapTime);
 
@@ -170,7 +170,7 @@
   jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
                          nullptr);
   jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
-  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false);
+  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr);
 }
 
 TEST_F(JpegRTest, writeXmpThenRead) {
@@ -228,7 +228,7 @@
   }
 
   jpegr_uncompressed_struct decodedJpegR;
-  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
   decodedJpegR.data = malloc(decodedJpegRSize);
   ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
@@ -236,7 +236,7 @@
   }
   if (SAVE_DECODING_RESULT) {
     // Output image data to file
-    std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb10";
+    std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
     std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
     if (!imageFile.is_open()) {
       ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -289,7 +289,7 @@
   }
 
   jpegr_uncompressed_struct decodedJpegR;
-  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
   decodedJpegR.data = malloc(decodedJpegRSize);
   ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
@@ -297,7 +297,7 @@
   }
   if (SAVE_DECODING_RESULT) {
     // Output image data to file
-    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb10";
+    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
     std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
     if (!imageFile.is_open()) {
       ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -354,7 +354,7 @@
   }
 
   jpegr_uncompressed_struct decodedJpegR;
-  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
   decodedJpegR.data = malloc(decodedJpegRSize);
   ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
@@ -362,7 +362,7 @@
   }
   if (SAVE_DECODING_RESULT) {
     // Output image data to file
-    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb10";
+    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
     std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
     if (!imageFile.is_open()) {
       ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -435,7 +435,7 @@
   }
 
   jpegr_uncompressed_struct decodedJpegR;
-  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
   decodedJpegR.data = malloc(decodedJpegRSize);
   ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
@@ -443,7 +443,7 @@
   }
   if (SAVE_DECODING_RESULT) {
     // Output image data to file
-    std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb10";
+    std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
     std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
     if (!imageFile.is_open()) {
       ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b7b2926..5306529 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -24,14 +24,49 @@
 
 #include <private/android/AHardwareBufferHelpers.h>
 
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
 #include <log/log.h>
 #include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/view/Surface.h>
-#include <android/binder_libbinder.h>
 
 using namespace android;
 
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+                                                        Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+        status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+    android_view_Surface_writeToParcel write = nullptr;
+    android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+    static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+        SurfaceParcelables ret;
+        void* dl = dlopen("libgui.so", RTLD_NOW);
+        LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+        ret.write =
+                (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+        LOG_ALWAYS_FATAL_IF(!ret.write,
+                            "libgui.so missing android_view_Surface_writeToParcel; "
+                            "loaded wrong libgui?");
+        ret.read =
+                (android_view_Surface_readFromParcel)dlsym(dl,
+                                                           "android_view_Surface_readFromParcel");
+        LOG_ALWAYS_FATAL_IF(!ret.read,
+                            "libgui.so missing android_view_Surface_readFromParcel; "
+                            "loaded wrong libgui?");
+        return ret;
+    }();
+    return &funcs;
+}
+
 static int32_t query(ANativeWindow* window, int what) {
     int value;
     int res = window->query(window, what, &value);
@@ -64,13 +99,6 @@
             return false;
     }
 }
-static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
-    return Surface::getIGraphicBufferProducer(window);
-}
-
-static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
-    return Surface::getSurfaceControlHandle(window);
-}
 
 /**************************************************************************************************
  * NDK
@@ -355,38 +383,24 @@
 
 binder_status_t ANativeWindow_readFromParcel(
         const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
-    const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-
-    // Use a android::view::Surface to unparcel the window
-    std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
-    status_t ret = shimSurface->readFromParcel(nativeParcel);
-    if (ret != OK) {
-        ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
-        return STATUS_BAD_VALUE;
+    auto funcs = getSurfaceParcelFunctions();
+    if (funcs->read == nullptr) {
+        ALOGE("Failed to load Surface_readFromParcel implementation");
+        return STATUS_FAILED_TRANSACTION;
     }
-    sp<Surface> surface = sp<Surface>::make(
-            shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
-    ANativeWindow* anw = surface.get();
-    ANativeWindow_acquire(anw);
-    *outWindow = anw;
-    return STATUS_OK;
+    const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+    return funcs->read(nativeParcel, outWindow);
 }
 
 binder_status_t ANativeWindow_writeToParcel(
         ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
-    int value;
-    int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
-    if (err != OK || value != NATIVE_WINDOW_SURFACE) {
-        ALOGE("Error: ANativeWindow is not backed by Surface");
-        return STATUS_BAD_VALUE;
+    auto funcs = getSurfaceParcelFunctions();
+    if (funcs->write == nullptr) {
+        ALOGE("Failed to load Surface_writeToParcel implementation");
+        return STATUS_FAILED_TRANSACTION;
     }
-    // Use a android::view::Surface to parcelize the window
-    std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
-    shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
-    shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
-
     Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-    return shimSurface->writeToParcel(nativeParcel);
+    return funcs->write(window, nativeParcel);
 }
 
 /**************************************************************************************************
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 8d99f3d..936e316 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -444,8 +444,11 @@
     ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
 
     VkQueue graphicsQueue;
-    VK_GET_DEV_PROC(device, GetDeviceQueue);
-    vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);
+    VK_GET_DEV_PROC(device, GetDeviceQueue2);
+    const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr,
+                                                 deviceQueueCreateFlags,
+                                                 (uint32_t)graphicsQueueIndex, 0};
+    vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue);
 
     VK_GET_DEV_PROC(device, DeviceWaitIdle);
     VK_GET_DEV_PROC(device, DestroyDevice);
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index e2aac8c..019d6cb 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -139,10 +139,12 @@
     }
 
     virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
-            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+            int deviceId, uint32_t size, int32_t type, int32_t format,
+            const native_handle_t *resource) {
         Parcel data, reply;
         data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
         data.writeString16(opPackageName);
+        data.writeInt32(deviceId);
         data.writeUint32(size);
         data.writeInt32(type);
         data.writeInt32(format);
@@ -237,6 +239,7 @@
         case CREATE_SENSOR_DIRECT_CONNECTION: {
             CHECK_INTERFACE(ISensorServer, data, reply);
             const String16& opPackageName = data.readString16();
+            const int deviceId = data.readInt32();
             uint32_t size = data.readUint32();
             int32_t type = data.readInt32();
             int32_t format = data.readInt32();
@@ -246,8 +249,8 @@
                 return BAD_VALUE;
             }
             native_handle_set_fdsan_tag(resource);
-            sp<ISensorEventConnection> ch =
-                    createSensorDirectConnection(opPackageName, size, type, format, resource);
+            sp<ISensorEventConnection> ch = createSensorDirectConnection(
+                    opPackageName, deviceId, size, type, format, resource);
             native_handle_close_with_tag(resource);
             native_handle_delete(resource);
             reply->writeStrongBinder(IInterface::asBinder(ch));
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 44a208d..ba190e0 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -315,6 +315,12 @@
 
 int SensorManager::createDirectChannel(
         size_t size, int channelType, const native_handle_t *resourceHandle) {
+    static constexpr int DEFAULT_DEVICE_ID = 0;
+    return createDirectChannel(DEFAULT_DEVICE_ID, size, channelType, resourceHandle);
+}
+
+int SensorManager::createDirectChannel(
+        int deviceId, size_t size, int channelType, const native_handle_t *resourceHandle) {
     Mutex::Autolock _l(mLock);
     if (assertStateLocked() != NO_ERROR) {
         return NO_INIT;
@@ -327,7 +333,7 @@
     }
 
     sp<ISensorEventConnection> conn =
-              mSensorServer->createSensorDirectConnection(mOpPackageName,
+              mSensorServer->createSensorDirectConnection(mOpPackageName, deviceId,
                   static_cast<uint32_t>(size),
                   static_cast<int32_t>(channelType),
                   SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index 3295196..5815728 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -50,7 +50,8 @@
     virtual int32_t isDataInjectionEnabled() = 0;
 
     virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
-            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+            int deviceId, uint32_t size, int32_t type, int32_t format,
+            const native_handle_t *resource) = 0;
 
     virtual int setOperationParameter(
             int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index c31f648..bb44cb8 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -66,6 +66,8 @@
         String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
     bool isDataInjectionEnabled();
     int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+    int createDirectChannel(
+        int deviceId, size_t size, int channelType, const native_handle_t *channelData);
     void destroyDirectChannel(int channelNativeHandle);
     int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
     int setOperationParameter(int handle, int type, const Vector<float> &floats, const Vector<int32_t> &ints);
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 379f763..3d60a1d 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,11 +1,6 @@
-abdolrashidi@google.com
-cclao@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 ianelliott@google.com
 jessehall@google.com
-lfy@google.com
 lpy@google.com
-romanl@google.com
 vantablack@google.com
-yuxinhu@google.com
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index b00ee33..3dc93ee 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -110,38 +110,6 @@
         }
     }
 
-    // Check the device config to decide whether multifile should be used
-    if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
-        mMultifileMode = true;
-        ALOGV("Using multifile EGL blobcache");
-    }
-
-    // Allow forcing the mode for debug purposes
-    std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
-    if (mode == "true") {
-        ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
-        mMultifileMode = true;
-    } else if (mode == "false") {
-        ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
-        mMultifileMode = false;
-    }
-
-    if (mMultifileMode) {
-        mCacheByteLimit = static_cast<size_t>(
-                base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
-                                                kMultifileCacheByteLimit));
-
-        // Check for a debug value
-        int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
-        if (debugCacheSize >= 0) {
-            ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
-                  mCacheByteLimit, debugCacheSize);
-            mCacheByteLimit = debugCacheSize;
-        }
-
-        ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
-    }
-
     mInitialized = true;
 }
 
@@ -167,6 +135,8 @@
         return;
     }
 
+    updateMode();
+
     if (mInitialized) {
         if (mMultifileMode) {
             MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -200,6 +170,8 @@
         return 0;
     }
 
+    updateMode();
+
     if (mInitialized) {
         if (mMultifileMode) {
             MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -247,6 +219,51 @@
     return 0;
 }
 
+void egl_cache_t::updateMode() {
+    // We don't set the mode in the constructor because these checks have
+    // a non-trivial cost, and not all processes that instantiate egl_cache_t
+    // will use it.
+
+    // If we've already set the mode, skip these checks
+    static bool checked = false;
+    if (checked) {
+        return;
+    }
+    checked = true;
+
+    // Check the device config to decide whether multifile should be used
+    if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
+        mMultifileMode = true;
+        ALOGV("Using multifile EGL blobcache");
+    }
+
+    // Allow forcing the mode for debug purposes
+    std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
+    if (mode == "true") {
+        ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+        mMultifileMode = true;
+    } else if (mode == "false") {
+        ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+        mMultifileMode = false;
+    }
+
+    if (mMultifileMode) {
+        mCacheByteLimit = static_cast<size_t>(
+                base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
+                                                kMultifileCacheByteLimit));
+
+        // Check for a debug value
+        int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
+        if (debugCacheSize >= 0) {
+            ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
+                  mCacheByteLimit, debugCacheSize);
+            mCacheByteLimit = debugCacheSize;
+        }
+
+        ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
+    }
+}
+
 BlobCache* egl_cache_t::getBlobCacheLocked() {
     if (mBlobCache == nullptr) {
         mBlobCache.reset(new FileBlobCache(kMaxMonolithicKeySize, kMaxMonolithicValueSize,
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 1399368..ae6d381 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -88,6 +88,9 @@
     egl_cache_t(const egl_cache_t&); // not implemented
     void operator=(const egl_cache_t&); // not implemented
 
+    // Check system properties to determine which blobcache mode should be used
+    void updateMode();
+
     // getBlobCacheLocked returns the BlobCache object being used to store the
     // key/value blob pairs.  If the BlobCache object has not yet been created,
     // this will do so, loading the serialized cache contents from disk if
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 43cac05..78cdd0d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4036,18 +4036,20 @@
 
 /**
  * If one of the meta shortcuts is detected, process them here:
- *     Meta + Backspace -> generate BACK
- *     Meta + Enter -> generate HOME
- * This will potentially overwrite keyCode and metaState.
+ *     Meta + Backspace; Meta + Grave; Meta + Left arrow -> generate BACK
+ * Most System shortcuts are handled in PhoneWindowManager.java except 'Back' shortcuts. Unlike
+ * Back, other shortcuts DO NOT need to be sent to applications and are fully handled by the system.
+ * But for Back key and Back shortcuts, we need to send KEYCODE_BACK to applications which can
+ * potentially handle the back key presses.
+ * Note: We don't send any Meta based KeyEvents to applications, so we need to convert to a KeyEvent
+ * where meta modifier is off before sending. Currently only use case is 'Back'.
  */
 void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
                                               int32_t& keyCode, int32_t& metaState) {
     if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
         int32_t newKeyCode = AKEYCODE_UNKNOWN;
-        if (keyCode == AKEYCODE_DEL) {
+        if (keyCode == AKEYCODE_DEL || keyCode == AKEYCODE_GRAVE || keyCode == AKEYCODE_DPAD_LEFT) {
             newKeyCode = AKEYCODE_BACK;
-        } else if (keyCode == AKEYCODE_ENTER) {
-            newKeyCode = AKEYCODE_HOME;
         }
         if (newKeyCode != AKEYCODE_UNKNOWN) {
             std::scoped_lock _l(mLock);
@@ -4178,9 +4180,12 @@
                   args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
         }
     }
-    LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
-                                             args->pointerProperties),
-                        "Invalid event: %s", args->dump().c_str());
+
+    if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+                             args->pointerProperties)) {
+        LOG(ERROR) << "Invalid event: " << args->dump();
+        return;
+    }
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
diff --git a/services/inputflinger/docs/input_coordinates.md b/services/inputflinger/docs/input_coordinates.md
new file mode 100644
index 0000000..7795710
--- /dev/null
+++ b/services/inputflinger/docs/input_coordinates.md
@@ -0,0 +1,113 @@
+# Input Coordinate Processing in InputFlinger
+
+This document aims to illustrate why we need to take care when converting
+between the discrete and continuous coordinate spaces, especially when
+performing rotations.
+
+The Linux evdev protocol works over **discrete integral** values. The same is
+true for displays, which output discrete pixels. WindowManager also tracks
+window bounds in pixels in the rotated logical display.
+
+However, our `MotionEvent` APIs
+report **floating point** axis values in a **continuous space**. This disparity
+is important to note when working in InputFlinger, which has to make sure the
+discrete raw coordinates are  converted to the continuous space correctly in all
+scenarios.
+
+## Disparity between continuous and discrete coordinates during rotation
+
+Let's consider an example of device that has a 3 x 4 screen.
+
+### Natural orientation:  No rotation
+
+If the user interacts with the highlighted pixel, the touchscreen would report
+the discreet coordinates (0, 2).
+
+```
+     ┌─────┬─────┬─────┐
+     │ 0,0 │ 1,0 │ 2,0 │
+     ├─────┼─────┼─────┤
+     │ 0,1 │ 1,1 │ 2,1 │
+     ├─────┼─────┼─────┤
+     │█0,2█│ 1,2 │ 2,2 │
+     ├─────┼─────┼─────┤
+     │ 0,3 │ 1,3 │ 2,3 │
+     └─────┴─────┴─────┘
+```
+
+When converted to the continuous space, the point (0, 2) corresponds to the
+location shown below.
+
+```
+     0     1     2     3
+  0  ┌─────┬─────┬─────┐
+     │     │     │     │
+  1  ├─────┼─────┼─────┤
+     │     │     │     │
+  2  █─────┼─────┼─────┤
+     │     │     │     │
+  3  ├─────┼─────┼─────┤
+     │     │     │     │
+  4  └─────┴─────┴─────┘
+```
+
+### Rotated orientation: 90-degree counter-clockwise rotation
+
+When the device is rotated and the same place on the touchscreen is touched, the
+input device will still report the same coordinates of (0, 2).
+
+In the rotated display, that now corresponds to the pixel (2, 2).
+
+```
+     ┌─────┬─────┬─────┬─────┐
+     │ 0,0 │ 1,0 │ 2,0 │ 3,0 │
+     ├─────┼─────┼─────┼─────┤
+     │ 0,1 │ 1,1 │ 2,1 │ 3,1 │
+     ├─────┼─────┼─────┼─────┤
+     │ 0,2 │ 1,2 │█2,2█│ 3,2 │
+     └─────┴─────┴─────┴─────┘
+```
+
+*It is important to note that rotating the device 90 degrees is NOT equivalent
+to rotating the continuous coordinate space by 90 degrees.*
+
+The point (2, 2) now corresponds to a different location in the continuous space
+than before, even though the user was interacting at the same place on the
+touchscreen.
+
+```
+     0     1     2     3     4
+  0  ┌─────┬─────┬─────┬─────┐
+     │     │     │     │     │
+  1  ├─────┼─────┼─────┼─────┤
+     │     │     │     │     │
+  2  ├─────┼─────█─────┼─────┤
+     │     │     │     │     │
+  3  └─────┴─────┴─────┴─────┘
+```
+
+If we were to simply (incorrectly) rotate the continuous space from before by
+90 degrees, the touched point would correspond to the location (2, 3), shown
+below. This new point is outside the bounds of the display, since it does not
+correspond to any pixel at that location.
+
+It should be impossible for a touchscreen to generate points outside the bounds
+of the display, because we assume that the area of the touchscreen maps directly
+to the area of the display. Therefore, that point is an invalid coordinate that
+cannot be generated by an input device.
+
+```
+     0     1     2     3     4
+  0  ┌─────┬─────┬─────┬─────┐
+     │     │     │     │     ╏
+  1  ├─────┼─────┼─────┼─────┤
+     │     │     │     │     ╏
+  2  ├─────┼─────┼─────┼─────┤
+     │     │     │     │     ╏
+  3  └-----┴-----█-----┴-----┘
+```
+
+The same logic applies to windows as well. When performing hit tests to
+determine if a point in the continuous space falls inside a window's bounds,
+hit test must be performed in the correct orientation, since points on the right
+and bottom edges of the window do not fall within the window bounds.
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6fd4ff3..9fe652c 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -277,7 +277,11 @@
     mHasMic = mClasses.test(InputDeviceClass::MIC);
 
     if (!isIgnored()) {
-        if (!changes) { // first time only
+        // Full configuration should happen the first time configure is called
+        // and when the device type is changed. Changing a device type can
+        // affect various other parameters so should result in a
+        // reconfiguration.
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
             mConfiguration.clear();
             for_each_subdevice([this](InputDeviceContext& context) {
                 PropertyMap configuration;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 3ba6cd0..b53fc73 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -293,7 +293,10 @@
 
     mConfig = *config;
 
-    if (!changes) { // first time only
+    // Full configuration should happen the first time configure is called and
+    // when the device type is changed. Changing a device type can affect
+    // various other parameters so should result in a reconfiguration.
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
         // Configure basic parameters.
         configureParameters();
 
@@ -328,7 +331,8 @@
           InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
           InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
           InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
-          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
+          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE |
+          InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
         // Configure device sources, display dimensions, orientation and
         // scaling factors.
         configureInputDevice(when, &resetNeeded);
@@ -838,38 +842,60 @@
 }
 
 void TouchInputMapper::computeInputTransforms() {
-    const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+    constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) {
+        return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270;
+    };
 
-    ui::Size rotatedRawSize = rawSize;
-    if (mInputDeviceOrientation == ui::ROTATION_270 || mInputDeviceOrientation == ui::ROTATION_90) {
-        std::swap(rotatedRawSize.width, rotatedRawSize.height);
-    }
-    const auto rotationFlags = ui::Transform::toRotationFlags(-mInputDeviceOrientation);
-    mRawRotation = ui::Transform{rotationFlags};
+    // See notes about input coordinates in the inputflinger docs:
+    // //frameworks/native/services/inputflinger/docs/input_coordinates.md
 
     // Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
-    ui::Transform undoRawOffset;
-    undoRawOffset.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
+    ui::Transform undoOffsetInRaw;
+    undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
 
-    // Step 2: Rotate the raw coordinates to the expected orientation.
-    ui::Transform rotate;
-    // When rotating raw coordinates, the raw size will be used as an offset.
-    // Account for the extra unit added to the raw range when the raw size was calculated.
-    rotate.set(rotationFlags, rotatedRawSize.width - 1, rotatedRawSize.height - 1);
+    // Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates
+    // will now be in the same orientation as the display in ROTATION_0.
+    // Note: Negating an ui::Rotation value will give its inverse rotation.
+    const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation);
+    const ui::Size orientedRawSize = isRotated(inputDeviceOrientation)
+            ? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()}
+            : ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+    // When rotating raw values, account for the extra unit added when calculating the raw range.
+    const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1,
+                                           orientedRawSize.height - 1);
 
-    // Step 3: Scale the raw coordinates to the display space.
-    ui::Transform scaleToDisplay;
-    const float xScale = static_cast<float>(mDisplayBounds.width) / rotatedRawSize.width;
-    const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height;
-    scaleToDisplay.set(xScale, 0, 0, yScale);
+    // Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will
+    // now be in the same orientation as the rotated display. There is no need to rotate the
+    // coordinates to the display rotation if the device is not orientation-aware.
+    const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation);
+    const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation)
+            ? ui::Size{orientedRawSize.height, orientedRawSize.width}
+            : orientedRawSize;
+    // When rotating raw values, account for the extra unit added when calculating the raw range.
+    const auto rotateInRaw = mParameters.orientationAware
+            ? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1)
+            : ui::Transform();
 
-    mRawToDisplay = (scaleToDisplay * (rotate * undoRawOffset));
+    // Step 4: Scale the raw coordinates to the display space.
+    // - Here, we assume that the raw surface of the touch device maps perfectly to the surface
+    //   of the display panel. This is usually true for touchscreens.
+    // - From this point onward, we are no longer in the discrete space of the raw coordinates but
+    //   are in the continuous space of the logical display.
+    ui::Transform scaleRawToDisplay;
+    const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width;
+    const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height;
+    scaleRawToDisplay.set(xScale, 0, 0, yScale);
 
-    // Calculate the transform that takes raw coordinates to the rotated display space.
-    ui::Transform displayToRotatedDisplay;
-    displayToRotatedDisplay.set(ui::Transform::toRotationFlags(-mViewport.orientation),
-                                mViewport.deviceWidth, mViewport.deviceHeight);
-    mRawToRotatedDisplay = displayToRotatedDisplay * mRawToDisplay;
+    // Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space
+    // that InputReader uses.
+    const auto undoRotateInDisplay =
+            ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight)
+                    .inverse();
+
+    // Now put it all together!
+    mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw)));
+    mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay);
+    mRawRotation = ui::Transform{mRawToDisplay.getOrientation()};
 }
 
 void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
@@ -945,6 +971,9 @@
             mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
                                               mViewport.physicalRight, mViewport.physicalBottom};
 
+            // TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that
+            //     uses mInputDeviceOrientation. The new logic uses the transforms calculated in
+            //     computeInputTransforms().
             // InputReader works in the un-rotated display coordinate space, so we don't need to do
             // anything if the device is already orientation-aware. If the device is not
             // orientation-aware, then we need to apply the inverse rotation of the display so that
@@ -1650,7 +1679,8 @@
     mPointerController->setButtonState(mCurrentRawState.buttonState);
     mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
                                  mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
-                                 mCurrentCookedState.cookedPointerData.touchingIdBits,
+                                 mCurrentCookedState.cookedPointerData.touchingIdBits |
+                                         mCurrentCookedState.cookedPointerData.hoveringIdBits,
                                  mViewport.displayId);
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 8ab6748..d3af402 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -31,6 +31,15 @@
 
 namespace {
 
+/**
+ * Log details of each gesture output by the gestures library.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
+ * restarting the shell)
+ */
+const bool DEBUG_TOUCHPAD_GESTURES =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+                                  ANDROID_LOG_INFO);
+
 // Describes a segment of the acceleration curve.
 struct CurveSegment {
     // The maximum pointer speed which this segment should apply. The last segment in a curve should
@@ -251,6 +260,7 @@
 
 std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                              SelfContainedHardwareState schs) {
+    ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
     mProcessing = true;
     mGestureInterpreter->PushHardwareState(&schs.state);
     mProcessing = false;
@@ -259,7 +269,7 @@
 }
 
 void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
-    ALOGD("Gesture ready: %s", gesture->String().c_str());
+    ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
     if (!mProcessing) {
         ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
         return;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 2e175b8..c091a51 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -80,18 +80,32 @@
     schs.fingers.clear();
     for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
         MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
-        if (slot.isInUse()) {
-            FingerState& fingerState = schs.fingers.emplace_back();
-            fingerState = {};
-            fingerState.touch_major = slot.getTouchMajor();
-            fingerState.touch_minor = slot.getTouchMinor();
-            fingerState.width_major = slot.getToolMajor();
-            fingerState.width_minor = slot.getToolMinor();
-            fingerState.pressure = slot.getPressure();
-            fingerState.orientation = slot.getOrientation();
-            fingerState.position_x = slot.getX();
-            fingerState.position_y = slot.getY();
-            fingerState.tracking_id = slot.getTrackingId();
+        if (!slot.isInUse()) {
+            continue;
+        }
+        // Some touchpads continue to report contacts even after they've identified them as palms.
+        // We want to exclude these contacts from the HardwareStates, but still need to report a
+        // tracking ID of -1 if a finger turns into a palm.
+        const bool isPalm = slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM;
+        if (isPalm && mFingerSlots.find(i) == mFingerSlots.end()) {
+            continue;
+        }
+
+        FingerState& fingerState = schs.fingers.emplace_back();
+        fingerState = {};
+        fingerState.touch_major = slot.getTouchMajor();
+        fingerState.touch_minor = slot.getTouchMinor();
+        fingerState.width_major = slot.getToolMajor();
+        fingerState.width_minor = slot.getToolMinor();
+        fingerState.pressure = slot.getPressure();
+        fingerState.orientation = slot.getOrientation();
+        fingerState.position_x = slot.getX();
+        fingerState.position_y = slot.getY();
+        fingerState.tracking_id = isPalm ? -1 : slot.getTrackingId();
+        if (fingerState.tracking_id == -1) {
+            mFingerSlots.erase(i);
+        } else {
+            mFingerSlots.insert(i);
         }
     }
     schs.state.fingers = schs.fingers.data();
@@ -103,6 +117,7 @@
 void HardwareStateConverter::reset() {
     mCursorButtonAccumulator.reset(mDeviceContext);
     mTouchButtonAccumulator.reset();
+    mFingerSlots.clear();
     mMscTimestamp = 0;
 }
 
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 8831299..d6787b7 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <optional>
+#include <set>
 
 #include <utils/Timers.h>
 
@@ -53,6 +54,7 @@
     MultiTouchMotionAccumulator mMotionAccumulator;
     TouchButtonAccumulator mTouchButtonAccumulator;
     int32_t mMscTimestamp = 0;
+    std::set<size_t> mFingerSlots;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index cd18cd3..089f45a 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -68,11 +68,11 @@
         .free_fn = freeProperty,
 };
 
-bool PropertyProvider::hasProperty(const std::string name) const {
+bool PropertyProvider::hasProperty(const std::string& name) const {
     return mProperties.find(name) != mProperties.end();
 }
 
-GesturesProp& PropertyProvider::getProperty(const std::string name) {
+GesturesProp& PropertyProvider::getProperty(const std::string& name) {
     return mProperties.at(name);
 }
 
@@ -84,7 +84,7 @@
     return dump;
 }
 
-GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc,
+GesturesProp* PropertyProvider::createIntArrayProperty(const std::string& name, int* loc,
                                                        size_t count, const int* init) {
     const auto [it, inserted] =
             mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -92,7 +92,7 @@
     return &it->second;
 }
 
-GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name,
+GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string& name,
                                                         GesturesPropBool* loc, size_t count,
                                                         const GesturesPropBool* init) {
     const auto [it, inserted] =
@@ -101,7 +101,7 @@
     return &it->second;
 }
 
-GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc,
+GesturesProp* PropertyProvider::createRealArrayProperty(const std::string& name, double* loc,
                                                         size_t count, const double* init) {
     const auto [it, inserted] =
             mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -109,7 +109,7 @@
     return &it->second;
 }
 
-GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc,
+GesturesProp* PropertyProvider::createStringProperty(const std::string& name, const char** loc,
                                                      const char* const init) {
     const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)});
     LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
index c21260f..50451a3 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
@@ -31,18 +31,18 @@
 // Implementation of a gestures library property provider, which provides configuration parameters.
 class PropertyProvider {
 public:
-    bool hasProperty(const std::string name) const;
-    GesturesProp& getProperty(const std::string name);
+    bool hasProperty(const std::string& name) const;
+    GesturesProp& getProperty(const std::string& name);
     std::string dump() const;
 
     // Methods to be called by the gestures library:
-    GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count,
+    GesturesProp* createIntArrayProperty(const std::string& name, int* loc, size_t count,
                                          const int* init);
-    GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc,
+    GesturesProp* createBoolArrayProperty(const std::string& name, GesturesPropBool* loc,
                                           size_t count, const GesturesPropBool* init);
-    GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count,
+    GesturesProp* createRealArrayProperty(const std::string& name, double* loc, size_t count,
                                           const double* init);
-    GesturesProp* createStringProperty(const std::string name, const char** loc,
+    GesturesProp* createStringProperty(const std::string& name, const char** loc,
                                        const char* const init);
 
     void freeProperty(GesturesProp* prop);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index af40fed..97138c7 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -86,6 +86,13 @@
             ],
         },
     },
+    sanitize: {
+        undefined: true,
+        all_undefined: true,
+        diag: {
+            undefined: true,
+        },
+    },
     static_libs: [
         "libc++fs",
         "libgmock",
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 7921881..36b9bab 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -191,6 +191,68 @@
     EXPECT_EQ(0u, finger2.flags);
 }
 
+TEST_F(HardwareStateConverterTest, OnePalm) {
+    const nsecs_t time = ARBITRARY_TIME;
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    HardwareStateConverter conv(deviceContext);
+
+    processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+    processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+    processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+    std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+    ASSERT_TRUE(schs.has_value());
+    EXPECT_EQ(0, schs->state.finger_cnt);
+}
+
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) {
+    const nsecs_t time = ARBITRARY_TIME;
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    HardwareStateConverter conv(deviceContext);
+
+    processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+    processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+    processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+
+    std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+    ASSERT_TRUE(schs.has_value());
+    EXPECT_EQ(1, schs->state.finger_cnt);
+
+    processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 51);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+    schs = processSync(conv, time);
+    ASSERT_TRUE(schs.has_value());
+    ASSERT_EQ(1, schs->state.finger_cnt);
+    EXPECT_EQ(-1, schs->state.fingers[0].tracking_id);
+
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97);
+
+    schs = processSync(conv, time);
+    ASSERT_TRUE(schs.has_value());
+    EXPECT_EQ(0, schs->state.finger_cnt);
+
+    processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 55);
+    processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 95);
+    schs = processSync(conv, time);
+    ASSERT_TRUE(schs.has_value());
+    ASSERT_EQ(1, schs->state.finger_cnt);
+    const FingerState& newFinger = schs->state.fingers[0];
+    EXPECT_EQ(123, newFinger.tracking_id);
+    EXPECT_NEAR(55, newFinger.position_x, EPSILON);
+    EXPECT_NEAR(95, newFinger.position_y, EPSILON);
+}
+
 TEST_F(HardwareStateConverterTest, ButtonPressed) {
     const nsecs_t time = ARBITRARY_TIME;
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index a02ef05..ae30006 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -54,7 +54,8 @@
     if (!changes ||
         (changes &
          (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-          InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+          InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
+          InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
         mReader->requestRefreshConfiguration(changes);
         mReader->loopOnce();
     }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index fe7af80..0855683 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6724,6 +6724,29 @@
     ASSERT_FALSE(fakePointerController->isPointerShown());
 }
 
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
+    // Initialize the device without setting device source to touch navigation.
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(ui::ROTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    // Ensure that the device is created as a touchscreen, not touch navigation.
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+    // Add device type association after the device was created.
+    mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+
+    // Send update to the mapper.
+    std::list<NotifyArgs> unused2 =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/);
+
+    // Check whether device type update was successful.
+    ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
+}
+
 // --- TouchDisplayProjectionTest ---
 
 class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -6731,28 +6754,30 @@
     // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
     // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
     // rotated equivalent of the given un-rotated physical display bounds.
-    void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
+    void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay,
+                                  int32_t naturalDisplayWidth = DISPLAY_WIDTH,
+                                  int32_t naturalDisplayHeight = DISPLAY_HEIGHT) {
         uint32_t inverseRotationFlags;
-        auto width = DISPLAY_WIDTH;
-        auto height = DISPLAY_HEIGHT;
+        auto rotatedWidth = naturalDisplayWidth;
+        auto rotatedHeight = naturalDisplayHeight;
         switch (orientation) {
             case ui::ROTATION_90:
                 inverseRotationFlags = ui::Transform::ROT_270;
-                std::swap(width, height);
+                std::swap(rotatedWidth, rotatedHeight);
                 break;
             case ui::ROTATION_180:
                 inverseRotationFlags = ui::Transform::ROT_180;
                 break;
             case ui::ROTATION_270:
                 inverseRotationFlags = ui::Transform::ROT_90;
-                std::swap(width, height);
+                std::swap(rotatedWidth, rotatedHeight);
                 break;
             case ui::ROTATION_0:
                 inverseRotationFlags = ui::Transform::ROT_0;
                 break;
         }
 
-        const ui::Transform rotation(inverseRotationFlags, width, height);
+        const ui::Transform rotation(inverseRotationFlags, rotatedWidth, rotatedHeight);
         const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
 
         std::optional<DisplayViewport> internalViewport =
@@ -6771,8 +6796,8 @@
         v.physicalRight = rotatedPhysicalDisplay.right;
         v.physicalBottom = rotatedPhysicalDisplay.bottom;
 
-        v.deviceWidth = width;
-        v.deviceHeight = height;
+        v.deviceWidth = rotatedWidth;
+        v.deviceHeight = rotatedHeight;
 
         v.isActive = true;
         v.uniqueId = UNIQUE_ID;
@@ -6886,6 +6911,197 @@
     }
 }
 
+// --- TouchscreenPrecisionTests ---
+
+// This test suite is used to ensure that touchscreen devices are scaled and configured correctly
+// in various orientations and with different display rotations. We configure the touchscreen to
+// have a higher resolution than that of the display by an integer scale factor in each axis so that
+// we can enforce that coordinates match precisely as expected.
+class TouchscreenPrecisionTestsFixture : public TouchDisplayProjectionTest,
+                                         public ::testing::WithParamInterface<ui::Rotation> {
+public:
+    void SetUp() override {
+        SingleTouchInputMapperTest::SetUp();
+
+        // Prepare the raw axes to have twice the resolution of the display in the X axis and
+        // four times the resolution of the display in the Y axis.
+        prepareButtons();
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, PRECISION_RAW_X_MIN, PRECISION_RAW_X_MAX,
+                                       0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, PRECISION_RAW_Y_MIN, PRECISION_RAW_Y_MAX,
+                                       0, 0);
+    }
+
+    static const int32_t PRECISION_RAW_X_MIN = TouchInputMapperTest::RAW_X_MIN;
+    static const int32_t PRECISION_RAW_X_MAX = PRECISION_RAW_X_MIN + DISPLAY_WIDTH * 2 - 1;
+    static const int32_t PRECISION_RAW_Y_MIN = TouchInputMapperTest::RAW_Y_MIN;
+    static const int32_t PRECISION_RAW_Y_MAX = PRECISION_RAW_Y_MIN + DISPLAY_HEIGHT * 4 - 1;
+
+    static const std::array<Point, 4> kRawCorners;
+};
+
+const std::array<Point, 4> TouchscreenPrecisionTestsFixture::kRawCorners = {{
+        {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MIN}, // left-top
+        {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MIN}, // right-top
+        {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MAX}, // right-bottom
+        {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MAX}, // left-bottom
+}};
+
+// Tests for how the touchscreen is oriented relative to the natural orientation of the display.
+// For example, if a touchscreen is configured with an orientation of 90 degrees, it is a portrait
+// touchscreen panel that is used on a device whose natural display orientation is in landscape.
+TEST_P(TouchscreenPrecisionTestsFixture, OrientationPrecision) {
+    enum class Orientation {
+        ORIENTATION_0 = ui::toRotationInt(ui::ROTATION_0),
+        ORIENTATION_90 = ui::toRotationInt(ui::ROTATION_90),
+        ORIENTATION_180 = ui::toRotationInt(ui::ROTATION_180),
+        ORIENTATION_270 = ui::toRotationInt(ui::ROTATION_270),
+        ftl_last = ORIENTATION_270,
+    };
+    using Orientation::ORIENTATION_0, Orientation::ORIENTATION_90, Orientation::ORIENTATION_180,
+            Orientation::ORIENTATION_270;
+    static const std::map<Orientation, std::array<vec2, 4> /*mappedCorners*/> kMappedCorners = {
+            {ORIENTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+            {ORIENTATION_90, {{{0, 479.5}, {0, 0}, {799.75, 0}, {799.75, 479.5}}}},
+            {ORIENTATION_180, {{{479.5, 799.75}, {0, 799.75}, {0, 0}, {479.5, 0}}}},
+            {ORIENTATION_270, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+    };
+
+    const auto touchscreenOrientation = static_cast<Orientation>(ui::toRotationInt(GetParam()));
+
+    // Configure the touchscreen as being installed in the one of the four different orientations
+    // relative to the display.
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    addConfigurationProperty("touch.orientation", ftl::enum_string(touchscreenOrientation).c_str());
+    prepareDisplay(ui::ROTATION_0);
+
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    // If the touchscreen is installed in a rotated orientation relative to the display (i.e. in
+    // orientations of either 90 or 270) this means the display's natural resolution will be
+    // flipped.
+    const bool displayRotated =
+            touchscreenOrientation == ORIENTATION_90 || touchscreenOrientation == ORIENTATION_270;
+    const int32_t width = displayRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+    const int32_t height = displayRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+    const Rect physicalFrame{0, 0, width, height};
+    configurePhysicalDisplay(ui::ROTATION_0, physicalFrame, width, height);
+
+    const auto& expectedPoints = kMappedCorners.at(touchscreenOrientation);
+    const float expectedPrecisionX = displayRotated ? 4 : 2;
+    const float expectedPrecisionY = displayRotated ? 2 : 4;
+
+    // Test all four corners.
+    for (int i = 0; i < 4; i++) {
+        const auto& raw = kRawCorners[i];
+        processDown(mapper, raw.x, raw.y);
+        processSync(mapper);
+        const auto& expected = expectedPoints[i];
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                      WithCoords(expected.x, expected.y),
+                      WithPrecision(expectedPrecisionX, expectedPrecisionY))))
+                << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+                << "with touchscreen orientation "
+                << ftl::enum_string(touchscreenOrientation).c_str() << ", expected point ("
+                << expected.x << ", " << expected.y << ").";
+        processUp(mapper);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                      WithCoords(expected.x, expected.y))));
+    }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionWhenOrientationAware) {
+    static const std::map<ui::Rotation /*rotation*/, std::array<vec2, 4> /*mappedCorners*/>
+            kMappedCorners = {
+                    {ui::ROTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+                    {ui::ROTATION_90, {{{0.5, 0}, {480, 0}, {480, 799.75}, {0.5, 799.75}}}},
+                    {ui::ROTATION_180, {{{0.5, 0.25}, {480, 0.25}, {480, 800}, {0.5, 800}}}},
+                    {ui::ROTATION_270, {{{0, 0.25}, {479.5, 0.25}, {479.5, 800}, {0, 800}}}},
+            };
+
+    const ui::Rotation displayRotation = GetParam();
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(displayRotation);
+
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+    // Test all four corners.
+    for (int i = 0; i < 4; i++) {
+        const auto& expected = expectedPoints[i];
+        const auto& raw = kRawCorners[i];
+        processDown(mapper, raw.x, raw.y);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                      WithCoords(expected.x, expected.y), WithPrecision(2, 4))))
+                << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+                << "with display rotation " << ui::toCString(displayRotation)
+                << ", expected point (" << expected.x << ", " << expected.y << ").";
+        processUp(mapper);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                      WithCoords(expected.x, expected.y))));
+    }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionOrientationAwareInOri270) {
+    static const std::map<ui::Rotation /*orientation*/, std::array<vec2, 4> /*mappedCorners*/>
+            kMappedCorners = {
+                    {ui::ROTATION_0, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+                    {ui::ROTATION_90, {{{800, 0}, {800, 479.5}, {0.25, 479.5}, {0.25, 0}}}},
+                    {ui::ROTATION_180, {{{800, 0.5}, {800, 480}, {0.25, 480}, {0.25, 0.5}}}},
+                    {ui::ROTATION_270, {{{799.75, 0.5}, {799.75, 480}, {0, 480}, {0, 0.5}}}},
+            };
+
+    const ui::Rotation displayRotation = GetParam();
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    // Ori 270, so width and height swapped
+    const Rect physicalFrame{0, 0, DISPLAY_HEIGHT, DISPLAY_WIDTH};
+    prepareDisplay(displayRotation);
+    configurePhysicalDisplay(displayRotation, physicalFrame, DISPLAY_HEIGHT, DISPLAY_WIDTH);
+
+    const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+    // Test all four corners.
+    for (int i = 0; i < 4; i++) {
+        const auto& expected = expectedPoints[i];
+        const auto& raw = kRawCorners[i];
+        processDown(mapper, raw.x, raw.y);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                      WithCoords(expected.x, expected.y), WithPrecision(4, 2))))
+                << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+                << "with display rotation " << ui::toCString(displayRotation)
+                << ", expected point (" << expected.x << ", " << expected.y << ").";
+        processUp(mapper);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                      WithCoords(expected.x, expected.y))));
+    }
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFixture,
+                         ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+                                           ui::ROTATION_270),
+                         [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) {
+                             return ftl::enum_string(testParamInfo.param);
+                         });
+
 // --- ExternalStylusFusionTest ---
 
 class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index b9d9607..edd14f8 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -176,4 +176,10 @@
     return arg.downTime == downTime;
 }
 
+MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
+    *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
+                     << ", but got " << arg.xPrecision << " and " << arg.yPrecision;
+    return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
+}
+
 } // namespace android
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 4676167..9e41a93 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -97,9 +97,14 @@
     return calling_pid == request_pid;
 }
 
-MemtrackProxy::MemtrackProxy()
-      : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
-        memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+MemtrackProxy::MemtrackProxy() {
+    memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance();
+
+    // Only check for a HIDL implementation if we failed to get the AIDL service
+    if (!memtrack_aidl_instance_) {
+        memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance();
+    }
+}
 
 ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
                                             std::vector<MemtrackRecord>* _aidl_return) {
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 4ac9651..4fff8bb 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -28,10 +28,10 @@
 
 SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
         uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
-        const String16& opPackageName)
+        const String16& opPackageName, int deviceId)
         : mService(service), mUid(uid), mMem(*mem),
         mHalChannelHandle(halChannelHandle),
-        mOpPackageName(opPackageName), mDestroyed(false) {
+        mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
     mUserId = multiuser_get_user_id(mUid);
     ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
 }
@@ -180,8 +180,7 @@
     };
 
     Mutex::Autolock _l(mConnectionLock);
-    SensorDevice& dev(SensorDevice::getInstance());
-    int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+    int ret = configure(handle, &config);
 
     if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
         if (ret == NO_ERROR) {
@@ -224,7 +223,6 @@
     std::unordered_map<int, int>& existingConnections =
                     (!temporarilyStopped) ? mActivated : mActivatedBackup;
 
-    SensorDevice& dev(SensorDevice::getInstance());
     for (auto &i : existingConnections) {
         int handle = i.first;
         int rateLevel = i.second;
@@ -239,8 +237,8 @@
                 // Only reconfigure the channel if it's ongoing
                 if (!temporarilyStopped) {
                     // Stopping before reconfiguring is the well-tested path in CTS
-                    dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
-                    dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig);
+                    configure(handle, &stopConfig);
+                    configure(handle, &capConfig);
                 }
             }
         }
@@ -258,7 +256,6 @@
     const struct sensors_direct_cfg_t stopConfig = {
         .rate_level = SENSOR_DIRECT_RATE_STOP
     };
-    SensorDevice& dev(SensorDevice::getInstance());
     for (auto &i : mMicRateBackup) {
         int handle = i.first;
         int rateLevel = i.second;
@@ -273,13 +270,23 @@
         // Only reconfigure the channel if it's ongoing
         if (!temporarilyStopped) {
             // Stopping before reconfiguring is the well-tested path in CTS
-            dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
-            dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+            configure(handle, &stopConfig);
+            configure(handle, &config);
         }
     }
     mMicRateBackup.clear();
 }
 
+int SensorService::SensorDirectConnection::configure(
+        int handle, const sensors_direct_cfg_t* config) {
+    if (mDeviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+        SensorDevice& dev(SensorDevice::getInstance());
+        return dev.configureDirectChannel(handle, getHalChannelHandle(), config);
+    } else {
+        return mService->configureRuntimeSensorDirectChannel(handle, this, config);
+    }
+}
+
 void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
     Mutex::Autolock _l(mConnectionLock);
     stopAllLocked(backupRecord);
@@ -290,9 +297,8 @@
         .rate_level = SENSOR_DIRECT_RATE_STOP
     };
 
-    SensorDevice& dev(SensorDevice::getInstance());
     for (auto &i : mActivated) {
-        dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+        configure(i.first, &config);
     }
 
     if (backupRecord && mActivatedBackup.empty()) {
@@ -306,8 +312,6 @@
     if (!mActivatedBackup.empty()) {
         stopAllLocked(false);
 
-        SensorDevice& dev(SensorDevice::getInstance());
-
         // recover list of report from backup
         ALOG_ASSERT(mActivated.empty(),
                     "mActivated must be empty if mActivatedBackup was non-empty");
@@ -319,7 +323,7 @@
             struct sensors_direct_cfg_t config = {
                 .rate_level = i.second
             };
-            dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+            configure(i.first, &config);
         }
     }
 }
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index d39a073..bfaf811 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -39,7 +39,7 @@
 public:
     SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
             const sensors_direct_mem_t *mem, int32_t halChannelHandle,
-            const String16& opPackageName);
+            const String16& opPackageName, int deviceId);
     void dump(String8& result) const;
     void dump(util::ProtoOutputStream* proto) const;
     uid_t getUid() const { return mUid; }
@@ -53,6 +53,7 @@
     void onSensorAccessChanged(bool hasAccess);
     void onMicSensorAccessChanged(bool isMicToggleOn);
     userid_t getUserId() const { return mUserId; }
+    int getDeviceId() const { return mDeviceId; }
 
 protected:
     virtual ~SensorDirectConnection();
@@ -68,6 +69,9 @@
 private:
     bool hasSensorAccess() const;
 
+    // Sends the configuration to the relevant sensor device.
+    int configure(int handle, const sensors_direct_cfg_t* config);
+
     // Stops all active sensor direct report requests.
     //
     // If backupRecord is true, stopped requests can be recovered
@@ -95,6 +99,7 @@
     const sensors_direct_mem_t mMem;
     const int32_t mHalChannelHandle;
     const String16 mOpPackageName;
+    const int mDeviceId;
 
     mutable Mutex mConnectionLock;
     std::unordered_map<int, int> mActivated;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3a0329c..0fb3cad 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -181,7 +181,7 @@
             handle, sensor.type, sensor.name);
 
     sp<RuntimeSensor::SensorCallback> runtimeSensorCallback(
-        new RuntimeSensorCallbackProxy(std::move(callback)));
+            new RuntimeSensorCallbackProxy(callback));
     sensor_t runtimeSensor = sensor;
     // force the handle to be consistent
     runtimeSensor.handle = handle;
@@ -193,11 +193,15 @@
         return mSensors.getNonSensor().getHandle();
     }
 
+    if (mRuntimeSensorCallbacks.find(deviceId) == mRuntimeSensorCallbacks.end()) {
+        mRuntimeSensorCallbacks.emplace(deviceId, callback);
+    }
     return handle;
 }
 
 status_t SensorService::unregisterRuntimeSensor(int handle) {
     ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+    int deviceId = getDeviceIdFromHandle(handle);
     {
         Mutex::Autolock _l(mLock);
         if (!unregisterDynamicSensorLocked(handle)) {
@@ -210,6 +214,20 @@
     for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
         connection->removeSensor(handle);
     }
+
+    // If this was the last sensor for this device, remove its callback.
+    bool deviceHasSensors = false;
+    mSensors.forEachEntry(
+            [&deviceId, &deviceHasSensors] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+                if (e.deviceId == deviceId) {
+                    deviceHasSensors = true;
+                    return false;  // stop iterating
+                }
+                return true;
+            });
+    if (!deviceHasSensors) {
+        mRuntimeSensorCallbacks.erase(deviceId);
+    }
     return OK;
 }
 
@@ -1517,7 +1535,7 @@
 }
 
 sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
-        const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
+        const String16& opPackageName, int deviceId, uint32_t size, int32_t type, int32_t format,
         const native_handle *resource) {
     resetTargetSdkVersionCache(opPackageName);
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
@@ -1593,14 +1611,25 @@
     native_handle_set_fdsan_tag(clone);
 
     sp<SensorDirectConnection> conn;
-    SensorDevice& dev(SensorDevice::getInstance());
-    int channelHandle = dev.registerDirectChannel(&mem);
+    int channelHandle = 0;
+    if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+        SensorDevice& dev(SensorDevice::getInstance());
+        channelHandle = dev.registerDirectChannel(&mem);
+    } else {
+        auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+        if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+            ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+        } else {
+            int fd = dup(clone->data[0]);
+            channelHandle = runtimeSensorCallback->second->onDirectChannelCreated(fd);
+        }
+    }
 
     if (channelHandle <= 0) {
         ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
     } else {
         mem.handle = clone;
-        conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName);
+        conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
     }
 
     if (conn == nullptr) {
@@ -1614,6 +1643,24 @@
     return conn;
 }
 
+int SensorService::configureRuntimeSensorDirectChannel(
+        int sensorHandle, const SensorDirectConnection* c, const sensors_direct_cfg_t* config) {
+    int deviceId = c->getDeviceId();
+    int sensorDeviceId = getDeviceIdFromHandle(sensorHandle);
+    if (sensorDeviceId != c->getDeviceId()) {
+        ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs"
+              "to device %d", c->getDeviceId(), sensorDeviceId);
+        return BAD_VALUE;
+    }
+    auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+    if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+        ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+        return BAD_VALUE;
+    }
+    return runtimeSensorCallback->second->onDirectChannelConfigured(
+            c->getHalChannelHandle(), sensorHandle, config->rate_level);
+}
+
 int SensorService::setOperationParameter(
             int32_t handle, int32_t type,
             const Vector<float> &floats, const Vector<int32_t> &ints) {
@@ -1769,8 +1816,18 @@
 void SensorService::cleanupConnection(SensorDirectConnection* c) {
     Mutex::Autolock _l(mLock);
 
-    SensorDevice& dev(SensorDevice::getInstance());
-    dev.unregisterDirectChannel(c->getHalChannelHandle());
+    int deviceId = c->getDeviceId();
+    if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+        SensorDevice& dev(SensorDevice::getInstance());
+        dev.unregisterDirectChannel(c->getHalChannelHandle());
+    } else {
+        auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+        if (runtimeSensorCallback != mRuntimeSensorCallbacks.end()) {
+            runtimeSensorCallback->second->onDirectChannelDestroyed(c->getHalChannelHandle());
+        } else {
+            ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+        }
+    }
     mConnectionHolder.removeDirectConnection(c);
 }
 
@@ -1848,6 +1905,19 @@
     return mSensors.getInterface(handle);
 }
 
+int SensorService::getDeviceIdFromHandle(int handle) const {
+    int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID;
+    mSensors.forEachEntry(
+            [&deviceId, handle] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+                if (e.si->getSensor().getHandle() == handle) {
+                    deviceId = e.deviceId;
+                    return false;  // stop iterating
+                }
+                return true;
+            });
+    return deviceId;
+}
+
 status_t SensorService::enable(const sp<SensorEventConnection>& connection,
         int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
         const String16& opPackageName) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3f6a895..fe72a69 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -154,6 +154,10 @@
         virtual status_t onConfigurationChanged(int handle, bool enabled,
                                                 int64_t samplingPeriodNanos,
                                                 int64_t batchReportLatencyNanos) = 0;
+        virtual int onDirectChannelCreated(int fd) = 0;
+        virtual void onDirectChannelDestroyed(int channelHandle) = 0;
+        virtual int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+                                              int rateLevel) = 0;
     };
 
     static char const* getServiceName() ANDROID_API { return "sensorservice"; }
@@ -187,6 +191,9 @@
     status_t unregisterRuntimeSensor(int handle) ANDROID_API;
     status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
 
+    int configureRuntimeSensorDirectChannel(int sensorHandle, const SensorDirectConnection* c,
+                                            const sensors_direct_cfg_t* config);
+
     // Returns true if a sensor should be throttled according to our rate-throttling rules.
     static bool isSensorInCappedSet(int sensorType);
 
@@ -370,7 +377,8 @@
             int requestedMode, const String16& opPackageName, const String16& attributionTag);
     virtual int isDataInjectionEnabled();
     virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
-            uint32_t size, int32_t type, int32_t format, const native_handle *resource);
+            int deviceId, uint32_t size, int32_t type, int32_t format,
+            const native_handle *resource);
     virtual int setOperationParameter(
             int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
     virtual status_t dump(int fd, const Vector<String16>& args);
@@ -380,6 +388,7 @@
     String8 getSensorStringType(int handle) const;
     bool isVirtualSensor(int handle) const;
     std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+    int getDeviceIdFromHandle(int handle) const;
     bool isWakeUpSensor(int type) const;
     void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
     static void sortEventBuffer(sensors_event_t* buffer, size_t count);
@@ -517,6 +526,7 @@
     std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
     Mode mCurrentOperatingMode;
     std::queue<sensors_event_t> mRuntimeSensorEventQueue;
+    std::unordered_map</*deviceId*/int, sp<RuntimeSensorCallback>> mRuntimeSensorCallbacks;
 
     // true if the head tracker sensor type is currently restricted to system usage only
     // (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
index 1b63d76..ee8ceb3 100644
--- a/services/sensorservice/aidl/fuzzer/fuzzer.cpp
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -16,7 +16,7 @@
 #include <fuzzbinder/libbinder_ndk_driver.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
-#include <ServiceManager.h>
+#include <fakeservicemanager/FakeServiceManager.h>
 #include <android-base/logging.h>
 #include <android/binder_interface_utils.h>
 #include <fuzzbinder/random_binder.h>
@@ -29,7 +29,7 @@
 [[clang::no_destroy]] static std::once_flag gSmOnce;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+    static android::sp<android::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager();
     std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
     fakeServiceManager->clear();
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 8555fd6..1a56ab7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -147,6 +147,7 @@
     MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
     MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
                 getOverlaySupport, (), (const, override));
+    MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9ad2edb..aa83883 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1312,6 +1312,18 @@
                                  false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
+    mLayerFEState.compositionType = Composition::REFRESH_RATE_INDICATOR;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
 /*
  * OutputLayer::uncacheBuffers
  */
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5f73fbc..3cdb3d5 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -408,8 +408,8 @@
                            capabilities.getDesiredMinLuminance());
 }
 
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
-                                             bool showInMiddle) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
+                                             bool showRenderRate, bool showInMiddle) {
     if (!enable) {
         mRefreshRateOverlay.reset();
         return;
@@ -428,11 +428,22 @@
         features |= RefreshRateOverlay::Features::ShowInMiddle;
     }
 
+    if (setByHwc) {
+        features |= RefreshRateOverlay::Features::SetByHwc;
+    }
+
     const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+    updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+}
+
+void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
+    ATRACE_CALL();
+    if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
+        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
+    }
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
@@ -441,7 +452,7 @@
         const auto newMode =
                 mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
         if (newMode) {
-            mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
+            updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps);
             return true;
         }
     }
@@ -510,21 +521,21 @@
     mDesiredActiveModeChanged = false;
 }
 
-void DisplayDevice::adjustRefreshRate(Fps leaderDisplayRefreshRate) {
+void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
     using fps_approx_ops::operator==;
     if (mRequestedRefreshRate == 0_Hz) {
         return;
     }
 
     using fps_approx_ops::operator>;
-    if (mRequestedRefreshRate > leaderDisplayRefreshRate) {
-        mAdjustedRefreshRate = leaderDisplayRefreshRate;
+    if (mRequestedRefreshRate > pacesetterDisplayRefreshRate) {
+        mAdjustedRefreshRate = pacesetterDisplayRefreshRate;
         return;
     }
 
     unsigned divisor = static_cast<unsigned>(
-            std::round(leaderDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
-    mAdjustedRefreshRate = leaderDisplayRefreshRate / divisor;
+            std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+    mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b86d9be..d9c3e1c 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -237,8 +237,9 @@
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
+    void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
                                   bool showInMiddle) REQUIRES(kMainThreadContext);
+    void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
@@ -247,9 +248,9 @@
 
     Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
 
-    // Round the requested refresh rate to match a divisor of the leader
+    // Round the requested refresh rate to match a divisor of the pacesetter
     // display's refresh rate. Only supported for virtual displays.
-    void adjustRefreshRate(Fps leaderDisplayRefreshRate);
+    void adjustRefreshRate(Fps pacesetterDisplayRefreshRate);
 
     // release HWC resources (if any) for removable displays
     void disconnect();
@@ -290,7 +291,7 @@
     // for virtual displays to match this requested refresh rate.
     const Fps mRequestedRefreshRate;
 
-    // Adjusted refresh rate, rounded to match a divisor of the leader
+    // Adjusted refresh rate, rounded to match a divisor of the pacesetter
     // display's refresh rate. Only supported for virtual displays.
     Fps mAdjustedRefreshRate = 0_Hz;
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 4194a7e..bd2680f 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1428,6 +1428,19 @@
     return Error::NONE;
 }
 
+Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) {
+    const auto status =
+            mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>(
+                                                                                   displayId),
+                                                                           enabled);
+    if (!status.isOk()) {
+        ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s",
+              status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
 Error AidlComposer::getClientTargetProperty(
         Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
     Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d163ff2..8313c09 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -239,6 +239,7 @@
     void onHotplugDisconnect(Display) override;
     Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
     Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override;
+    Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 9b9b7fd..c65c572 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -294,6 +294,7 @@
             std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
     virtual Error setHdrConversionStrategy(
             ::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
+    virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 470bf76..28148ac 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -808,6 +808,21 @@
     return NO_ERROR;
 }
 
+status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId,
+                                                               bool enabled) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId]
+                                                                         .hwcDisplay->getId(),
+                                                                 enabled);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in setting refresh refresh rate change callback debug enabled %s",
+              to_string(error).c_str());
+        return INVALID_OPERATION;
+    }
+    return NO_ERROR;
+}
+
 status_t HWComposer::getDisplayDecorationSupport(
         PhysicalDisplayId displayId,
         std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 95568eb..7a3f41c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -297,6 +297,7 @@
     virtual status_t setHdrConversionStrategy(
             aidl::android::hardware::graphics::common::HdrConversionStrategy,
             aidl::android::hardware::graphics::common::Hdr*) = 0;
+    virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -453,6 +454,7 @@
     status_t setHdrConversionStrategy(
             aidl::android::hardware::graphics::common::HdrConversionStrategy,
             aidl::android::hardware::graphics::common::Hdr*) override;
+    status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 9bc62b6..23de4fa 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1358,6 +1358,10 @@
     return Error::UNSUPPORTED;
 }
 
+Error HidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display, bool) {
+    return Error::UNSUPPORTED;
+}
+
 Error HidlComposer::getClientTargetProperty(
         Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
     IComposerClient::ClientTargetProperty property;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 2bab1fe..d04652b 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -348,6 +348,7 @@
             override;
     Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
                                    Hdr*) override;
+    Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 14d67c6..c30465f 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -130,6 +130,14 @@
     return mLayer;
 }
 
+const LayerHierarchy* LayerHierarchy::getRelativeParent() const {
+    return mRelativeParent;
+}
+
+const LayerHierarchy* LayerHierarchy::getParent() const {
+    return mParent;
+}
+
 std::string LayerHierarchy::getDebugStringShort() const {
     std::string debug = "LayerHierarchy{";
     debug += ((mLayer) ? mLayer->getDebugString() : "root") + " ";
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 0f700a9..3dd89ba 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -144,6 +144,8 @@
     }
 
     const RequestedLayerState* getLayer() const;
+    const LayerHierarchy* getRelativeParent() const;
+    const LayerHierarchy* getParent() const;
     std::string getDebugString(const char* prefix = "") const;
     std::string getDebugStringShort() const;
     // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 360a0a9..33cc429 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -187,6 +187,10 @@
                 continue;
             }
 
+            if (transaction.flags & ISurfaceComposer::eAnimation) {
+                layer->changes |= RequestedLayerState::Changes::Animation;
+            }
+
             uint32_t oldParentId = layer->parentId;
             uint32_t oldRelativeParentId = layer->relativeParentId;
             uint32_t oldTouchCropId = layer->touchCropId;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 85b00d7..8a45093 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,12 +27,16 @@
 LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
                              const LayerHierarchy::TraversalPath& path)
       : path(path) {
+    static uint32_t sUniqueSequenceId = 0;
+    // Provide a unique id for clones otherwise keeping using the sequence id.
+    // The seq id can still be useful for debugging if its available.
+    uniqueSequence = (path.isClone()) ? sUniqueSequenceId++ : state.id;
     sequence = static_cast<int32_t>(state.id);
     name = state.name;
     textureName = state.textureName;
     premultipliedAlpha = state.premultipliedAlpha;
     inputInfo.name = state.name;
-    inputInfo.id = static_cast<int32_t>(state.id);
+    inputInfo.id = static_cast<int32_t>(uniqueSequence);
     inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
     inputInfo.ownerPid = state.ownerPid;
     changes = RequestedLayerState::Changes::Created;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 07aa122..6fb2f57 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -57,6 +57,12 @@
     bool isHiddenByPolicyFromParent = false;
     bool isHiddenByPolicyFromRelativeParent = false;
     ftl::Flags<RequestedLayerState::Changes> changes;
+    // Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
+    // For mirrored layers, snapshots will have the same sequence so this unique id provides
+    // an alternative identifier when needed.
+    uint32_t uniqueSequence;
+    // Layer id used to create this snapshot. Multiple snapshots will have the same sequence if they
+    // generated from the same layer, for example when mirroring.
     int32_t sequence;
     std::string name;
     uint32_t textureName;
@@ -84,6 +90,7 @@
     scheduler::LayerInfo::FrameRate frameRate;
     ui::Transform::RotationFlags fixedTransformHint;
     bool handleSkipScreenshotFlag = false;
+    int32_t frameRateSelectionPriority;
     LayerHierarchy::TraversalPath mirrorRootPath;
     bool unreachable = true;
     ChildState childState;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index db6e716..a16de1b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -274,7 +274,7 @@
     // InputDispatcher, and obviously if they aren't visible they can't occlude
     // anything.
     const bool visibleForInput =
-            (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
+            snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
     snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
 }
 
@@ -773,6 +773,7 @@
         snapshot.layerOpaqueFlagSet =
                 (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
         snapshot.cachingHint = requested.cachingHint;
+        snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
     }
 
     if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
@@ -954,12 +955,13 @@
         snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
     } else {
         snapshot.inputInfo = {};
+        // b/271132344 revisit this and see if we can always use the layers uid/pid
+        snapshot.inputInfo.name = requested.name;
+        snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
+        snapshot.inputInfo.ownerPid = requested.ownerPid;
     }
 
-    snapshot.inputInfo.name = requested.name;
-    snapshot.inputInfo.id = static_cast<int32_t>(requested.id);
-    snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
-    snapshot.inputInfo.ownerPid = requested.ownerPid;
+    snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
     snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
     if (!needsInputInfo(snapshot, requested)) {
         return;
@@ -983,7 +985,9 @@
     }
 
     snapshot.inputInfo.alpha = snapshot.color.a;
-    snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode;
+    snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
+            ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+            : parentSnapshot.inputInfo.touchOcclusionMode;
     if (requested.dropInputMode == gui::DropInputMode::ALL ||
         parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
         snapshot.dropInputMode = gui::DropInputMode::ALL;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 8b475a3..6f5485d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -53,6 +53,7 @@
         VisibleRegion = 1u << 14,
         Buffer = 1u << 15,
         SidebandStream = 1u << 16,
+        Animation = 1u << 17,
     };
     static Rect reduce(const Rect& win, const Region& exclude);
     RequestedLayerState(const LayerCreationArgs&);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5a4193d..0f2af2f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,8 @@
         mDrawingState.color.b = -1.0_hf;
     }
 
-    mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns());
+    mFrameTracker.setDisplayRefreshPeriod(
+            args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
 
     mOwnerUid = args.ownerUid;
     mOwnerPid = args.ownerPid;
@@ -680,6 +681,9 @@
     } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
         snapshot->compositionType =
                 aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
     } else {
         // Normal buffer layers
         snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
@@ -1253,7 +1257,7 @@
         return parentFrameRate;
     }();
 
-    *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+    *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
 
     // The frame rate is propagated to the children
     bool childrenHaveFrameRate = false;
@@ -1267,7 +1271,7 @@
     if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
         childrenHaveFrameRate) {
         *transactionNeeded |=
-                setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+                setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
     }
 
     // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
@@ -1419,7 +1423,7 @@
     return surfaceFrame;
 }
 
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
     if (mDrawingState.frameRateForLayerTree == frameRate) {
         return false;
     }
@@ -1432,9 +1436,21 @@
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-    mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
+    mFlinger->mScheduler
+            ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+                                 scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
+    return true;
+}
 
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+    if (mDrawingState.frameRateForLayerTree == frameRate) {
+        return false;
+    }
+
+    mDrawingState.frameRateForLayerTree = frameRate;
+    mFlinger->mScheduler
+            ->recordLayerHistory(sequence, layerProps, systemTime(),
+                                 scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
     return true;
 }
 
@@ -2096,15 +2112,7 @@
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
     if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
-        ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
-
-        // Only populate for the primary display.
-        if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
-            const auto compositionType = getCompositionType(*display);
-            layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
-            LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
-                                           [&]() { return layerProto->mutable_visible_region(); });
-        }
+        writeCompositionStateToProto(layerProto);
     }
 
     for (const sp<Layer>& layer : mDrawingChildren) {
@@ -2114,6 +2122,18 @@
     return layerProto;
 }
 
+void Layer::writeCompositionStateToProto(LayerProto* layerProto) {
+    ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
+
+    // Only populate for the primary display.
+    if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
+        const auto compositionType = getCompositionType(*display);
+        layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+        LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
+                                       [&]() { return layerProto->mutable_visible_region(); });
+    }
+}
+
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo) {
     const ui::Transform transform = getTransform();
     auto buffer = getExternalTexture();
@@ -3033,6 +3053,10 @@
                                       mLastClientCompositionFence);
             mLastClientCompositionFence = nullptr;
         }
+    } else {
+        // if we are latching a buffer for the first time then clear the mLastLatchTime since
+        // we don't want to incorrectly classify a frame if we miss the desired present time.
+        updateLastLatchTime(0);
     }
 
     mDrawingState.producerId = bufferData.producerId;
@@ -3052,7 +3076,7 @@
     } else {
         mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
     }
-
+    mDrawingState.latchedVsyncId = info.vsyncId;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
@@ -3062,18 +3086,9 @@
     mDrawingState.desiredPresentTime = desiredPresentTime;
     mDrawingState.isAutoTimestamp = isAutoTimestamp;
 
-    const nsecs_t presentTime = [&] {
-        if (!isAutoTimestamp) return desiredPresentTime;
-
-        const auto prediction =
-                mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
-        if (prediction.has_value()) return prediction->presentTime;
-
-        return static_cast<nsecs_t>(0);
-    }();
-
-    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-    mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+    if (mFlinger->mLegacyFrontEndEnabled) {
+        recordLayerHistoryBufferUpdate(getLayerProps());
+    }
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
@@ -3090,6 +3105,32 @@
     return true;
 }
 
+void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) {
+    mDrawingState.desiredPresentTime = desiredPresentTime;
+    mDrawingState.isAutoTimestamp = isAutoTimestamp;
+}
+
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+    const nsecs_t presentTime = [&] {
+        if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime;
+
+        const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                mDrawingState.latchedVsyncId);
+        if (prediction.has_value()) return prediction->presentTime;
+
+        return static_cast<nsecs_t>(0);
+    }();
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+                                             scheduler::LayerHistory::LayerUpdateType::Buffer);
+}
+
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+    const nsecs_t presentTime =
+            mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+                                             scheduler::LayerHistory::LayerUpdateType::AnimationTX);
+}
+
 bool Layer::setDataspace(ui::Dataspace dataspace) {
     mDrawingState.dataspaceRequested = true;
     if (mDrawingState.dataspace == dataspace) return false;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 597f8ad..2fb122c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -228,6 +228,7 @@
         float currentSdrHdrRatio = 1.f;
         float desiredSdrHdrRatio = 1.f;
         gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+        int64_t latchedVsyncId = 0;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -305,6 +306,7 @@
                    const BufferData& /* bufferData */, nsecs_t /* postTime */,
                    nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
                    std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+    void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
     bool setDataspace(ui::Dataspace /*dataspace*/);
     bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
     bool setCachingHint(gui::CachingHint cachingHint);
@@ -608,6 +610,7 @@
     bool isRemovedFromCurrentState() const;
 
     LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags);
+    void writeCompositionStateToProto(LayerProto* layerProto);
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
@@ -850,7 +853,20 @@
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                    const sp<GraphicBuffer>& buffer, uint64_t framenumber,
                                    const sp<Fence>& releaseFence);
-    bool setFrameRateForLayerTree(FrameRate);
+    bool setFrameRateForLayerTreeLegacy(FrameRate);
+    bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
+    void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
+    void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+    auto getLayerProps() const {
+        return scheduler::LayerProps{
+                .visible = isVisible(),
+                .bounds = getBounds(),
+                .transform = getTransform(),
+                .setFrameRateVote = getFrameRateForLayerTree(),
+                .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+        };
+    };
+    bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
 
 protected:
     // For unit tests
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0506c47..55281fa 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -247,6 +247,153 @@
     outRegion.right = proto.right();
     outRegion.bottom = proto.bottom();
 }
+
+void LayerProtoHelper::writeHierarchyToProto(
+        LayersProto& outLayersProto, const frontend::LayerHierarchy& root,
+        const frontend::LayerSnapshotBuilder& snapshotBuilder,
+        const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags) {
+    using Variant = frontend::LayerHierarchy::Variant;
+    frontend::LayerSnapshot defaultSnapshot;
+
+    LayerProto* layerProto = outLayersProto.add_layers();
+    const frontend::RequestedLayerState& layer = *root.getLayer();
+    frontend::LayerSnapshot* snapshot = snapshotBuilder.getSnapshot(layer.id);
+
+    if (!snapshot) {
+        defaultSnapshot.uniqueSequence = layer.id;
+        snapshot = &defaultSnapshot;
+    }
+    writeSnapshotToProto(layerProto, layer, *snapshot, traceFlags);
+    for (const auto& [child, variant] : root.mChildren) {
+        if (variant == Variant::Attached || variant == Variant::Detached) {
+            layerProto->add_children(child->getLayer()->id);
+        } else if (variant == Variant::Relative) {
+            layerProto->add_relatives(child->getLayer()->id);
+        }
+    }
+
+    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);
+        }
+    }
+
+    for (const auto& [child, variant] : root.mChildren) {
+        // avoid visiting relative layers twice
+        if (variant == Variant::Detached) {
+            continue;
+        }
+        writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags);
+    }
+}
+
+void LayerProtoHelper::writeSnapshotToProto(LayerProto* layerInfo,
+                                            const frontend::RequestedLayerState& requestedState,
+                                            const frontend::LayerSnapshot& snapshot,
+                                            uint32_t traceFlags) {
+    const ui::Transform transform = snapshot.geomLayerTransform;
+    auto buffer = requestedState.externalTexture;
+    if (buffer != nullptr) {
+        LayerProtoHelper::writeToProto(*buffer,
+                                       [&]() { return layerInfo->mutable_active_buffer(); });
+        LayerProtoHelper::writeToProtoDeprecated(ui::Transform(requestedState.bufferTransform),
+                                                 layerInfo->mutable_buffer_transform());
+    }
+    layerInfo->set_invalidate(snapshot.contentDirty);
+    layerInfo->set_is_protected(snapshot.hasProtectedContent);
+    layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(snapshot.dataspace)));
+    layerInfo->set_curr_frame(requestedState.bufferData->frameNumber);
+    layerInfo->set_requested_corner_radius(requestedState.cornerRadius);
+    layerInfo->set_corner_radius(
+            (snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0);
+    layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius);
+    layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay);
+    LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+    LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+                                           [&]() { return layerInfo->mutable_position(); });
+    LayerProtoHelper::writeToProto(snapshot.geomLayerBounds,
+                                   [&]() { return layerInfo->mutable_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.surfaceDamage,
+                                   [&]() { return layerInfo->mutable_damage_region(); });
+
+    if (requestedState.hasColorTransform) {
+        LayerProtoHelper::writeToProto(snapshot.colorTransform,
+                                       layerInfo->mutable_color_transform());
+    }
+
+    LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+                                   [&]() { return layerInfo->mutable_source_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.transformedBounds,
+                                   [&]() { return layerInfo->mutable_screen_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
+                                   [&]() { return layerInfo->mutable_corner_radius_crop(); });
+    layerInfo->set_shadow_radius(snapshot.shadowRadius);
+
+    layerInfo->set_id(snapshot.uniqueSequence);
+    layerInfo->set_name(requestedState.name);
+    layerInfo->set_type("Layer");
+
+    LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+                                   [&]() { return layerInfo->mutable_transparent_region(); });
+
+    layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
+    layerInfo->set_z(requestedState.z);
+
+    ui::Transform requestedTransform = requestedState.getTransform(0);
+    LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+        return layerInfo->mutable_requested_position();
+    });
+
+    LayerProtoHelper::writeToProto(requestedState.crop,
+                                   [&]() { return layerInfo->mutable_crop(); });
+
+    layerInfo->set_is_opaque(snapshot.contentOpaque);
+    if (requestedState.externalTexture)
+        layerInfo->set_pixel_format(
+                decodePixelFormat(requestedState.externalTexture->getPixelFormat()));
+    LayerProtoHelper::writeToProto(snapshot.color, [&]() { return layerInfo->mutable_color(); });
+    LayerProtoHelper::writeToProto(requestedState.color,
+                                   [&]() { return layerInfo->mutable_requested_color(); });
+    layerInfo->set_flags(requestedState.flags);
+
+    LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+                                             layerInfo->mutable_requested_transform());
+
+    layerInfo->set_is_relative_of(requestedState.isRelativeOf);
+
+    layerInfo->set_owner_uid(requestedState.ownerUid);
+
+    if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
+        LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+                                       [&]() { return layerInfo->mutable_input_window_info(); });
+    }
+
+    if (traceFlags & LayerTracing::TRACE_EXTRA) {
+        auto protoMap = layerInfo->mutable_metadata();
+        for (const auto& entry : requestedState.metadata.mMap) {
+            (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+        }
+    }
+
+    LayerProtoHelper::writeToProto(requestedState.destinationFrame,
+                                   [&]() { return layerInfo->mutable_destination_frame(); });
+}
+
 } // namespace surfaceflinger
 } // namespace android
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 6ade143..de4bd01 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -58,6 +58,15 @@
     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);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 0ade467..9a4261d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -29,7 +29,6 @@
 #include <SkBlendMode.h>
 #include <SkRect.h>
 #include <SkSurface.h>
-#include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
 
 #undef LOG_TAG
@@ -46,15 +45,6 @@
 constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
 constexpr int kBufferHeight = kDigitHeight;
 
-SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
-    constexpr float kFrameRate = 0.f;
-    constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
-    constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
-
-    return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility,
-                                                             kSeamlessness);
-}
-
 } // namespace
 
 SurfaceControlHolder::~SurfaceControlHolder() {
@@ -242,7 +232,7 @@
         return;
     }
 
-    createTransaction(mSurfaceControl->get())
+    createTransaction()
             .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
             .setTrustedOverlay(mSurfaceControl->get(), true)
             .apply();
@@ -272,14 +262,14 @@
         }
     }();
 
-    createTransaction(mSurfaceControl->get())
-            .setTransform(mSurfaceControl->get(), transform)
-            .apply();
+    createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
 
     BufferCache::const_iterator it =
             mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
-        const int minFps = mFpsRange.min.getIntValue();
+        // HWC minFps is not known by the framework in order
+        // to consider lower rates we set minFps to 0.
+        const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
         // Clamp to the range. The current displayFps may be outside of this range if the display
@@ -327,7 +317,7 @@
         frame.offsetBy(width >> 1, height >> 4);
     }
 
-    createTransaction(mSurfaceControl->get())
+    createTransaction()
             .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
                        0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
             .setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -335,14 +325,14 @@
 }
 
 void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
-    createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
+    createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
 void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
     mDisplayFps = displayFps;
     mRenderFps = renderFps;
     const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
-    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+    createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
@@ -351,7 +341,23 @@
     const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
-    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+    createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const {
+    constexpr float kFrameRate = 0.f;
+    constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+    constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+    const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+    SurfaceComposerClient::Transaction transaction;
+    if (isSetByHwc()) {
+        transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
+                             layer_state_t::eLayerIsRefreshRateIndicator);
+    }
+    transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+    return transaction;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b68a88c..0b89b8e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -21,6 +21,7 @@
 
 #include <ftl/flags.h>
 #include <ftl/small_map.h>
+#include <gui/SurfaceComposerClient.h>
 #include <ui/LayerStack.h>
 #include <ui/Size.h>
 #include <ui/Transform.h>
@@ -55,6 +56,7 @@
         Spinner = 1 << 0,
         RenderRate = 1 << 1,
         ShowInMiddle = 1 << 2,
+        SetByHwc = 1 << 3,
     };
 
     RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
@@ -63,6 +65,7 @@
     void setViewport(ui::Size);
     void changeRefreshRate(Fps, Fps);
     void animate();
+    bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
 
 private:
     using Buffers = std::vector<sp<GraphicBuffer>>;
@@ -82,6 +85,8 @@
 
     const Buffers& getOrCreateBuffers(Fps, Fps);
 
+    SurfaceComposerClient::Transaction createTransaction() const;
+
     struct Key {
         int displayFps;
         int renderFps;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 839500f..327ca3f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -285,46 +285,72 @@
 
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
 
-    auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
-        bool stopLayerFound = false;
-        auto filterVisitor = [&](Layer* layer) {
-            // We don't want to capture any layers beyond the stop layer
-            if (stopLayerFound) return;
+    auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
+                             const ui::Transform transform, bool& outStopTraversal) -> bool {
+        // Likewise if we just found a stop layer, set the flag and abort
+        for (const auto& [area, stopLayerId, listener] : descriptors) {
+            if (stopLayerId != UNASSIGNED_LAYER_ID && layerId == stopLayerId) {
+                outStopTraversal = true;
+                return false;
+            }
+        }
 
-            // Likewise if we just found a stop layer, set the flag and abort
-            for (const auto& [area, stopLayerId, listener] : descriptors) {
-                if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) {
-                    stopLayerFound = true;
+        // Compute the layer's position on the screen
+        constexpr bool roundOutwards = true;
+        Rect transformed = transform.transform(bounds, roundOutwards);
+
+        // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
+        Rect ignore;
+        if (!transformed.intersect(sampledBounds, &ignore)) return false;
+
+        // If the layer doesn't intersect a sampling area, skip capturing it
+        bool intersectsAnyArea = false;
+        for (const auto& [area, stopLayer, listener] : descriptors) {
+            if (transformed.intersect(area, &ignore)) {
+                intersectsAnyArea = true;
+                listeners.insert(listener);
+            }
+        }
+        if (!intersectsAnyArea) return false;
+
+        ALOGV("Traversing [%s] [%d, %d, %d, %d]", layerName, bounds.left, bounds.top, bounds.right,
+              bounds.bottom);
+
+        return true;
+    };
+
+    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
+    if (mFlinger.mLayerLifecycleManagerEnabled) {
+        auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
+                            bool& outStopTraversal) -> bool {
+            const Rect bounds =
+                    frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+                                                          snapshot.transparentRegionHint);
+            const ui::Transform transform = snapshot.geomLayerTransform;
+            return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+                                 outStopTraversal);
+        };
+        getLayerSnapshots =
+                mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+                                                         filterFn);
+    } else {
+        auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
+            bool stopLayerFound = false;
+            auto filterVisitor = [&](Layer* layer) {
+                // We don't want to capture any layers beyond the stop layer
+                if (stopLayerFound) return;
+
+                if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
+                                   Rect(layer->getBounds()), layer->getTransform(),
+                                   stopLayerFound)) {
                     return;
                 }
-            }
-
-            // Compute the layer's position on the screen
-            const Rect bounds = Rect(layer->getBounds());
-            const ui::Transform transform = layer->getTransform();
-            constexpr bool roundOutwards = true;
-            Rect transformed = transform.transform(bounds, roundOutwards);
-
-            // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
-            Rect ignore;
-            if (!transformed.intersect(sampledBounds, &ignore)) return;
-
-            // If the layer doesn't intersect a sampling area, skip capturing it
-            bool intersectsAnyArea = false;
-            for (const auto& [area, stopLayer, listener] : descriptors) {
-                if (transformed.intersect(area, &ignore)) {
-                    intersectsAnyArea = true;
-                    listeners.insert(listener);
-                }
-            }
-            if (!intersectsAnyArea) return;
-
-            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
-                  bounds.top, bounds.right, bounds.bottom);
-            visitor(layer);
+                visitor(layer);
+            };
+            mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
         };
-        mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
-    };
+        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    }
 
     std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
     if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -344,7 +370,6 @@
                                                      renderengine::impl::ExternalTexture::Usage::
                                                              WRITEABLE);
     }
-    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
     constexpr bool kRegionSampling = true;
     constexpr bool kGrayscale = false;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e853833..beaf972 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -118,27 +118,17 @@
     }
 }
 
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
-                          LayerUpdateType updateType) {
+void LayerHistory::record(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+                          nsecs_t now, LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
-    auto id = layer->getSequence();
-
     auto [found, layerPair] = findLayer(id);
     if (found == LayerStatus::NotFound) {
         // Offscreen layer
-        ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+        ALOGV("%s: %d not registered", __func__, id);
         return;
     }
 
     const auto& info = layerPair->second;
-    const auto layerProps = LayerInfo::LayerProps{
-            .visible = layer->isVisible(),
-            .bounds = layer->getBounds(),
-            .transform = layer->getTransform(),
-            .setFrameRateVote = layer->getFrameRateForLayerTree(),
-            .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
-    };
-
     info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Set frame rate to attached choreographer.
@@ -149,7 +139,7 @@
         while (it != range.second) {
             sp<EventThreadConnection> choreographerConnection = it->second.promote();
             if (choreographerConnection) {
-                choreographerConnection->frameRate = layer->getFrameRateForLayerTree().rate;
+                choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
                 it++;
             } else {
                 it = mAttachedChoreographers.erase(it);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 68e7030..69caf9f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -38,6 +38,7 @@
 namespace scheduler {
 
 class LayerInfo;
+struct LayerProps;
 
 class LayerHistory {
 public:
@@ -63,7 +64,8 @@
     };
 
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
+    void record(int32_t id, const LayerProps& props, nsecs_t presentTime, nsecs_t now,
+                LayerUpdateType updateType);
 
     // Updates the default frame rate compatibility which takes effect when the app
     // does not set a preference for refresh rate.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 0142ccd..5a90d58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -44,14 +44,17 @@
         mOwnerUid(ownerUid),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps()}),
-        mRefreshRateHistory(name) {}
+        mLayerProps(std::make_unique<LayerProps>()),
+        mRefreshRateHistory(name) {
+    ;
+}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                                   bool pendingModeChange, LayerProps props) {
+                                   bool pendingModeChange, const LayerProps& props) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
-    mLayerProps = props;
+    *mLayerProps = props;
     switch (updateType) {
         case LayerUpdateType::AnimationTX:
             mLastAnimationTime = std::max(lastPresentTime, now);
@@ -305,6 +308,26 @@
     return mTraceTags.at(type).c_str();
 }
 
+LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
+    return mLayerProps->setFrameRateVote;
+}
+
+bool LayerInfo::isVisible() const {
+    return mLayerProps->visible;
+}
+
+int32_t LayerInfo::getFrameRateSelectionPriority() const {
+    return mLayerProps->frameRateSelectionPriority;
+}
+
+FloatRect LayerInfo::getBounds() const {
+    return mLayerProps->bounds;
+}
+
+ui::Transform LayerInfo::getTransform() const {
+    return mLayerProps->transform;
+}
+
 LayerInfo::RefreshRateHistory::HeuristicTraceTagData
 LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
     const std::string prefix = "LFPS ";
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 93485be..a3523ac 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -37,7 +37,7 @@
 namespace scheduler {
 
 using namespace std::chrono_literals;
-
+struct LayerProps;
 // Maximum period between presents for a layer to be considered active.
 constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
 
@@ -132,19 +132,11 @@
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
 
-    struct LayerProps {
-        bool visible = false;
-        FloatRect bounds;
-        ui::Transform transform;
-        FrameRate setFrameRateVote;
-        int32_t frameRateSelectionPriority = -1;
-    };
-
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                            bool pendingModeChange, LayerProps props);
+                            bool pendingModeChange, const LayerProps& props);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -168,13 +160,11 @@
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
-    FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
-    bool isVisible() const { return mLayerProps.visible; }
-    int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
-
-    FloatRect getBounds() const { return mLayerProps.bounds; }
-
-    ui::Transform getTransform() const { return mLayerProps.transform; }
+    FrameRate getSetFrameRateVote() const;
+    bool isVisible() const;
+    int32_t getFrameRateSelectionPriority() const;
+    FloatRect getBounds() const;
+    ui::Transform getTransform() const;
 
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
@@ -294,7 +284,7 @@
     static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
     static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
 
-    LayerProps mLayerProps;
+    std::unique_ptr<LayerProps> mLayerProps;
 
     RefreshRateHistory mRefreshRateHistory;
 
@@ -304,5 +294,13 @@
     static bool sTraceEnabled;
 };
 
+struct LayerProps {
+    bool visible = false;
+    FloatRect bounds;
+    ui::Transform transform;
+    LayerInfo::FrameRate setFrameRateVote;
+    int32_t frameRateSelectionPriority = -1;
+};
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 064f853..f18dfdc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -81,7 +81,7 @@
     mTouchTimer.reset();
 
     // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
-    demoteLeaderDisplay();
+    demotePacesetterDisplay();
 }
 
 void Scheduler::startTimers() {
@@ -106,11 +106,11 @@
     }
 }
 
-void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
-    demoteLeaderDisplay();
+void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    demotePacesetterDisplay();
 
     std::scoped_lock lock(mDisplayLock);
-    promoteLeaderDisplay(leaderIdOpt);
+    promotePacesetterDisplay(pacesetterIdOpt);
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
@@ -121,17 +121,17 @@
 void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
                                         RefreshRateSelectorPtr selectorPtr,
                                         std::shared_ptr<VsyncSchedule> vsyncSchedule) {
-    demoteLeaderDisplay();
+    demotePacesetterDisplay();
 
     std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
     mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
 
-    promoteLeaderDisplay();
+    promotePacesetterDisplay();
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
-    demoteLeaderDisplay();
+    demotePacesetterDisplay();
 
     std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.erase(displayId);
@@ -142,7 +142,7 @@
     // headless virtual display.)
     LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
 
-    promoteLeaderDisplay();
+    promotePacesetterDisplay();
 }
 
 void Scheduler::run() {
@@ -165,7 +165,7 @@
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
     const bool supportsFrameRateOverrideByContent =
-            leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+            pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
@@ -192,7 +192,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
+        const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps;
         const nsecs_t currentPeriod =
                 getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs();
 
@@ -285,7 +285,7 @@
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
     const bool supportsFrameRateOverrideByContent =
-            leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+            pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -326,7 +326,7 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
+    if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
         return;
     }
 
@@ -487,10 +487,10 @@
     mLayerHistory.deregisterLayer(layer);
 }
 
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    if (leaderSelectorPtr()->canSwitch()) {
-        mLayerHistory.record(layer, presentTime, systemTime(), updateType);
+    if (pacesetterSelectorPtr()->canSwitch()) {
+        mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
     }
 }
 
@@ -504,7 +504,7 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    const auto selectorPtr = leaderSelectorPtr();
+    const auto selectorPtr = pacesetterSelectorPtr();
     if (!selectorPtr->canSwitch()) return;
 
     ATRACE_CALL();
@@ -514,22 +514,22 @@
 }
 
 void Scheduler::resetIdleTimer() {
-    leaderSelectorPtr()->resetIdleTimer();
+    pacesetterSelectorPtr()->resetIdleTimer();
 }
 
 void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
-        leaderSelectorPtr()->resetKernelIdleTimer();
+        pacesetterSelectorPtr()->resetKernelIdleTimer();
     }
 }
 
 void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
-    const bool isLeader = [this, id]() REQUIRES(kMainThreadContext) {
+    const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) {
         ftl::FakeGuard guard(mDisplayLock);
-        return id == mLeaderDisplayId;
+        return id == mPacesetterDisplayId;
     }();
-    if (isLeader) {
+    if (isPacesetter) {
         // TODO (b/255657128): This needs to be handled per display.
         std::lock_guard<std::mutex> lock(mPolicyLock);
         mPolicy.displayPowerMode = powerMode;
@@ -539,7 +539,7 @@
         auto vsyncSchedule = getVsyncScheduleLocked(id);
         vsyncSchedule->getController().setDisplayPowerMode(powerMode);
     }
-    if (!isLeader) return;
+    if (!isPacesetter) return;
 
     if (mDisplayPowerTimer) {
         mDisplayPowerTimer->reset();
@@ -560,8 +560,8 @@
         std::optional<PhysicalDisplayId> idOpt) const {
     ftl::FakeGuard guard(kMainThreadContext);
     if (!idOpt) {
-        LOG_ALWAYS_FATAL_IF(!mLeaderDisplayId, "Missing a leader!");
-        idOpt = mLeaderDisplayId;
+        LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
+        idOpt = mPacesetterDisplayId;
     }
     auto scheduleOpt = mVsyncSchedules.get(*idOpt);
     LOG_ALWAYS_FATAL_IF(!scheduleOpt);
@@ -573,7 +573,7 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
+    const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -637,7 +637,7 @@
         {
             std::scoped_lock lock(mDisplayLock);
             ftl::FakeGuard guard(kMainThreadContext);
-            dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+            dumper.dump("pacesetterDisplayId"sv, mPacesetterDisplayId);
         }
         dumper.dump("layerHistory"sv, mLayerHistory.dump());
         dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
@@ -651,13 +651,13 @@
 void Scheduler::dumpVsync(std::string& out) const {
     std::scoped_lock lock(mDisplayLock);
     ftl::FakeGuard guard(kMainThreadContext);
-    if (mLeaderDisplayId) {
-        base::StringAppendF(&out, "VsyncSchedule for leader %s:\n",
-                            to_string(*mLeaderDisplayId).c_str());
+    if (mPacesetterDisplayId) {
+        base::StringAppendF(&out, "VsyncSchedule for pacesetter %s:\n",
+                            to_string(*mPacesetterDisplayId).c_str());
         getVsyncScheduleLocked()->dump(out);
     }
     for (auto& [id, vsyncSchedule] : mVsyncSchedules) {
-        if (id == mLeaderDisplayId) {
+        if (id == mPacesetterDisplayId) {
             continue;
         }
         base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
@@ -669,31 +669,31 @@
     if (consideredSignals.idle) return false;
 
     const auto frameRateOverrides =
-            leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
-                                                       displayRefreshRate, consideredSignals);
+            pacesetterSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                           displayRefreshRate, consideredSignals);
 
     // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
     // the FrameRateOverrideMappings rather than here.
     return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
 }
 
-void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
-    // TODO(b/241286431): Choose the leader display.
-    mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
-    ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    // TODO(b/241286431): Choose the pacesetter display.
+    mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+    ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
 
-    auto vsyncSchedule = getVsyncScheduleLocked(*mLeaderDisplayId);
-    if (const auto leaderPtr = leaderSelectorPtrLocked()) {
-        leaderPtr->setIdleTimerCallbacks(
+    auto vsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId);
+    if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) {
+        pacesetterPtr->setIdleTimerCallbacks(
                 {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
                               .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
                  .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
                             .onExpired =
                                     [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
 
-        leaderPtr->startIdleTimer();
+        pacesetterPtr->startIdleTimer();
 
-        const Fps refreshRate = leaderPtr->getActiveMode().modePtr->getFps();
+        const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps();
         vsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
                                              true /* force */);
     }
@@ -707,14 +707,14 @@
     }
 }
 
-void Scheduler::demoteLeaderDisplay() {
+void Scheduler::demotePacesetterDisplay() {
     // No need to lock for reads on kMainThreadContext.
-    if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
-        leaderPtr->stopIdleTimer();
-        leaderPtr->clearIdleTimerCallbacks();
+    if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+        pacesetterPtr->stopIdleTimer();
+        pacesetterPtr->clearIdleTimerCallbacks();
     }
 
-    // Clear state that depends on the leader's RefreshRateSelector.
+    // Clear state that depends on the pacesetter's RefreshRateSelector.
     std::scoped_lock lock(mPolicyLock);
     mPolicy = {};
 }
@@ -743,10 +743,11 @@
 
             modeChoices = chooseDisplayModes();
 
-            // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
-            // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+            // TODO(b/240743786): The pacesetter display's mode must change for any
+            // DisplayModeRequest to go through. Fix this by tracking per-display Scheduler::Policy
+            // and timers.
             std::tie(modeOpt, consideredSignals) =
-                    modeChoices.get(*mLeaderDisplayId)
+                    modeChoices.get(*mPacesetterDisplayId)
                             .transform([](const DisplayModeChoice& choice) {
                                 return std::make_pair(choice.mode, choice.consideredSignals);
                             })
@@ -879,7 +880,7 @@
 FrameRateMode Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
     const auto frameRateMode =
-            leaderSelectorPtr()
+            pacesetterSelectorPtr()
                     ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
                     .ranking.front()
                     .frameRateMode;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7374054..62a5fb2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,8 +102,8 @@
 
     void startTimers();
 
-    // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
-    void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+    // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+    void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
             EXCLUDES(mDisplayLock);
 
     using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -167,9 +167,9 @@
 
     const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
 
-    // In some cases, we should only modulate for the leader display. In those
+    // In some cases, we should only modulate for the pacesetter display. In those
     // cases, the caller should pass in the relevant display, and the method
-    // will no-op if it's not the leader. Other cases are not specific to a
+    // will no-op if it's not the pacesetter. Other cases are not specific to a
     // display.
     template <typename... Args,
               typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
@@ -177,13 +177,13 @@
         if (id) {
             std::scoped_lock lock(mDisplayLock);
             ftl::FakeGuard guard(kMainThreadContext);
-            if (id != mLeaderDisplayId) {
+            if (id != mPacesetterDisplayId) {
                 return;
             }
         }
 
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            setVsyncConfig(*config, getLeaderVsyncPeriod());
+            setVsyncConfig(*config, getPacesetterVsyncPeriod());
         }
     }
 
@@ -219,8 +219,8 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
-            EXCLUDES(mDisplayLock);
+    void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+                            LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
@@ -254,7 +254,7 @@
     void dump(ConnectionHandle, std::string&) const;
     void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
 
-    // Returns the preferred refresh rate and frame rate for the leader display.
+    // Returns the preferred refresh rate and frame rate for the pacesetter display.
     FrameRateMode getPreferredDisplayMode();
 
     // Notifies the scheduler about a refresh rate timeline change.
@@ -277,12 +277,12 @@
     // Retrieves the overridden refresh rate for a given uid.
     std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
-    Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
-        return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
+    Period getPacesetterVsyncPeriod() const EXCLUDES(mDisplayLock) {
+        return pacesetterSelectorPtr()->getActiveMode().fps.getPeriod();
     }
 
-    Fps getLeaderRefreshRate() const EXCLUDES(mDisplayLock) {
-        return leaderSelectorPtr()->getActiveMode().fps;
+    Fps getPacesetterRefreshRate() const EXCLUDES(mDisplayLock) {
+        return pacesetterSelectorPtr()->getActiveMode().fps;
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -318,14 +318,14 @@
     void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
     void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
 
-    // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
-    // `mLeaderDisplayId` is never `std::nullopt`.
-    void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+    // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
+    // The new `mPacesetterDisplayId` is never `std::nullopt`.
+    void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
             REQUIRES(kMainThreadContext, mDisplayLock);
 
-    // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
-    // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
-    void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+    // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
+    // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+    void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
 
     void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr,
                                  std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext)
@@ -415,23 +415,23 @@
     display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules
             GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
 
-    ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+    ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
             GUARDED_BY(kMainThreadContext);
 
-    RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+    RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
         std::scoped_lock lock(mDisplayLock);
-        return leaderSelectorPtrLocked();
+        return pacesetterSelectorPtrLocked();
     }
 
-    RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+    RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
         ftl::FakeGuard guard(kMainThreadContext);
-        const RefreshRateSelectorPtr noLeader;
-        return mLeaderDisplayId
-                .and_then([this](PhysicalDisplayId leaderId)
+        const RefreshRateSelectorPtr noPacesetter;
+        return mPacesetterDisplayId
+                .and_then([this](PhysicalDisplayId pacesetterId)
                                   REQUIRES(mDisplayLock, kMainThreadContext) {
-                                      return mRefreshRateSelectors.get(leaderId);
+                                      return mRefreshRateSelectors.get(pacesetterId);
                                   })
-                .value_or(std::cref(noLeader));
+                .value_or(std::cref(noPacesetter));
     }
 
     std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked(
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 62e37db..84671ae 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -176,10 +176,16 @@
 
 void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) {
     std::lock_guard<std::mutex> lock(mHwVsyncLock);
-    if (mHwVsyncState == HwVsyncState::Enabled) {
-        callback.setVsyncEnabled(mId, false);
+    switch (mHwVsyncState) {
+        case HwVsyncState::Enabled:
+            callback.setVsyncEnabled(mId, false);
+            [[fallthrough]];
+        case HwVsyncState::Disabled:
+            mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
+            break;
+        case HwVsyncState::Disallowed:
+            break;
     }
-    mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
 }
 
 bool VsyncSchedule::isHardwareVsyncAllowed(bool makeAllowed) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e45b438..50d4dce 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -456,6 +456,7 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
+    // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner
     mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
     mRefreshRateOverlayRenderRate =
             property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
@@ -473,9 +474,9 @@
             {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
              .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
     mLayerLifecycleManagerEnabled =
-            base::GetBoolProperty("debug.sf.enable_layer_lifecycle_manager"s, false);
+            base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false);
     mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
-            base::GetBoolProperty("debug.sf.enable_legacy_frontend"s, true);
+            base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -2098,8 +2099,24 @@
     mScheduler->forceNextResync();
 }
 
-void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {
-    // TODO(b/202734676) update refresh rate value on the RefreshRateOverlay
+void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
+    ATRACE_CALL();
+    if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
+        const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
+        ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
+        static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+            {
+                {
+                    const auto display = getDisplayDeviceLocked(*displayId);
+                    FTL_FAKE_GUARD(kMainThreadContext,
+                                   display->updateRefreshRateOverlayRate(fps,
+                                                                         display->getActiveMode()
+                                                                                 .fps,
+                                                                         /* setByHwc */ true));
+                }
+            }
+        }));
+    }
 }
 
 void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) {
@@ -2178,6 +2195,38 @@
     return mustComposite;
 }
 
+void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
+    using Changes = frontend::RequestedLayerState::Changes;
+    if (snapshot.path.isClone() ||
+        !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
+        return;
+    }
+
+    const auto layerProps = scheduler::LayerProps{
+            .visible = snapshot.isVisible,
+            .bounds = snapshot.geomLayerBounds,
+            .transform = snapshot.geomLayerTransform,
+            .setFrameRateVote = snapshot.frameRate,
+            .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
+    };
+
+    auto it = mLegacyLayers.find(snapshot.sequence);
+    LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+                        snapshot.getDebugString().c_str());
+
+    if (snapshot.changes.test(Changes::Animation)) {
+        it->second->recordLayerHistoryAnimationTx(layerProps);
+    }
+
+    if (snapshot.changes.test(Changes::FrameRate)) {
+        it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
+    }
+
+    if (snapshot.changes.test(Changes::Buffer)) {
+        it->second->recordLayerHistoryBufferUpdate(layerProps);
+    }
+}
+
 bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update,
                                           bool transactionsFlushed, bool& outTransactionsAreEmpty) {
     using Changes = frontend::RequestedLayerState::Changes;
@@ -2219,7 +2268,7 @@
     }
 
     if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
-                                                      Changes::Hierarchy)) {
+                                                      Changes::Hierarchy | Changes::Visibility)) {
         mUpdateInputInfo = true;
     }
     if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
@@ -2257,6 +2306,7 @@
         }
 
         for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+            updateLayerHistory(*snapshot);
             if (!snapshot->hasReadyFrame) continue;
             newDataLatched = true;
             if (!snapshot->isVisible) break;
@@ -2275,12 +2325,13 @@
             mLayerLifecycleManager.commitChanges();
         }
 
+        commitTransactions();
+
         // enter boot animation on first buffer latch
         if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
             ALOGI("Enter boot animation");
             mBootStage = BootStage::BOOTANIMATION;
         }
-        commitTransactions();
     }
     mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
     return mustComposite;
@@ -3365,7 +3416,7 @@
     }
 
     if (display->isVirtual()) {
-        display->adjustRefreshRate(mScheduler->getLeaderRefreshRate());
+        display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
     }
 
     mDisplays.try_emplace(displayToken, std::move(display));
@@ -3953,8 +4004,14 @@
             mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
         } else {
             layer->useEmptyDamage();
-            // If the layer has frames we will update the latch time when latching the buffer.
-            layer->updateLastLatchTime(latchTime);
+            if (!layer->hasBuffer()) {
+                // The last latch time is used to classify a missed frame as buffer stuffing
+                // instead of a missed frame. This is used to identify scenarios where we
+                // could not latch a buffer or apply a transaction due to backpressure.
+                // We only update the latch time for buffer less layers here, the latch time
+                // is updated for buffer layers when the buffer is latched.
+                layer->updateLastLatchTime(latchTime);
+            }
         }
     });
     mForceTransactionDisplayChange = false;
@@ -4440,10 +4497,14 @@
         }
         if ((flags & eAnimation) && resolvedState.state.surface) {
             if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
-                using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-                mScheduler->recordLayerHistory(layer.get(),
-                                               isAutoTimestamp ? 0 : desiredPresentTime,
-                                               LayerUpdateType::AnimationTX);
+                const auto layerProps = scheduler::LayerProps{
+                        .visible = layer->isVisible(),
+                        .bounds = layer->getBounds(),
+                        .transform = layer->getTransform(),
+                        .setFrameRateVote = layer->getFrameRateForLayerTree(),
+                        .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+                };
+                layer->recordLayerHistoryAnimationTx(layerProps);
             }
         }
     }
@@ -4904,6 +4965,10 @@
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
+    if ((what & layer_state_t::eBufferChanged) == 0) {
+        layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+    }
+
     if (what & layer_state_t::eTrustedPresentationInfoChanged) {
         if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
                                               s.trustedPresentationListener)) {
@@ -5012,6 +5077,10 @@
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
+    if ((what & layer_state_t::eBufferChanged) == 0) {
+        layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+    }
+
     if (what & layer_state_t::eTrustedPresentationInfoChanged) {
         if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
                                               s.trustedPresentationListener)) {
@@ -5025,16 +5094,6 @@
     if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
         flags |= eTraversalNeeded;
 
-    for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
-        if (snapshot->path.isClone() ||
-            !snapshot->changes.test(frontend::RequestedLayerState::Changes::FrameRate))
-            continue;
-        auto it = mLegacyLayers.find(snapshot->sequence);
-        LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
-                            snapshot->getDebugString().c_str());
-        it->second->setFrameRateForLayerTree(snapshot->frameRate);
-    }
-
     return flags;
 }
 
@@ -5233,8 +5292,18 @@
     const sp<IBinder> token = display->getDisplayToken().promote();
     LOG_ALWAYS_FATAL_IF(token == nullptr);
 
+    TransactionState state;
+    state.inputWindowCommands = mInputWindowCommands;
+    nsecs_t now = systemTime();
+    state.desiredPresentTime = now;
+    state.postTime = now;
+    state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER;
+    state.originPid = mPid;
+    state.originUid = static_cast<int>(getuid());
+    uint64_t transactionId = (((uint64_t)mPid) << 32) | mUniqueTransactionId++;
+    state.id = transactionId;
+
     // reset screen orientation and use primary layer stack
-    std::vector<ResolvedComposerState> state;
     Vector<DisplayState> displays;
     DisplayState d;
     d.what = DisplayState::eDisplayProjectionChanged |
@@ -5246,15 +5315,17 @@
     d.layerStackSpaceRect.makeInvalid();
     d.width = 0;
     d.height = 0;
-    displays.add(d);
+    state.displays.add(d);
 
-    nsecs_t now = systemTime();
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back(state);
 
-    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
     // It should be on the main thread, apply it directly.
-    applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
-                          /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
-                          {}, mPid, getuid(), transactionId);
+    if (mLegacyFrontEndEnabled) {
+        applyTransactionsLocked(transactions, /*vsyncId=*/{0});
+    } else {
+        applyAndCommitDisplayTransactionStates(transactions);
+    }
 
     setPowerModeInternal(display, hal::PowerMode::ON);
 }
@@ -5693,14 +5764,27 @@
         }
     }
 
-    LayersProto layersProto;
-    for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
-            continue;
+    if (mLegacyFrontEndEnabled) {
+        LayersProto layersProto;
+        for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+            if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
+                continue;
+            }
+            layer->writeToProto(layersProto, traceFlags);
         }
-        layer->writeToProto(layersProto, traceFlags);
+        return layersProto;
     }
 
+    const frontend::LayerHierarchy& root = mLayerHierarchyBuilder.getHierarchy();
+    LayersProto layersProto;
+    for (auto& [child, variant] : root.mChildren) {
+        if (variant != frontend::LayerHierarchy::Variant::Attached ||
+            stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+            continue;
+        }
+        LayerProtoHelper::writeHierarchyToProto(layersProto, *child, mLayerSnapshotBuilder,
+                                                mLegacyLayers, traceFlags);
+    }
     return layersProto;
 }
 
@@ -6782,7 +6866,8 @@
 
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
-        getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, args.uid);
+        getLayerSnapshots =
+                getLayerSnapshotsForScreenshots(layerStack, args.uid, /*snapshotFilterFn=*/nullptr);
     } else {
         auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
             traverseLayersInLayerStack(layerStack, args.uid, visitor);
@@ -6824,7 +6909,8 @@
 
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
-        getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID);
+        getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+                                                            /*snapshotFilterFn=*/nullptr);
     } else {
         auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
             traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
@@ -7093,6 +7179,8 @@
         captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
         layerFE->mSnapshot->geomLayerTransform =
                 renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+        layerFE->mSnapshot->geomInverseLayerTransform =
+                layerFE->mSnapshot->geomLayerTransform.inverse();
     }
 
     // We allow the system server to take screenshots of secure layers for
@@ -7501,10 +7589,20 @@
 }
 
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
+    bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
     for (const auto& [id, display] : mPhysicalDisplays) {
         if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+            if (setByHwc) {
+                const auto status =
+                        getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+                if (status != NO_ERROR) {
+                    ALOGE("Error updating the refresh rate changed callback debug enabled");
+                    return;
+                }
+            }
+
             if (const auto device = getDisplayDeviceLocked(id)) {
-                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+                device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
                                                  mRefreshRateOverlayRenderRate,
                                                  mRefreshRateOverlayShowInMiddle);
             }
@@ -7632,15 +7730,15 @@
 
     updateActiveDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
-    mScheduler->setLeaderDisplay(mActiveDisplayId);
+    mScheduler->setPacesetterDisplay(mActiveDisplayId);
 
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay.getTransformHint();
 
-    // The policy of the new active/leader display may have changed while it was inactive. In that
-    // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
-    // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
-    // and the kernel idle timer of the newly active display must be toggled.
+    // The policy of the new active/pacesetter display may have changed while it was inactive. In
+    // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
+    // either case, the Scheduler's cachedModeChangedParams must be initialized to the newly active
+    // mode, and the kernel idle timer of the newly active display must be toggled.
     constexpr bool kForce = true;
     applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce);
 }
@@ -7853,12 +7951,18 @@
 }
 
 std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
-SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
-                                                uint32_t uid) {
+SurfaceFlinger::getLayerSnapshotsForScreenshots(
+        std::optional<ui::LayerStack> layerStack, uint32_t uid,
+        std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+                snapshotFilterFn) {
     return [&, layerStack, uid]() {
         std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+        bool stopTraversal = false;
         mLayerSnapshotBuilder.forEachVisibleSnapshot(
                 [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+                    if (stopTraversal) {
+                        return;
+                    }
                     if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
                         return;
                     }
@@ -7868,6 +7972,9 @@
                     if (!snapshot->hasSomethingToDraw()) {
                         return;
                     }
+                    if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) {
+                        return;
+                    }
 
                     auto it = mLegacyLayers.find(snapshot->sequence);
                     LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
@@ -7906,7 +8013,8 @@
                      .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
         mLayerSnapshotBuilder.update(args);
 
-        auto getLayerSnapshotsFn = getLayerSnapshotsForScreenshots({}, uid);
+        auto getLayerSnapshotsFn =
+                getLayerSnapshotsForScreenshots({}, uid, /*snapshotFilterFn=*/nullptr);
         std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
         args.root = mLayerHierarchyBuilder.getHierarchy();
         args.parentCrop.reset();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 094b2cc..338531f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -726,6 +726,7 @@
             REQUIRES(kMainThreadContext);
     bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed,
                               bool& out) REQUIRES(kMainThreadContext);
+    void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
     LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext);
 
     void updateInputFlinger();
@@ -1396,7 +1397,9 @@
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
     }
     std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
-            std::optional<ui::LayerStack> layerStack, uint32_t uid);
+            std::optional<ui::LayerStack> layerStack, uint32_t uid,
+            std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+                    snapshotFilterFn);
     std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
             uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
             bool childrenOnly, const FloatRect& parentCrop);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index cfb2032..f27f53b 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -247,7 +247,7 @@
 
     auto &mutableLayerHistory() { return mLayerHistory; }
 
-    auto refreshRateSelector() { return leaderSelectorPtr(); }
+    auto refreshRateSelector() { return pacesetterSelectorPtr(); }
 
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f6b2c8e..f17d2e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -220,9 +220,9 @@
     sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
 
     for (int i = 0; i < historySize; ++i) {
-        historyV1.record(layer1.get(), time1, time1,
+        historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
                          scheduler::LayerHistory::LayerUpdateType::Buffer);
-        historyV1.record(layer2.get(), time2, time2,
+        historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
                          scheduler::LayerHistory::LayerUpdateType::Buffer);
         time1 += mFdp.PickValueInArray(kVsyncPeriods);
         time2 += mFdp.PickValueInArray(kVsyncPeriods);
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index e9b1fbb..d0ab105 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -187,8 +187,6 @@
 
 // Expect no filtering because the output source crop and output buffer are the same size.
 TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
-    // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
     gui::DisplayCaptureArgs captureArgs;
     captureArgs.displayToken = mDisplay;
     captureArgs.width = 50;
@@ -224,4 +222,17 @@
     mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
 }
 
+// Expect no filtering because parent's position transform shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
+    Transaction().setPosition(mParent, 100, 100).apply();
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mParent->getHandle();
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index eac9edc..012a4ad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -103,9 +103,11 @@
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
         "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
+        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+        "SurfaceFlinger_HdrOutputControlTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
-        "SurfaceFlinger_MultiDisplayLeaderTest.cpp",
+        "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
         "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
         "SurfaceFlinger_PowerHintTest.cpp",
@@ -113,7 +115,6 @@
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
         "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
-        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateSelectorTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8397f8d..b767276 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -112,7 +112,8 @@
                                Fps desiredRefreshRate, int numFrames) {
         LayerHistory::Summary summary;
         for (int i = 0; i < numFrames; i++) {
-            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
             time += frameRate.getPeriodNsecs();
 
             summary = summarizeLayerHistory(time);
@@ -155,7 +156,8 @@
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
 
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -177,7 +179,8 @@
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
 
     auto summary = summarizeLayerHistory(time);
@@ -205,7 +208,8 @@
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -214,7 +218,8 @@
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -232,7 +237,8 @@
 
     nsecs_t time = systemTime();
 
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     auto summary = summarizeLayerHistory(time);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     // Layer is still considered inactive so we expect to get Min
@@ -240,7 +246,8 @@
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     summary = summarizeLayerHistory(time);
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -257,7 +264,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -280,7 +288,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -307,7 +316,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -335,7 +345,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -363,7 +374,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -395,7 +407,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -441,7 +454,8 @@
 
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
         summary = summarizeLayerHistory(time);
     }
@@ -453,13 +467,15 @@
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
 
     // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
@@ -472,7 +488,8 @@
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
     for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -488,10 +505,12 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -504,7 +523,8 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
-    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     summary = summarizeLayerHistory(time);
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
@@ -530,7 +550,8 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -551,7 +572,8 @@
 
     // layer3 becomes active and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -582,7 +604,8 @@
 
     // the very first updates makes the layer frequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
         EXPECT_EQ(1, layerCount());
@@ -593,7 +616,8 @@
     }
 
     // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     EXPECT_EQ(1, layerCount());
@@ -607,7 +631,8 @@
 
     // Now even if we post a quick few frame we should stay infrequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
 
         EXPECT_EQ(1, layerCount());
@@ -618,7 +643,8 @@
     }
 
     // More quick frames will get us to frequent again
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += HI_FPS_PERIOD;
 
     EXPECT_EQ(1, layerCount());
@@ -645,9 +671,10 @@
     nsecs_t time = systemTime();
 
     // Post a buffer to the layers to make them active
-    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    history().record(explicitInvisiblelayer.get(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
+                     time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
+                     time, time, LayerHistory::LayerUpdateType::Buffer);
 
     EXPECT_EQ(2, layerCount());
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -673,7 +700,8 @@
 
     // layer is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -684,7 +712,8 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // another update with the same cadence keep in infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -694,7 +723,8 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // an update as animation will immediately vote for Max
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::AnimationTX);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -719,7 +749,8 @@
 
     // Fill up the window with frequent updates
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += (60_Hz).getPeriodNsecs();
 
         EXPECT_EQ(1, layerCount());
@@ -731,7 +762,8 @@
 
     // posting a buffer after long inactivity should retain the layer as active
     time += std::chrono::nanoseconds(3s).count();
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
@@ -741,9 +773,11 @@
 
     // posting more infrequent buffer should make the layer infrequent
     time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
@@ -751,7 +785,8 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // posting another buffer should keep the layer infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
@@ -759,8 +794,10 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // posting more buffers would mean starting of an animation, so making the layer frequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
@@ -769,7 +806,8 @@
 
     // posting a buffer after long inactivity should retain the layer as active
     time += std::chrono::nanoseconds(3s).count();
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
@@ -778,7 +816,8 @@
 
     // posting another buffer should keep the layer frequent
     time += (60_Hz).getPeriodNsecs();
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
@@ -801,7 +840,8 @@
 
     // layer is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -869,10 +909,10 @@
     const nsecs_t startTime = systemTime();
 
     const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
-    history().record(heuristicLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
-    history().record(infrequentLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+                     startTime, LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+                     startTime, LayerHistory::LayerUpdateType::Buffer);
 
     nsecs_t time = startTime;
     nsecs_t lastInfrequentUpdate = startTime;
@@ -880,14 +920,15 @@
     int infrequentLayerUpdates = 0;
     while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
         time += heuristicUpdateDelta.count();
-        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
 
         if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
             ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
                   totalInfrequentLayerUpdates);
             lastInfrequentUpdate = time;
-            history().record(infrequentLayer.get(), time, time,
-                             LayerHistory::LayerUpdateType::Buffer);
+            history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
+                             time, LayerHistory::LayerUpdateType::Buffer);
             infrequentLayerUpdates++;
         }
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 26281d2..dc76b4c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -161,7 +161,8 @@
 
     // recordLayerHistory should be a noop
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -185,7 +186,8 @@
                                                                       kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
 }
 
@@ -234,7 +236,8 @@
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
 
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
     FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6adcd52..44ab569 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -380,8 +380,10 @@
     commitTransaction();
 
     auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
-    history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
-    history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
+                   LayerHistory::LayerUpdateType::Buffer);
+    history.record(child->getSequence(), child->getLayerProps(), 0, 0,
+                   LayerHistory::LayerUpdateType::Buffer);
 
     const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
     const auto summary = history.summarize(*selectorPtr, 0);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
new file mode 100644
index 0000000..a2c54ac
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+#include <gui/AidlStatusUtil.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
+using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+using gui::aidl_utils::statusTFromBinderStatus;
+
+TEST(HdrOutputControlTest, testGetHdrOutputConversionSupport) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status status = sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+}
+
+TEST(HdrOutputControlTest, testGetHdrConversionCapabilities) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status getSupportStatus =
+            sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+    std::vector<gui::HdrConversionCapability> capabilities;
+    binder::Status status = sf->getHdrConversionCapabilities(&capabilities);
+
+    if (hdrOutputConversionSupport) {
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    } else {
+        ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+    }
+}
+
+TEST(HdrOutputControlTest, testSetHdrConversionStrategy) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status getSupportStatus =
+            sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+    std::vector<HdrConversionStrategy> strategies =
+            {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::passthrough)>),
+             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
+             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+    int32_t outPreferredHdrOutputType = 0;
+
+    for (HdrConversionStrategy strategy : strategies) {
+        binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+
+        if (hdrOutputConversionSupport) {
+            ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+        } else {
+            ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
similarity index 63%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
index 9c58943..e38f56e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
@@ -25,12 +25,12 @@
 namespace android {
 namespace {
 
-struct MultiDisplayLeaderTest : DisplayTransactionTest {
+struct MultiDisplayPacesetterTest : DisplayTransactionTest {
     static constexpr bool kWithMockScheduler = false;
-    MultiDisplayLeaderTest() : DisplayTransactionTest(kWithMockScheduler) {}
+    MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {}
 };
 
-TEST_F(MultiDisplayLeaderTest, foldable) {
+TEST_F(MultiDisplayPacesetterTest, foldable) {
     injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
 
     // Inject inner and outer displays with uninitialized power modes.
@@ -50,31 +50,31 @@
         outerDisplay = injector.inject();
     }
 
-    // When the device boots, the inner display should be the leader.
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+    // When the device boots, the inner display should be the pacesetter.
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
 
     // ...and should still be after powering on.
     mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
 
-    // The outer display should become the leader after folding.
+    // The outer display should become the pacesetter after folding.
     mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
     mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
 
-    // The inner display should become the leader after unfolding.
+    // The inner display should become the pacesetter after unfolding.
     mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
     mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
 
-    // The inner display should stay the leader if both are powered on.
-    // TODO(b/256196556): The leader should depend on the displays' VSYNC phases.
+    // The inner display should stay the pacesetter if both are powered on.
+    // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
     mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
 
-    // The outer display should become the leader if designated.
-    mFlinger.scheduler()->setLeaderDisplay(outerDisplay->getPhysicalId());
-    ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+    // The outer display should become the pacesetter if designated.
+    mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ac04720..d4b4434 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -63,7 +63,7 @@
         return Scheduler::createConnection(std::move(eventThread));
     }
 
-    auto refreshRateSelector() { return leaderSelectorPtr(); }
+    auto refreshRateSelector() { return pacesetterSelectorPtr(); }
 
     const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
         return mRefreshRateSelectors;
@@ -92,13 +92,13 @@
         Scheduler::unregisterDisplay(displayId);
     }
 
-    std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
-        return mLeaderDisplayId;
+    std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+        return mPacesetterDisplayId;
     }
 
-    void setLeaderDisplay(PhysicalDisplayId displayId) {
+    void setPacesetterDisplay(PhysicalDisplayId displayId) {
         ftl::FakeGuard guard(kMainThreadContext);
-        Scheduler::setLeaderDisplay(displayId);
+        Scheduler::setPacesetterDisplay(displayId);
     }
 
     auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
index adf0804..4010fa6 100644
--- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -86,6 +86,12 @@
     mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
 }
 
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) {
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
 TEST_F(VsyncScheduleTest, MakeAllowed) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
 }
@@ -97,6 +103,13 @@
     mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
 }
 
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
 TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
     ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
     EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
@@ -129,6 +142,16 @@
     mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
 }
 
+TEST_F(VsyncScheduleTest, EnableDisable2) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
 TEST_F(VsyncScheduleTest, StartPeriodTransition) {
     // Note: startPeriodTransition is only called when hardware vsyncs are
     // allowed.
@@ -225,5 +248,23 @@
     ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
 }
 
+TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) {
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f28b8d8..5dc3490 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -175,6 +175,7 @@
                  Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
     MOCK_METHOD1(onHotplugConnect, void(Display));
     MOCK_METHOD1(onHotplugDisconnect, void(Display));
+    MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
 };
 
 } // namespace Hwc2::mock