Merge "Add additional flag for FMQ SF" into main
diff --git a/Android.bp b/Android.bp
index 72311f0..13954ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -125,3 +125,9 @@
     srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
     path: "aidl",
 }
+
+dirgroup {
+    name: "trusty_dirgroup_frameworks_native",
+    dirs: ["libs/binder"],
+    visibility: ["//trusty/vendor/google/aosp/scripts"],
+}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9c01169..07d16f7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,9 +4,6 @@
       "name": "SurfaceFlinger_test",
       "options": [
         {
-          "include-filter": "*"
-        },
-        {
           // TODO(b/305717998): Deflake and re-enable
           "exclude-filter": "*ChildLayerTest*"
         }
@@ -23,12 +20,7 @@
   ],
   "hwasan-postsubmit": [
     {
-      "name": "SurfaceFlinger_test",
-      "options": [
-        {
-          "include-filter": "*"
-        }
-      ]
+      "name": "SurfaceFlinger_test"
     }
   ]
 }
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 6f7fea3..ce7c55c 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -108,7 +108,7 @@
 
     const uint32_t cap_syslog_mask = CAP_TO_MASK(CAP_SYSLOG);
     const uint32_t cap_syslog_index = CAP_TO_INDEX(CAP_SYSLOG);
-    bool has_cap_syslog = (capdata[cap_syslog_index].effective & cap_syslog_mask) != 0;
+    bool has_cap_syslog = (capdata[cap_syslog_index].permitted & cap_syslog_mask) != 0;
 
     memset(&capdata, 0, sizeof(capdata));
     if (has_cap_syslog) {
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1a48239..6a248ef 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -266,6 +266,7 @@
 public:
     InputDeviceInfo();
     InputDeviceInfo(const InputDeviceInfo& other);
+    InputDeviceInfo& operator=(const InputDeviceInfo& other);
     ~InputDeviceInfo();
 
     struct MotionRange {
@@ -315,13 +316,11 @@
 
     inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; }
 
-    inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
-        mKeyCharacterMap = value;
+    inline void setKeyCharacterMap(std::unique_ptr<KeyCharacterMap> value) {
+        mKeyCharacterMap = std::move(value);
     }
 
-    inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
-        return mKeyCharacterMap;
-    }
+    inline const KeyCharacterMap* getKeyCharacterMap() const { return mKeyCharacterMap.get(); }
 
     inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
     inline bool hasVibrator() const { return mHasVibrator; }
@@ -364,7 +363,7 @@
     std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
     uint32_t mSources;
     int32_t mKeyboardType;
-    std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
+    std::unique_ptr<KeyCharacterMap> mKeyCharacterMap;
     std::optional<InputDeviceUsiVersion> mUsiVersion;
     ui::LogicalDisplayId mAssociatedDisplayId{ui::LogicalDisplayId::INVALID};
     bool mEnabled;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 7ea34c2..0a9e74f 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -72,7 +72,7 @@
     };
 
     /* Loads a key character map from a file. */
-    static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+    static base::Result<std::unique_ptr<KeyCharacterMap>> load(const std::string& filename,
                                                                Format format);
 
     /* Loads a key character map from its string contents. */
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 47519c2..6d95ca7 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -16,10 +16,14 @@
 
 #pragma once
 
+#include <array>
 #include <chrono>
+#include <iterator>
 #include <optional>
 #include <vector>
 
+#include <android-base/logging.h>
+#include <ftl/mixins.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 #include <input/RingBuffer.h>
@@ -79,13 +83,127 @@
         PointerCoords coords;
     };
 
+    /**
+     * Container that stores pointers as an associative array, supporting O(1) lookup by pointer id,
+     * as well as forward iteration in the order in which the pointer or pointers were inserted in
+     * the container. PointerMap has a maximum capacity equal to MAX_POINTERS.
+     */
+    class PointerMap {
+    public:
+        struct PointerId : ftl::DefaultConstructible<PointerId, int32_t>,
+                           ftl::Equatable<PointerId> {
+            using DefaultConstructible::DefaultConstructible;
+        };
+
+        /**
+         * Custom iterator to enable use of range-based for loops.
+         */
+        template <typename T>
+        class iterator {
+        public:
+            using iterator_category = std::forward_iterator_tag;
+            using value_type = T;
+            using difference_type = std::ptrdiff_t;
+            using pointer = T*;
+            using reference = T&;
+
+            explicit iterator(pointer element) : mElement{element} {}
+
+            friend bool operator==(const iterator& lhs, const iterator& rhs) {
+                return lhs.mElement == rhs.mElement;
+            }
+
+            friend bool operator!=(const iterator& lhs, const iterator& rhs) {
+                return !(lhs == rhs);
+            }
+
+            iterator operator++() {
+                ++mElement;
+                return *this;
+            }
+
+            reference operator*() const { return *mElement; }
+
+        private:
+            pointer mElement;
+        };
+
+        PointerMap() {
+            idToIndex.fill(std::nullopt);
+            for (Pointer& pointer : pointers) {
+                pointer.properties.clear();
+                pointer.coords.clear();
+            }
+        }
+
+        /**
+         * Forward iterators to traverse the pointers in `pointers`. The order of the pointers is
+         * determined by the order in which they were inserted (not by id).
+         */
+        iterator<Pointer> begin() { return iterator<Pointer>{&pointers[0]}; }
+
+        iterator<const Pointer> begin() const { return iterator<const Pointer>{&pointers[0]}; }
+
+        iterator<Pointer> end() { return iterator<Pointer>{&pointers[nextPointerIndex]}; }
+
+        iterator<const Pointer> end() const {
+            return iterator<const Pointer>{&pointers[nextPointerIndex]};
+        }
+
+        /**
+         * Inserts the given pointer into the PointerMap. Precondition: The current number of
+         * contained pointers must be less than MAX_POINTERS when this function is called. It
+         * fatally logs if the user tries to insert more than MAX_POINTERS, or if pointer id is out
+         * of bounds.
+         */
+        void insert(const Pointer& pointer) {
+            LOG_IF(FATAL, nextPointerIndex >= pointers.size())
+                    << "Cannot insert more than " << MAX_POINTERS << " in PointerMap.";
+            LOG_IF(FATAL, (pointer.properties.id < 0) || (pointer.properties.id > MAX_POINTER_ID))
+                    << "Invalid pointer id.";
+            idToIndex[pointer.properties.id] = std::optional<size_t>{nextPointerIndex};
+            pointers[nextPointerIndex] = pointer;
+            ++nextPointerIndex;
+        }
+
+        /**
+         * Returns the pointer associated with the provided id if it exists.
+         * Otherwise, std::nullopt is returned.
+         */
+        std::optional<Pointer> find(PointerId id) const {
+            const int32_t idValue = ftl::to_underlying(id);
+            LOG_IF(FATAL, (idValue < 0) || (idValue > MAX_POINTER_ID)) << "Invalid pointer id.";
+            const std::optional<size_t> index = idToIndex[idValue];
+            return index.has_value() ? std::optional{pointers[*index]} : std::nullopt;
+        }
+
+    private:
+        /**
+         * The index at which a pointer is inserted in `pointers`. Likewise, it represents the
+         * number of pointers in PointerMap.
+         */
+        size_t nextPointerIndex{0};
+
+        /**
+         * Sequentially stores pointers. Each pointer's position is determined by the value of
+         * nextPointerIndex at insertion time.
+         */
+        std::array<Pointer, MAX_POINTERS + 1> pointers;
+
+        /**
+         * Maps each pointer id to its associated index in pointers. If no pointer with the id
+         * exists in pointers, the mapped value is std::nullopt.
+         */
+        std::array<std::optional<size_t>, MAX_POINTER_ID + 1> idToIndex;
+    };
+
     struct Sample {
         std::chrono::nanoseconds eventTime;
-        std::vector<Pointer> pointers;
+        PointerMap pointerMap;
 
         std::vector<PointerCoords> asPointerCoords() const {
             std::vector<PointerCoords> pointersCoords;
-            for (const Pointer& pointer : pointers) {
+            for (const Pointer& pointer : pointerMap) {
                 pointersCoords.push_back(pointer.coords);
             }
             return pointersCoords;
@@ -100,13 +218,12 @@
     RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
 
     /**
-     * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
-     * does not move between samples.
+     * Latest sample in mLatestSamples after resampling motion event.
      */
     std::optional<Sample> mLastRealSample;
 
     /**
-     * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
+     * Latest prediction. That is, the latest extrapolated sample.
      */
     std::optional<Sample> mPreviousPrediction;
 
@@ -134,12 +251,12 @@
     bool canInterpolate(const InputMessage& futureSample) const;
 
     /**
-     * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+     * Returns a sample interpolated between the latest sample of mLatestSamples and futureMessage,
      * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
      * mLatestSamples must have at least one sample when attemptInterpolation is called.
      */
     std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
-                                               const InputMessage& futureSample) const;
+                                               const InputMessage& futureMessage) const;
 
     /**
      * Checks if there are necessary conditions to extrapolate. That is, there are at least two
@@ -156,7 +273,8 @@
     std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
 
     /**
-     * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
+     * Iterates through motion event samples, and replaces real coordinates with resampled
+     * coordinates to avoid jerkiness in certain conditions.
      */
     void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
 
@@ -174,4 +292,5 @@
 
     inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
 };
+
 } // namespace android
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 52b485a..f7b9f05 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -31,7 +31,8 @@
 #endif
 
 using AidlServiceManager = android::os::IServiceManager;
-using IAccessor = android::os::IAccessor;
+using android::os::IAccessor;
+using binder::Status;
 
 static const char* kStaticCachableList[] = {
         // go/keep-sorted start
@@ -39,10 +40,14 @@
         "account",
         "activity",
         "alarm",
+        "android.frameworks.stats.IStats/default",
         "android.system.keystore2.IKeystoreService/default",
         "appops",
         "audio",
+        "autofill",
+        "batteryproperties",
         "batterystats",
+        "biometic",
         "carrier_config",
         "connectivity",
         "content",
@@ -58,6 +63,7 @@
         "jobscheduler",
         "legacy_permission",
         "location",
+        "lock_settings",
         "media.extractor",
         "media.metrics",
         "media.player",
@@ -78,15 +84,19 @@
         "phone",
         "platform_compat",
         "power",
+        "processinfo",
         "role",
+        "sensitive_content_protection_service",
         "sensorservice",
         "statscompanion",
         "telephony.registry",
         "thermalservice",
         "time_detector",
+        "tracing.proxy",
         "trust",
         "uimode",
         "user",
+        "vibrator",
         "virtualdevice",
         "virtualdevice_native",
         "webviewupdate",
@@ -109,19 +119,38 @@
     return false;
 }
 
-binder::Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
-                                                         const os::Service& service) {
+Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+                                                 const os::Service& service) {
     if (!kUseCache) {
-        return binder::Status::ok();
+        return Status::ok();
     }
+    std::string traceStr;
+    if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) {
+        traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName;
+    }
+    binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, traceStr.c_str());
+
     if (service.getTag() == os::Service::Tag::binder) {
         sp<IBinder> binder = service.get<os::Service::Tag::binder>();
-        if (binder && mCacheForGetService->isClientSideCachingEnabled(serviceName) &&
-            binder->isBinderAlive()) {
+        if (!binder) {
+            binder::ScopedTrace
+                    aidlTrace(ATRACE_TAG_AIDL,
+                              "BinderCacheWithInvalidation::updateCache failed: binder_null");
+        } else if (!binder->isBinderAlive()) {
+            binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+                                          "BinderCacheWithInvalidation::updateCache failed: "
+                                          "isBinderAlive_false");
+        } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
+            binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+                                          "BinderCacheWithInvalidation::updateCache successful");
             return mCacheForGetService->setItem(serviceName, binder);
+        } else {
+            binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+                                          "BinderCacheWithInvalidation::updateCache failed: "
+                                          "caching_not_enabled");
         }
     }
-    return binder::Status::ok();
+    return Status::ok();
 }
 
 bool BackendUnifiedServiceManager::returnIfCached(const std::string& serviceName,
@@ -147,21 +176,20 @@
     return mTheRealServiceManager;
 }
 
-binder::Status BackendUnifiedServiceManager::getService(const ::std::string& name,
-                                                        sp<IBinder>* _aidl_return) {
+Status BackendUnifiedServiceManager::getService(const ::std::string& name,
+                                                sp<IBinder>* _aidl_return) {
     os::Service service;
-    binder::Status status = getService2(name, &service);
+    Status status = getService2(name, &service);
     *_aidl_return = service.get<os::Service::Tag::binder>();
     return status;
 }
 
-binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name,
-                                                         os::Service* _out) {
+Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os::Service* _out) {
     if (returnIfCached(name, _out)) {
-        return binder::Status::ok();
+        return Status::ok();
     }
     os::Service service;
-    binder::Status status = mTheRealServiceManager->getService2(name, &service);
+    Status status = mTheRealServiceManager->getService2(name, &service);
 
     if (status.isOk()) {
         status = toBinderService(name, service, _out);
@@ -172,14 +200,13 @@
     return status;
 }
 
-binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& name,
-                                                          os::Service* _out) {
+Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) {
     os::Service service;
     if (returnIfCached(name, _out)) {
-        return binder::Status::ok();
+        return Status::ok();
     }
 
-    binder::Status status = mTheRealServiceManager->checkService(name, &service);
+    Status status = mTheRealServiceManager->checkService(name, &service);
     if (status.isOk()) {
         status = toBinderService(name, service, _out);
         if (status.isOk()) {
@@ -189,16 +216,15 @@
     return status;
 }
 
-binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
-                                                             const os::Service& in,
-                                                             os::Service* _out) {
+Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
+                                                     const os::Service& in, os::Service* _out) {
     switch (in.getTag()) {
         case os::Service::Tag::binder: {
             if (in.get<os::Service::Tag::binder>() == nullptr) {
                 // failed to find a service. Check to see if we have any local
                 // injected Accessors for this service.
                 os::Service accessor;
-                binder::Status status = getInjectedAccessor(name, &accessor);
+                Status status = getInjectedAccessor(name, &accessor);
                 if (!status.isOk()) {
                     *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
                     return status;
@@ -214,7 +240,7 @@
             }
 
             *_out = in;
-            return binder::Status::ok();
+            return Status::ok();
         }
         case os::Service::Tag::accessor: {
             sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>();
@@ -222,11 +248,11 @@
             if (accessor == nullptr) {
                 ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
                 *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
-                return binder::Status::ok();
+                return Status::ok();
             }
             auto request = [=] {
                 os::ParcelFileDescriptor fd;
-                binder::Status ret = accessor->addConnection(&fd);
+                Status ret = accessor->addConnection(&fd);
                 if (ret.isOk()) {
                     return base::unique_fd(fd.release());
                 } else {
@@ -239,11 +265,11 @@
             if (status != OK) {
                 ALOGE("Failed to set up preconnected binder RPC client: %s",
                       statusToString(status).c_str());
-                return binder::Status::fromStatusT(status);
+                return Status::fromStatusT(status);
             }
             session->setSessionSpecificRoot(accessorBinder);
             *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
-            return binder::Status::ok();
+            return Status::ok();
         }
         default: {
             LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag());
@@ -251,53 +277,52 @@
     }
 }
 
-binder::Status BackendUnifiedServiceManager::addService(const ::std::string& name,
-                                                        const sp<IBinder>& service,
-                                                        bool allowIsolated, int32_t dumpPriority) {
+Status BackendUnifiedServiceManager::addService(const ::std::string& name,
+                                                const sp<IBinder>& service, bool allowIsolated,
+                                                int32_t dumpPriority) {
     return mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
 }
-binder::Status BackendUnifiedServiceManager::listServices(
-        int32_t dumpPriority, ::std::vector<::std::string>* _aidl_return) {
+Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
+                                                  ::std::vector<::std::string>* _aidl_return) {
     return mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::registerForNotifications(
+Status BackendUnifiedServiceManager::registerForNotifications(
         const ::std::string& name, const sp<os::IServiceCallback>& callback) {
     return mTheRealServiceManager->registerForNotifications(name, callback);
 }
-binder::Status BackendUnifiedServiceManager::unregisterForNotifications(
+Status BackendUnifiedServiceManager::unregisterForNotifications(
         const ::std::string& name, const sp<os::IServiceCallback>& callback) {
     return mTheRealServiceManager->unregisterForNotifications(name, callback);
 }
-binder::Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name,
-                                                        bool* _aidl_return) {
+Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) {
     return mTheRealServiceManager->isDeclared(name, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::getDeclaredInstances(
+Status BackendUnifiedServiceManager::getDeclaredInstances(
         const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) {
     return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::updatableViaApex(
+Status BackendUnifiedServiceManager::updatableViaApex(
         const ::std::string& name, ::std::optional<::std::string>* _aidl_return) {
     return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::getUpdatableNames(
-        const ::std::string& apexName, ::std::vector<::std::string>* _aidl_return) {
+Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName,
+                                                       ::std::vector<::std::string>* _aidl_return) {
     return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::getConnectionInfo(
+Status BackendUnifiedServiceManager::getConnectionInfo(
         const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) {
     return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
 }
-binder::Status BackendUnifiedServiceManager::registerClientCallback(
+Status BackendUnifiedServiceManager::registerClientCallback(
         const ::std::string& name, const sp<IBinder>& service,
         const sp<os::IClientCallback>& callback) {
     return mTheRealServiceManager->registerClientCallback(name, service, callback);
 }
-binder::Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
-                                                                  const sp<IBinder>& service) {
+Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
+                                                          const sp<IBinder>& service) {
     return mTheRealServiceManager->tryUnregisterService(name, service);
 }
-binder::Status BackendUnifiedServiceManager::getServiceDebugInfo(
+Status BackendUnifiedServiceManager::getServiceDebugInfo(
         ::std::vector<os::ServiceDebugInfo>* _aidl_return) {
     return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
 }
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 47b2ec9..feb8470 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -18,6 +18,7 @@
 #include <android/os/BnServiceManager.h>
 #include <android/os/IServiceManager.h>
 #include <binder/IPCThreadState.h>
+#include <binder/Trace.h>
 #include <map>
 #include <memory>
 
@@ -59,6 +60,12 @@
     }
 
     bool removeItem(const std::string& key, const sp<IBinder>& who) {
+        std::string traceStr;
+        uint64_t tag = ATRACE_TAG_AIDL;
+        if (atrace_is_tag_enabled(tag)) {
+            traceStr = "BinderCacheWithInvalidation::removeItem " + key;
+        }
+        binder::ScopedTrace aidlTrace(tag, traceStr.c_str());
         std::lock_guard<std::mutex> lock(mCacheMutex);
         if (auto it = mCache.find(key); it != mCache.end()) {
             if (it->second.service == who) {
@@ -81,11 +88,22 @@
         if (item->localBinder() == nullptr) {
             status_t status = item->linkToDeath(deathRecipient);
             if (status != android::OK) {
+                std::string traceStr;
+                uint64_t tag = ATRACE_TAG_AIDL;
+                if (atrace_is_tag_enabled(tag)) {
+                    traceStr =
+                            "BinderCacheWithInvalidation::setItem Failed LinkToDeath for service " +
+                            key + " : " + std::to_string(status);
+                }
+                binder::ScopedTrace aidlTrace(tag, traceStr.c_str());
+
                 ALOGE("Failed to linkToDeath binder for service %s. Error: %d", key.c_str(),
                       status);
                 return binder::Status::fromStatusT(status);
             }
         }
+        binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+                                      "BinderCacheWithInvalidation::setItem Successfully Cached");
         std::lock_guard<std::mutex> lock(mCacheMutex);
         Entry entry = {.service = item, .deathRecipient = deathRecipient};
         mCache[key] = entry;
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index c6b0cb7..bb03e89 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -330,8 +330,8 @@
         if (err != NO_ERROR || // failed transaction
                 size != size64 || offset != offset64) { // ILP32 size check
             ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)",
-                    IInterface::asBinder(this).get(),
-                    parcel_fd, size, err, strerror(-err));
+                  IInterface::asBinder(this).get(), parcel_fd, size, err,
+                  statusToString(err).c_str());
             return;
         }
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 18c4134..96d821e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -668,7 +668,8 @@
                 // FD was unowned in the source parcel.
                 int newFd = -1;
                 if (status_t status = binder::os::dupFileDescriptor(oldFd, &newFd); status != OK) {
-                    ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
+                    ALOGW("Failed to duplicate file descriptor %d: %s", oldFd,
+                          statusToString(status).c_str());
                 }
                 rpcFields->mFds->emplace_back(unique_fd(newFd));
                 // Fixup the index in the data.
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index b8742af..c7851dc 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -503,7 +503,7 @@
 
                 auto status = binder::os::getRandomBytes(sessionId.data(), sessionId.size());
                 if (status != OK) {
-                    ALOGE("Failed to read random session ID: %s", strerror(-status));
+                    ALOGE("Failed to read random session ID: %s", statusToString(status).c_str());
                     return;
                 }
             } while (server->mSessions.end() != server->mSessions.find(sessionId));
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index cd21a91..16023ff 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -164,7 +164,7 @@
         status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov,
                                                                        1, std::nullopt, &fds);
         if (status != OK) {
-            ALOGE("Failed to send fd over bootstrap transport: %s", strerror(-status));
+            ALOGE("Failed to send fd over bootstrap transport: %s", statusToString(status).c_str());
             return status;
         }
 
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 07b8c40..886eb4b 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -255,7 +255,7 @@
                     "new variant was added to the ABinderRpc_ConnectionInfo and this needs to be "
                     "updated.");
         }
-        return OK;
+        return STATUS_OK;
     };
     sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate));
     if (accessorBinder == nullptr) {
@@ -321,7 +321,7 @@
     // This AIBinder needs a strong ref to pass ownership to the caller
     binder->incStrong(nullptr);
     *outDelegator = binder.get();
-    return OK;
+    return STATUS_OK;
 }
 
 ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) {
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 9fe5d78..66667d3 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -287,6 +287,11 @@
  *        this object with one strong ref count and is responsible for removing
  *        that ref count with with AIBinder_decStrong when the caller wishes to
  *        drop the reference.
+ * \return STATUS_OK on success.
+ *         STATUS_UNEXPECTED_NULL if instance or binder arguments are null.
+ *         STATUS_BAD_TYPE if the binder is not an IAccessor.
+ *         STATUS_NAME_NOT_FOUND if the binder is an IAccessor, but not
+ *         associated with the provided instance name.
  */
 binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* _Nonnull instance,
                                                      AIBinder* _Nonnull binder,
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 4545d7b..020ebcc 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -16,7 +16,6 @@
         "libdowncast_rs",
         "liblibc",
         "liblog_rust",
-        "libnix",
     ],
     host_supported: true,
     vendor_available: true,
@@ -200,7 +199,6 @@
         "libdowncast_rs",
         "liblibc",
         "liblog_rust",
-        "libnix",
     ],
 }
 
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 08582ab..9833cbe 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -22,10 +22,9 @@
 use std::ffi::{c_void, CStr, CString};
 use std::os::raw::c_char;
 
-use libc::sockaddr;
-use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
+use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t};
 use std::sync::Arc;
-use std::{fmt, ptr};
+use std::{fmt, mem, ptr};
 
 /// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
 ///
@@ -44,9 +43,9 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum ConnectionInfo {
     /// For vsock connection
-    Vsock(VsockAddr),
+    Vsock(sockaddr_vm),
     /// For unix domain socket connection
-    Unix(UnixAddr),
+    Unix(sockaddr_un),
 }
 
 /// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
@@ -148,13 +147,21 @@
         match connection {
             ConnectionInfo::Vsock(addr) => {
                 // Safety: The sockaddr is being copied in the NDK API
-                unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
+                unsafe {
+                    sys::ABinderRpc_ConnectionInfo_new(
+                        &addr as *const sockaddr_vm as *const sockaddr,
+                        mem::size_of::<sockaddr_vm>() as socklen_t,
+                    )
+                }
             }
             ConnectionInfo::Unix(addr) => {
                 // Safety: The sockaddr is being copied in the NDK API
                 // The cast is from sockaddr_un* to sockaddr*.
                 unsafe {
-                    sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
+                    sys::ABinderRpc_ConnectionInfo_new(
+                        &addr as *const sockaddr_un as *const sockaddr,
+                        mem::size_of::<sockaddr_un>() as socklen_t,
+                    )
                 }
             }
         }
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 25e6a52..49f4cba 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -50,9 +50,28 @@
 using namespace std::chrono_literals;
 
 namespace {
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+// RAII wrapper to defer arbitrary work until the Deferred instance is deleted.
+template <class F>
+class Deferred {
+public:
+    explicit Deferred(F f) : mF{std::move(f)} {}
+
+    ~Deferred() { mF(); }
+
+    Deferred(const Deferred&) = delete;
+    Deferred& operator=(const Deferred&) = delete;
+
+private:
+    F mF;
+};
+#endif
+
 inline const char* boolToString(bool b) {
     return b ? "true" : "false";
 }
+
 } // namespace
 
 namespace android {
@@ -77,12 +96,6 @@
     std::unique_lock _lock{mutex};        \
     base::ScopedLockAssertion assumeLocked(mutex);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-static ReleaseBufferCallback EMPTY_RELEASE_CALLBACK =
-        [](const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
-           std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {};
-#endif
-
 void BLASTBufferItemConsumer::onDisconnect() {
     Mutex::Autolock lock(mMutex);
     mPreviouslyConnected = mCurrentlyConnected;
@@ -225,9 +238,8 @@
             this);
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-    std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> bufferReleaseConsumer;
-    gui::BufferReleaseChannel::open(mName, bufferReleaseConsumer, mBufferReleaseProducer);
-    mBufferReleaseReader = std::make_shared<BufferReleaseReader>(std::move(bufferReleaseConsumer));
+    gui::BufferReleaseChannel::open(mName, mBufferReleaseConsumer, mBufferReleaseProducer);
+    mBufferReleaseReader.emplace(*this);
 #endif
 
     BQA_LOGV("BLASTBufferQueue created");
@@ -260,7 +272,7 @@
     // safe default, most producers are expected to override this
     mProducer->setMaxDequeuedBufferCount(2);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-    mBufferReleaseThread.start(sp<BLASTBufferQueue>::fromExisting(this));
+    mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this));
 #endif
 }
 
@@ -636,7 +648,7 @@
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
     ReleaseBufferCallback releaseBufferCallback =
-            applyTransaction ? EMPTY_RELEASE_CALLBACK : makeReleaseBufferCallbackThunk();
+            applyTransaction ? nullptr : makeReleaseBufferCallbackThunk();
 #else
     auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
 #endif
@@ -1137,6 +1149,24 @@
 #endif
 };
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+class BBQBufferQueueCore : public BufferQueueCore {
+public:
+    explicit BBQBufferQueueCore(const wp<BLASTBufferQueue>& bbq) : mBLASTBufferQueue{bbq} {}
+
+    void notifyBufferReleased() const override {
+        sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+        if (!bbq) {
+            return;
+        }
+        bbq->mBufferReleaseReader->interruptBlockingRead();
+    }
+
+private:
+    wp<BLASTBufferQueue> mBLASTBufferQueue;
+};
+#endif
+
 // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
 // can be non-blocking when the producer is in the client process.
 class BBQBufferQueueProducer : public BufferQueueProducer {
@@ -1188,6 +1218,44 @@
         return BufferQueueProducer::query(what, value);
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    status_t waitForBufferRelease(std::unique_lock<std::mutex>& bufferQueueLock,
+                                  nsecs_t timeout) const override {
+        sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+        if (!bbq) {
+            return OK;
+        }
+
+        // BufferQueue has already checked if we have a free buffer. If there's an unread interrupt,
+        // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
+        // we don't miss an interrupt.
+        bbq->mBufferReleaseReader->clearInterrupts();
+        bbq->mThreadsBlockingOnDequeue++;
+        bufferQueueLock.unlock();
+        Deferred cleanup{[&]() {
+            bufferQueueLock.lock();
+            bbq->mThreadsBlockingOnDequeue--;
+        }};
+
+        ATRACE_FORMAT("waiting for free buffer");
+        ReleaseCallbackId id;
+        sp<Fence> fence;
+        uint32_t maxAcquiredBufferCount;
+        status_t status =
+                bbq->mBufferReleaseReader->readBlocking(id, fence, maxAcquiredBufferCount, timeout);
+        if (status == TIMED_OUT) {
+            return TIMED_OUT;
+        } else if (status != OK) {
+            // Waiting was interrupted or an error occurred. BufferQueueProducer will check if we
+            // have a free buffer and call this method again if not.
+            return OK;
+        }
+
+        bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+        return OK;
+    }
+#endif
+
 private:
     const wp<BLASTBufferQueue> mBLASTBufferQueue;
 };
@@ -1201,14 +1269,18 @@
     LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
     LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");
 
-    sp<BufferQueueCore> core(new BufferQueueCore());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    auto core = sp<BBQBufferQueueCore>::make(this);
+#else
+    auto core = sp<BufferQueueCore>::make();
+#endif
     LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
 
-    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
+    auto producer = sp<BBQBufferQueueProducer>::make(core, this);
     LOG_ALWAYS_FATAL_IF(producer == nullptr,
                         "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
 
-    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
+    auto consumer = sp<BufferQueueConsumer>::make(core);
     consumer->setAllowExtraAcquire(true);
     LOG_ALWAYS_FATAL_IF(consumer == nullptr,
                         "BLASTBufferQueue: failed to create BufferQueueConsumer");
@@ -1273,10 +1345,8 @@
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
 
-BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(
-        std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> endpoint)
-      : mEndpoint{std::move(endpoint)} {
-    mEpollFd = android::base::unique_fd{epoll_create1(0)};
+BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} {
+    mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)};
     LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
                         "Failed to create buffer release epoll file descriptor. errno=%d "
                         "message='%s'",
@@ -1284,9 +1354,9 @@
 
     epoll_event registerEndpointFd{};
     registerEndpointFd.events = EPOLLIN;
-    registerEndpointFd.data.fd = mEndpoint->getFd();
-    status_t status =
-            epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEndpoint->getFd(), &registerEndpointFd);
+    registerEndpointFd.data.fd = mBbq.mBufferReleaseConsumer->getFd();
+    status_t status = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mBbq.mBufferReleaseConsumer->getFd(),
+                                &registerEndpointFd);
     LOG_ALWAYS_FATAL_IF(status == -1,
                         "Failed to register buffer release consumer file descriptor with epoll. "
                         "errno=%d message='%s'",
@@ -1308,78 +1378,153 @@
                         errno, strerror(errno));
 }
 
-BLASTBufferQueue::BufferReleaseReader& BLASTBufferQueue::BufferReleaseReader::operator=(
-        BufferReleaseReader&& other) {
-    if (this != &other) {
-        ftl::FakeGuard guard{mMutex};
-        ftl::FakeGuard otherGuard{other.mMutex};
-        mEndpoint = std::move(other.mEndpoint);
-        mEpollFd = std::move(other.mEpollFd);
-        mEventFd = std::move(other.mEventFd);
-    }
-    return *this;
-}
-
 status_t BLASTBufferQueue::BufferReleaseReader::readBlocking(ReleaseCallbackId& outId,
                                                              sp<Fence>& outFence,
-                                                             uint32_t& outMaxAcquiredBufferCount) {
+                                                             uint32_t& outMaxAcquiredBufferCount,
+                                                             nsecs_t timeout) {
+    // TODO(b/363290953) epoll_wait only has millisecond timeout precision. If timeout is less than
+    // 1ms, then we round timeout up to 1ms. Otherwise, we round timeout to the nearest
+    // millisecond. Once epoll_pwait2 can be used in libgui, we can specify timeout with nanosecond
+    // precision.
+    int timeoutMs = -1;
+    if (timeout == 0) {
+        timeoutMs = 0;
+    } else if (timeout > 0) {
+        const int nsPerMs = 1000000;
+        if (timeout < nsPerMs) {
+            timeoutMs = 1;
+        } else {
+            timeoutMs = static_cast<int>(
+                    std::chrono::round<std::chrono::milliseconds>(std::chrono::nanoseconds{timeout})
+                            .count());
+        }
+    }
+
     epoll_event event{};
-    while (true) {
-        int eventCount = epoll_wait(mEpollFd.get(), &event, 1 /* maxevents */, -1 /* timeout */);
-        if (eventCount == 1) {
-            break;
-        }
-        if (eventCount == -1 && errno != EINTR) {
-            ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno,
-                  strerror(errno));
-        }
+    int eventCount;
+    do {
+        eventCount = epoll_wait(mEpollFd.get(), &event, 1 /*maxevents*/, timeoutMs);
+    } while (eventCount == -1 && errno != EINTR);
+
+    if (eventCount == -1) {
+        ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno,
+              strerror(errno));
+        return UNKNOWN_ERROR;
+    }
+
+    if (eventCount == 0) {
+        return TIMED_OUT;
     }
 
     if (event.data.fd == mEventFd.get()) {
-        uint64_t value;
-        if (read(mEventFd.get(), &value, sizeof(uint64_t)) == -1 && errno != EWOULDBLOCK) {
-            ALOGE("error while reading from eventfd. errno=%d message='%s'", errno,
-                  strerror(errno));
-        }
+        clearInterrupts();
         return WOULD_BLOCK;
     }
 
-    std::lock_guard lock{mMutex};
-    return mEndpoint->readReleaseFence(outId, outFence, outMaxAcquiredBufferCount);
+    return mBbq.mBufferReleaseConsumer->readReleaseFence(outId, outFence,
+                                                         outMaxAcquiredBufferCount);
 }
 
 void BLASTBufferQueue::BufferReleaseReader::interruptBlockingRead() {
-    uint64_t value = 1;
-    if (write(mEventFd.get(), &value, sizeof(uint64_t)) == -1) {
+    if (eventfd_write(mEventFd.get(), 1) == -1) {
         ALOGE("failed to notify dequeue event. errno=%d message='%s'", errno, strerror(errno));
     }
 }
 
-void BLASTBufferQueue::BufferReleaseThread::start(const sp<BLASTBufferQueue>& bbq) {
-    mRunning = std::make_shared<std::atomic_bool>(true);
-    mReader = bbq->mBufferReleaseReader;
-    std::thread([running = mRunning, reader = mReader, weakBbq = wp<BLASTBufferQueue>(bbq)]() {
+void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() {
+    eventfd_t value;
+    if (eventfd_read(mEventFd.get(), &value) == -1 && errno != EWOULDBLOCK) {
+        ALOGE("error while reading from eventfd. errno=%d message='%s'", errno, strerror(errno));
+    }
+}
+
+BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) {
+    android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)};
+    LOG_ALWAYS_FATAL_IF(!epollFd.ok(),
+                        "Failed to create buffer release background thread epoll file descriptor. "
+                        "errno=%d message='%s'",
+                        errno, strerror(errno));
+
+    epoll_event registerEndpointFd{};
+    registerEndpointFd.events = EPOLLIN;
+    registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd();
+    status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(),
+                                &registerEndpointFd);
+    LOG_ALWAYS_FATAL_IF(status == -1,
+                        "Failed to register background thread buffer release consumer file "
+                        "descriptor with epoll. errno=%d message='%s'",
+                        errno, strerror(errno));
+
+    // EventFd is used to break the background thread's loop.
+    android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)};
+    LOG_ALWAYS_FATAL_IF(!eventFd.ok(),
+                        "Failed to create background thread buffer release event file descriptor. "
+                        "errno=%d message='%s'",
+                        errno, strerror(errno));
+
+    epoll_event registerEventFd{};
+    registerEventFd.events = EPOLLIN;
+    registerEventFd.data.fd = eventFd.get();
+    status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &registerEventFd);
+    LOG_ALWAYS_FATAL_IF(status == -1,
+                        "Failed to register background thread event file descriptor with epoll. "
+                        "errno=%d message='%s'",
+                        errno, strerror(errno));
+
+    mEventFd = eventFd.get();
+
+    std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd),
+                 weakBbq = wp<BLASTBufferQueue>(bbq)]() {
         pthread_setname_np(pthread_self(), "BufferReleaseThread");
-        while (*running) {
-            ReleaseCallbackId id;
-            sp<Fence> fence;
-            uint32_t maxAcquiredBufferCount;
-            if (status_t status = reader->readBlocking(id, fence, maxAcquiredBufferCount);
-                status != OK) {
+        while (true) {
+            epoll_event event{};
+            int eventCount;
+            do {
+                eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/);
+            } while (eventCount == -1 && errno != EINTR);
+
+            if (eventCount == -1) {
+                ALOGE("epoll_wait error while waiting for buffer release in background thread. "
+                      "errno=%d message='%s'",
+                      errno, strerror(errno));
                 continue;
             }
+
+            // EventFd is used to join this thread.
+            if (event.data.fd == eventFd.get()) {
+                return;
+            }
+
             sp<BLASTBufferQueue> bbq = weakBbq.promote();
             if (!bbq) {
                 return;
             }
+
+            // If there are threads blocking on dequeue, give those threads priority for handling
+            // the release.
+            if (bbq->mThreadsBlockingOnDequeue > 0) {
+                std::this_thread::sleep_for(0ms);
+                continue;
+            }
+
+            ReleaseCallbackId id;
+            sp<Fence> fence;
+            uint32_t maxAcquiredBufferCount;
+            status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence,
+                                                                            maxAcquiredBufferCount);
+            if (status != OK) {
+                ALOGE("failed to read from buffer release consumer in background thread. errno=%d "
+                      "message='%s'",
+                      errno, strerror(errno));
+                continue;
+            }
             bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
         }
     }).detach();
 }
 
 BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
-    *mRunning = false;
-    mReader->interruptBlockingRead();
+    eventfd_write(mEventFd, 1);
 }
 
 #endif
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 69d25be..d0607bf 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -297,7 +297,11 @@
         // We might have freed a slot while dropping old buffers, or the producer
         // may be blocked waiting for the number of buffers in the queue to
         // decrease.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
 
         ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
 #ifndef NO_BINDER
@@ -350,7 +354,12 @@
         mCore->mActiveBuffers.erase(slot);
         mCore->mFreeSlots.insert(slot);
         mCore->clearBufferSlotLocked(slot);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
+
         VALIDATE_CONSISTENCY();
     }
 
@@ -520,7 +529,12 @@
         }
         BQ_LOGV("releaseBuffer: releasing slot %d", slot);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
+
         VALIDATE_CONSISTENCY();
     } // Autolock scope
 
@@ -574,7 +588,11 @@
     mCore->mQueue.clear();
     mCore->freeAllBuffersLocked();
     mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    mCore->notifyBufferReleased();
+#else
     mCore->mDequeueCondition.notify_all();
+#endif
     return NO_ERROR;
 }
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index e0c5b1f..d52cf70 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -371,6 +371,12 @@
     }
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+void BufferQueueCore::notifyBufferReleased() const {
+    mDequeueCondition.notify_all();
+}
+#endif
+
 #if DEBUG_ONLY_CODE
 void BufferQueueCore::validateConsistencyLocked() const {
     static const useconds_t PAUSE_TIME = 0;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index da74e9c..473a374 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -202,7 +202,11 @@
         if (delta < 0) {
             listener = mCore->mConsumerListener;
         }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
     } // Autolock scope
 
     // Call back without lock held
@@ -254,7 +258,12 @@
         }
         mCore->mAsyncMode = async;
         VALIDATE_CONSISTENCY();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
+
         if (delta < 0) {
             listener = mCore->mConsumerListener;
         }
@@ -376,6 +385,12 @@
                     (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                 return WOULD_BLOCK;
             }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+            if (status_t status = waitForBufferRelease(lock, mDequeueTimeout);
+                status == TIMED_OUT) {
+                return TIMED_OUT;
+            }
+#else
             if (mDequeueTimeout >= 0) {
                 std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
                         std::chrono::nanoseconds(mDequeueTimeout));
@@ -385,12 +400,29 @@
             } else {
                 mCore->mDequeueCondition.wait(lock);
             }
+#endif
         }
     } // while (tryAgain)
 
     return NO_ERROR;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+status_t BufferQueueProducer::waitForBufferRelease(std::unique_lock<std::mutex>& lock,
+                                                   nsecs_t timeout) const {
+    if (mDequeueTimeout >= 0) {
+        std::cv_status result =
+                mCore->mDequeueCondition.wait_for(lock, std::chrono::nanoseconds(timeout));
+        if (result == std::cv_status::timeout) {
+            return TIMED_OUT;
+        }
+    } else {
+        mCore->mDequeueCondition.wait(lock);
+    }
+    return OK;
+}
+#endif
+
 status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                             uint32_t width, uint32_t height, PixelFormat format,
                                             uint64_t usage, uint64_t* outBufferAge,
@@ -741,7 +773,11 @@
         mCore->mActiveBuffers.erase(slot);
         mCore->mFreeSlots.insert(slot);
         mCore->clearBufferSlotLocked(slot);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
         VALIDATE_CONSISTENCY();
     }
 
@@ -1082,7 +1118,11 @@
         }
 
         mCore->mBufferHasBeenQueued = true;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
         mCore->mLastQueuedSlot = slot;
 
         output->width = mCore->mDefaultWidth;
@@ -1218,7 +1258,11 @@
             bufferId = gb->getId();
         }
         mSlots[slot].mFence = fence;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        mCore->notifyBufferReleased();
+#else
         mCore->mDequeueCondition.notify_all();
+#endif
         listener = mCore->mConsumerListener;
         VALIDATE_CONSISTENCY();
     }
@@ -1457,7 +1501,11 @@
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                     mCore->mConnectedPid = -1;
                     mCore->mSidebandStream.clear();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+                    mCore->notifyBufferReleased();
+#else
                     mCore->mDequeueCondition.notify_all();
+#endif
                     mCore->mAutoPrerotation = false;
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
                     mCore->mAdditionalOptions.clear();
@@ -1508,7 +1556,6 @@
 
     const bool useDefaultSize = !width && !height;
     while (true) {
-        size_t newBufferCount = 0;
         uint32_t allocWidth = 0;
         uint32_t allocHeight = 0;
         PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN;
@@ -1530,8 +1577,9 @@
 
             // Only allocate one buffer at a time to reduce risks of overlapping an allocation from
             // both allocateBuffers and dequeueBuffer.
-            newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1;
-            if (newBufferCount == 0) {
+            if (mCore->mFreeSlots.empty()) {
+                BQ_LOGV("allocateBuffers: a slot was occupied while "
+                        "allocating. Dropping allocated buffer.");
                 return;
             }
 
@@ -1573,27 +1621,23 @@
         };
 #endif
 
-        Vector<sp<GraphicBuffer>> buffers;
-        for (size_t i = 0; i < newBufferCount; ++i) {
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
-            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
 #else
-            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
-                    allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
-                    allocUsage, allocName);
+        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                allocUsage, allocName);
 #endif
 
-            status_t result = graphicBuffer->initCheck();
+        status_t result = graphicBuffer->initCheck();
 
-            if (result != NO_ERROR) {
-                BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
-                        " %u, usage %#" PRIx64 ")", width, height, format, usage);
-                std::lock_guard<std::mutex> lock(mCore->mMutex);
-                mCore->mIsAllocating = false;
-                mCore->mIsAllocatingCondition.notify_all();
-                return;
-            }
-            buffers.push_back(graphicBuffer);
+        if (result != NO_ERROR) {
+            BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
+                    " %u, usage %#" PRIx64 ")", width, height, format, usage);
+            std::lock_guard<std::mutex> lock(mCore->mMutex);
+            mCore->mIsAllocating = false;
+            mCore->mIsAllocatingCondition.notify_all();
+            return;
         }
 
         { // Autolock scope
@@ -1621,15 +1665,13 @@
                 continue;
             }
 
-            for (size_t i = 0; i < newBufferCount; ++i) {
-                if (mCore->mFreeSlots.empty()) {
-                    BQ_LOGV("allocateBuffers: a slot was occupied while "
-                            "allocating. Dropping allocated buffer.");
-                    continue;
-                }
+            if (mCore->mFreeSlots.empty()) {
+                BQ_LOGV("allocateBuffers: a slot was occupied while "
+                        "allocating. Dropping allocated buffer.");
+            } else {
                 auto slot = mCore->mFreeSlots.begin();
                 mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
-                mSlots[*slot].mGraphicBuffer = buffers[i];
+                mSlots[*slot].mGraphicBuffer = graphicBuffer;
                 mSlots[*slot].mFence = Fence::NO_FENCE;
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
                 mSlots[*slot].mAdditionalOptionsGenerationId = allocOptionsGenId;
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index 27367aa..e9c6ef3 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -136,6 +136,7 @@
 status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
         ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
         uint32_t& outMaxAcquiredBufferCount) {
+    std::lock_guard lock{mMutex};
     Message message;
     mFlattenedBuffer.resize(message.getFlattenedSize());
     std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
@@ -152,7 +153,7 @@
             .msg_controllen = controlMessageBuffer.size(),
     };
 
-    int result;
+    ssize_t result;
     do {
         result = recvmsg(mFd, &msg, 0);
     } while (result == -1 && errno == EINTR);
@@ -242,7 +243,7 @@
         memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
     }
 
-    int result;
+    ssize_t result;
     do {
         result = sendmsg(mFd, &msg, 0);
     } while (result == -1 && errno == EINTR);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8592cff..99c64da 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -150,6 +150,9 @@
 private:
     friend class BLASTBufferQueueHelper;
     friend class BBQBufferQueueProducer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    friend class BBQBufferQueueCore;
+#endif
 
     // can't be copied
     BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
@@ -317,48 +320,52 @@
     std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the
+    // client.
+    std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer;
+    std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+
     class BufferReleaseReader {
     public:
-        BufferReleaseReader() = default;
-        BufferReleaseReader(std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint>);
-        BufferReleaseReader& operator=(BufferReleaseReader&&);
+        explicit BufferReleaseReader(BLASTBufferQueue&);
+
+        BufferReleaseReader(const BufferReleaseReader&) = delete;
+        BufferReleaseReader& operator=(const BufferReleaseReader&) = delete;
 
         // Block until we can read a buffer release message.
         //
         // Returns:
         // * OK if a ReleaseCallbackId and Fence were successfully read.
         // * WOULD_BLOCK if the blocking read was interrupted by interruptBlockingRead.
+        // * TIMED_OUT if the blocking read timed out.
         // * UNKNOWN_ERROR if something went wrong.
         status_t readBlocking(ReleaseCallbackId& outId, sp<Fence>& outReleaseFence,
-                              uint32_t& outMaxAcquiredBufferCount);
+                              uint32_t& outMaxAcquiredBufferCount, nsecs_t timeout);
 
-        // Signals the reader's eventfd to wake up any threads waiting on readBlocking.
         void interruptBlockingRead();
+        void clearInterrupts();
 
     private:
-        std::mutex mMutex;
-        std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mEndpoint GUARDED_BY(mMutex);
+        BLASTBufferQueue& mBbq;
+
         android::base::unique_fd mEpollFd;
         android::base::unique_fd mEventFd;
     };
 
-    // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to
-    // the client. See BBQBufferQueueProducer::dequeueBuffer for details.
-    std::shared_ptr<BufferReleaseReader> mBufferReleaseReader;
-    std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+    std::optional<BufferReleaseReader> mBufferReleaseReader;
+
+    std::atomic<int> mThreadsBlockingOnDequeue = 0;
 
     class BufferReleaseThread {
     public:
-        BufferReleaseThread() = default;
+        BufferReleaseThread(const sp<BLASTBufferQueue>&);
         ~BufferReleaseThread();
-        void start(const sp<BLASTBufferQueue>&);
 
     private:
-        std::shared_ptr<std::atomic_bool> mRunning;
-        std::shared_ptr<BufferReleaseReader> mReader;
+        int mEventFd;
     };
 
-    BufferReleaseThread mBufferReleaseThread;
+    std::optional<BufferReleaseThread> mBufferReleaseThread;
 #endif
 };
 
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index d5dd7c8..77cdf2c 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -80,6 +80,13 @@
     BufferQueueCore();
     virtual ~BufferQueueCore();
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+protected:
+    // Wake up any threads waiting for a buffer release. The BufferQueue mutex should always held
+    // when this method is called.
+    virtual void notifyBufferReleased() const;
+#endif
+
 private:
     // Dump our state in a string
     void dumpState(const String8& prefix, String8* outResult) const;
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 37a9607..086ce7c 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -218,6 +218,14 @@
     // total maximum buffer count for the buffer queue (dequeued AND acquired)
     status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+    // Wait until a buffer has been released. The method may spuriously return OK when no buffer has
+    // been released. The BufferQueue mutex is passed in the locked state. It must be unlocked
+    // before waiting for a release and locked before returning.
+    virtual status_t waitForBufferRelease(std::unique_lock<std::mutex>& lock,
+                                          nsecs_t timeout) const;
+#endif
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h
index 51fe0b6..0edadec 100644
--- a/libs/gui/include/gui/BufferReleaseChannel.h
+++ b/libs/gui/include/gui/BufferReleaseChannel.h
@@ -69,7 +69,8 @@
                                   sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);
 
     private:
-        std::vector<uint8_t> mFlattenedBuffer;
+        std::mutex mMutex;
+        std::vector<uint8_t> mFlattenedBuffer GUARDED_BY(mMutex);
     };
 
     class ProducerEndpoint : public Endpoint, public Parcelable {
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 845a1ca..3b6a66e 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -30,6 +30,7 @@
 static constexpr int kHeight = 100;
 static constexpr int kMaxLockedBuffers = 3;
 static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kUsage = GRALLOC_USAGE_SW_READ_RARELY;
 static constexpr int kFrameSleepUs = 30 * 1000;
 
 class BufferItemConsumerTest : public ::testing::Test {
@@ -56,7 +57,7 @@
     };
 
     void SetUp() override {
-        mBIC = new BufferItemConsumer(kFormat, kMaxLockedBuffers, true);
+        mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
         String8 name("BufferItemConsumer_Under_Test");
         mBIC->setName(name);
         mBFL = new BufferFreedListener(this);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index c903031..4a6f66e 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -191,7 +191,9 @@
         mKeyboardLayoutInfo(other.mKeyboardLayoutInfo),
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
-        mKeyCharacterMap(other.mKeyCharacterMap),
+        mKeyCharacterMap(other.mKeyCharacterMap
+                                 ? std::make_unique<KeyCharacterMap>(*other.mKeyCharacterMap)
+                                 : nullptr),
         mUsiVersion(other.mUsiVersion),
         mAssociatedDisplayId(other.mAssociatedDisplayId),
         mEnabled(other.mEnabled),
@@ -204,6 +206,34 @@
         mLights(other.mLights),
         mViewBehavior(other.mViewBehavior) {}
 
+InputDeviceInfo& InputDeviceInfo::operator=(const InputDeviceInfo& other) {
+    mId = other.mId;
+    mGeneration = other.mGeneration;
+    mControllerNumber = other.mControllerNumber;
+    mIdentifier = other.mIdentifier;
+    mAlias = other.mAlias;
+    mIsExternal = other.mIsExternal;
+    mHasMic = other.mHasMic;
+    mKeyboardLayoutInfo = other.mKeyboardLayoutInfo;
+    mSources = other.mSources;
+    mKeyboardType = other.mKeyboardType;
+    mKeyCharacterMap = other.mKeyCharacterMap
+            ? std::make_unique<KeyCharacterMap>(*other.mKeyCharacterMap)
+            : nullptr;
+    mUsiVersion = other.mUsiVersion;
+    mAssociatedDisplayId = other.mAssociatedDisplayId;
+    mEnabled = other.mEnabled;
+    mHasVibrator = other.mHasVibrator;
+    mHasBattery = other.mHasBattery;
+    mHasButtonUnderPad = other.mHasButtonUnderPad;
+    mHasSensor = other.mHasSensor;
+    mMotionRanges = other.mMotionRanges;
+    mSensors = other.mSensors;
+    mLights = other.mLights;
+    mViewBehavior = other.mViewBehavior;
+    return *this;
+}
+
 InputDeviceInfo::~InputDeviceInfo() {
 }
 
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index d775327..90d29dd 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -84,15 +84,15 @@
 
 KeyCharacterMap::KeyCharacterMap(const std::string& filename) : mLoadFileName(filename) {}
 
-base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+base::Result<std::unique_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
                                                                      Format format) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
         return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
     }
-    std::shared_ptr<KeyCharacterMap> map =
-            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
+    std::unique_ptr<KeyCharacterMap> map =
+            std::unique_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
     if (!map.get()) {
         ALOGE("Error allocating key character map.");
         return Errorf("Error allocating key character map.");
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index e2cc6fb..056db09 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <iomanip>
 #include <ostream>
 
 #include <android-base/logging.h>
@@ -37,6 +38,11 @@
         true;
 #endif
 
+/**
+ * Log debug messages about timestamp and coordinates of event resampling.
+ * Enable this via "adb shell setprop log.tag.LegacyResamplerResampling DEBUG"
+ * (requires restart)
+ */
 bool debugResampling() {
     if (!IS_DEBUGGABLE_BUILD) {
         static const bool DEBUG_TRANSPORT_RESAMPLING =
@@ -107,46 +113,44 @@
     const size_t latestIndex = numSamples - 1;
     const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
     for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
-        std::vector<Pointer> pointers;
-        const size_t numPointers = motionEvent.getPointerCount();
-        for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
-            pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
-                                       *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
-                                                                                   sampleIndex))});
+        PointerMap pointerMap;
+        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+             ++pointerIndex) {
+            pointerMap.insert(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
+                                      *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
+                                                                                  sampleIndex))});
         }
         mLatestSamples.pushBack(
-                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
+                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointerMap});
     }
 }
 
 LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
-    std::vector<Pointer> pointers;
+    PointerMap pointerMap;
     for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
-        pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
-                                   message.body.motion.pointers[i].coords});
+        pointerMap.insert(Pointer{message.body.motion.pointers[i].properties,
+                                  message.body.motion.pointers[i].coords});
     }
-    return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
+    return Sample{nanoseconds{message.body.motion.eventTime}, pointerMap};
 }
 
 bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
-    if (target.pointers.size() > auxiliary.pointers.size()) {
-        LOG_IF(INFO, debugResampling())
-                << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
-        return false;
-    }
-    for (size_t i = 0; i < target.pointers.size(); ++i) {
-        if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
-            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+    for (const Pointer& pointer : target.pointerMap) {
+        const std::optional<Pointer> auxiliaryPointer =
+                auxiliary.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+        if (!auxiliaryPointer.has_value()) {
+            LOG_IF(INFO, debugResampling())
+                    << "Not resampled. Auxiliary sample does not contain all pointers from target.";
             return false;
         }
-        if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+        if (pointer.properties.toolType != auxiliaryPointer->properties.toolType) {
             LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
             return false;
         }
-        if (!canResampleTool(target.pointers[i].properties.toolType)) {
+        if (!canResampleTool(pointer.properties.toolType)) {
             LOG_IF(INFO, debugResampling())
                     << "Not resampled. Cannot resample "
-                    << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+                    << ftl::enum_string(pointer.properties.toolType) << " ToolType.";
             return false;
         }
     }
@@ -166,35 +170,40 @@
 
     const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too small: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     }
     return true;
 }
 
 std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
-        nanoseconds resampleTime, const InputMessage& futureSample) const {
-    if (!canInterpolate(futureSample)) {
+        nanoseconds resampleTime, const InputMessage& futureMessage) const {
+    if (!canInterpolate(futureMessage)) {
         return std::nullopt;
     }
     LOG_IF(FATAL, mLatestSamples.empty())
             << "Not resampled. mLatestSamples must not be empty to interpolate.";
 
     const Sample& pastSample = *(mLatestSamples.end() - 1);
+    const Sample& futureSample = messageToSample(futureMessage);
 
-    const nanoseconds delta =
-            nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
+    const nanoseconds delta = nanoseconds{futureSample.eventTime} - pastSample.eventTime;
     const float alpha =
-            std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+            std::chrono::duration<float, std::nano>(resampleTime - pastSample.eventTime) / delta;
 
-    std::vector<Pointer> resampledPointers;
-    for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
-        const PointerCoords& resampledCoords =
-                calculateResampledCoords(pastSample.pointers[i].coords,
-                                         futureSample.body.motion.pointers[i].coords, alpha);
-        resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+    PointerMap resampledPointerMap;
+    for (const Pointer& pointer : pastSample.pointerMap) {
+        if (std::optional<Pointer> futureSamplePointer =
+                    futureSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+            futureSamplePointer.has_value()) {
+            const PointerCoords& resampledCoords =
+                    calculateResampledCoords(pointer.coords, futureSamplePointer->coords, alpha);
+            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
+        }
     }
-    return Sample{resampleTime, resampledPointers};
+    return Sample{resampleTime, resampledPointerMap};
 }
 
 bool LegacyResampler::canExtrapolate() const {
@@ -212,10 +221,14 @@
 
     const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too small: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     } else if (delta > RESAMPLE_MAX_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too large: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     }
     return true;
@@ -241,20 +254,28 @@
             (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
     LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
             << "Resample time is too far in the future. Adjusting prediction from "
-            << (resampleTime - presentSample.eventTime) << " to "
-            << (farthestPrediction - presentSample.eventTime) << "ns.";
+            << std::setprecision(3)
+            << std::chrono::duration<double, std::milli>{resampleTime - presentSample.eventTime}
+                       .count()
+            << "ms to "
+            << std::chrono::duration<double, std::milli>{farthestPrediction -
+                                                         presentSample.eventTime}
+                       .count()
+            << "ms";
     const float alpha =
-            std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
-            delta;
+            std::chrono::duration<float, std::nano>(newResampleTime - pastSample.eventTime) / delta;
 
-    std::vector<Pointer> resampledPointers;
-    for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
-        const PointerCoords& resampledCoords =
-                calculateResampledCoords(pastSample.pointers[i].coords,
-                                         presentSample.pointers[i].coords, alpha);
-        resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+    PointerMap resampledPointerMap;
+    for (const Pointer& pointer : presentSample.pointerMap) {
+        if (std::optional<Pointer> pastSamplePointer =
+                    pastSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+            pastSamplePointer.has_value()) {
+            const PointerCoords& resampledCoords =
+                    calculateResampledCoords(pastSamplePointer->coords, pointer.coords, alpha);
+            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
+        }
     }
-    return Sample{newResampleTime, resampledPointers};
+    return Sample{newResampleTime, resampledPointerMap};
 }
 
 inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
@@ -267,6 +288,12 @@
     return RESAMPLE_LATENCY;
 }
 
+/**
+ * The resampler is unaware of ACTION_DOWN. Thus, it needs to constantly check for pointer IDs
+ * occurrences. This problem could be fixed if the resampler has access to the entire stream of
+ * MotionEvent actions. That way, both ACTION_DOWN and ACTION_UP will be visible; therefore,
+ * facilitating pointer tracking between samples.
+ */
 void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
     const size_t numSamples = motionEvent.getHistorySize() + 1;
     for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
@@ -276,34 +303,59 @@
 }
 
 void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+    if (!mLastRealSample.has_value() || !mPreviousPrediction.has_value()) {
+        LOG_IF(INFO, debugResampling()) << "Still pointers not overwritten. Not enough data.";
+        return;
+    }
     for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
+        const std::optional<Pointer> lastRealPointer = mLastRealSample->pointerMap.find(
+                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+        const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
+                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+        // This could happen because resampler only receives ACTION_MOVE events.
+        if (!lastRealPointer.has_value() || !previousPointer.has_value()) {
+            continue;
+        }
         const PointerCoords& pointerCoords =
                 *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
-        if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
+        if (equalXY(pointerCoords, lastRealPointer->coords)) {
             LOG_IF(INFO, debugResampling())
                     << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
                     << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
-                    << mLastRealSample->pointers[pointerIndex].coords;
+                    << previousPointer->coords;
             setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
-                                        mPreviousPrediction->pointers[pointerIndex].coords);
+                                        previousPointer->coords);
         }
     }
 }
 
 void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
     if (!mPreviousPrediction.has_value()) {
+        LOG_IF(INFO, debugResampling()) << "Old sample not overwritten. Not enough data.";
         return;
     }
     if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
         mPreviousPrediction->eventTime) {
         LOG_IF(INFO, debugResampling())
                 << "Motion event sample older than predicted sample. Overwriting event time from "
-                << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
-                << mPreviousPrediction->eventTime.count() << "ns.";
+                << std::setprecision(3)
+                << std::chrono::duration<double,
+                                         std::milli>{nanoseconds{motionEvent.getHistoricalEventTime(
+                                                             sampleIndex)}}
+                           .count()
+                << "ms to "
+                << std::chrono::duration<double, std::milli>{mPreviousPrediction->eventTime}.count()
+                << "ms";
         for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
              ++pointerIndex) {
+            const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
+                    PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+            // This could happen because resampler only receives ACTION_MOVE events.
+            if (!previousPointer.has_value()) {
+                continue;
+            }
             setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
-                                        mPreviousPrediction->pointers[pointerIndex].coords);
+                                        previousPointer->coords);
         }
     }
 }
@@ -333,6 +385,7 @@
             mPreviousPrediction = sample;
         }
     }
+    LOG_IF(FATAL, mLatestSamples.empty()) << "mLatestSamples must contain at least one sample.";
     mLastRealSample = *(mLatestSamples.end() - 1);
 }
 
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 701fb43..e93c04d 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -214,3 +214,10 @@
   description: "Enable telemetry for rotary input"
   bug: "370353565"
 }
+
+flag {
+  name: "set_input_device_kernel_wake"
+  namespace: "input"
+  description: "Set input device's power/wakeup sysfs node"
+  bug: "372812925"
+}
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index fae8518..3162b77 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -648,7 +648,15 @@
 
     mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
 
-    assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+    assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+                                              {Pointer{.id = 0,
+                                                       .x = 1.4f,
+                                                       .y = 1.4f,
+                                                       .isResampled = true},
+                                               Pointer{.id = 1,
+                                                       .x = 2.4f,
+                                                       .y = 2.4f,
+                                                       .isResampled = true}});
 }
 
 TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {
@@ -670,7 +678,15 @@
 
     mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
 
-    assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+    assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
+                                              {Pointer{.id = 1,
+                                                       .x = 4.4f,
+                                                       .y = 4.4f,
+                                                       .isResampled = true},
+                                               Pointer{.id = 0,
+                                                       .x = 3.4f,
+                                                       .y = 3.4f,
+                                                       .isResampled = true}});
 }
 
 TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) {
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d05ff34..5a78a5c 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -175,6 +175,14 @@
     AHARDWAREBUFFER_FORMAT_YCbCr_P010               = 0x36,
 
     /**
+     * YUV P210 format.
+     * Must have an even width and height. Can be accessed in OpenGL
+     * shaders through an external sampler. Does not support mip-maps
+     * cube-maps or multi-layered textures.
+     */
+    AHARDWAREBUFFER_FORMAT_YCbCr_P210               = 0x3c,
+
+    /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R8_UNORM
      *   OpenGL ES: GR_GL_R8
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index f19b908..a1d986e 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -220,7 +220,13 @@
         Self(buffer)
     }
 
-    /// Get the internal |AHardwareBuffer| pointer without decrementing the refcount. This can
+    /// Get the internal `AHardwareBuffer` pointer that is only valid when this `HardwareBuffer`
+    /// exists. This can be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+    pub fn as_raw(&self) -> NonNull<AHardwareBuffer> {
+        self.0
+    }
+
+    /// Get the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
     /// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
     pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
         let buffer = ManuallyDrop::new(self);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 3be8ddc..7012df2 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -548,6 +548,10 @@
                 .flags = ANDROID_DLEXT_USE_NAMESPACE,
                 .library_namespace = ns,
         };
+        auto prop = base::GetProperty("debug.angle.libs.suffix", "");
+        if (!prop.empty()) {
+            name = std::string("lib") + kind + "_" + prop + ".so";
+        }
         so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
     }
 
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
index a24822a..6e0a9f7 100644
--- a/services/gpuservice/vts/Android.bp
+++ b/services/gpuservice/vts/Android.bp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 package {
+    default_team: "trendy_team_android_gpu",
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
diff --git a/services/inputflinger/InputDeviceMetricsSource.cpp b/services/inputflinger/InputDeviceMetricsSource.cpp
index dee4cb8..70262fb 100644
--- a/services/inputflinger/InputDeviceMetricsSource.cpp
+++ b/services/inputflinger/InputDeviceMetricsSource.cpp
@@ -50,6 +50,18 @@
     return InputDeviceUsageSource::BUTTONS;
 }
 
+std::set<InputDeviceUsageSource> getUsageSourcesForKeyArgs(
+        const NotifyKeyArgs& args, const std::vector<InputDeviceInfo>& inputDevices) {
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+    for (const InputDeviceInfo& inputDevice : inputDevices) {
+        if (args.deviceId == inputDevice.getId()) {
+            keyboardType = inputDevice.getKeyboardType();
+            break;
+        }
+    }
+    return std::set{getUsageSourceForKeyArgs(keyboardType, args)};
+}
+
 std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
     LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
     std::set<InputDeviceUsageSource> sources;
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
index a6be8f4..702badd 100644
--- a/services/inputflinger/InputDeviceMetricsSource.h
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -54,6 +54,10 @@
 /** Returns the InputDeviceUsageSource that corresponds to the key event. */
 InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
 
+/** Returns the InputDeviceUsageSources that correspond to the key event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForKeyArgs(
+        const NotifyKeyArgs&, const std::vector<InputDeviceInfo>& inputDevices);
+
 /** Returns the InputDeviceUsageSources that correspond to the motion event. */
 std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 602904f..4f9d9e4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4551,7 +4551,7 @@
             if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
                 IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
                 !mInputFilterEnabled) {
-                mLatencyTracker.trackNotifyKey(args);
+                mLatencyTracker.trackListener(args);
             }
         }
 
@@ -4687,7 +4687,7 @@
         if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
             IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
             !mInputFilterEnabled) {
-            mLatencyTracker.trackNotifyMotion(args);
+            mLatencyTracker.trackListener(args);
         }
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 0852026..0921e37 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -20,6 +20,7 @@
 
 #include <inttypes.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
@@ -32,6 +33,8 @@
 
 namespace android::inputdispatcher {
 
+namespace {
+
 /**
  * Events that are older than this time will be considered mature, at which point we will stop
  * waiting for the apps to provide further information about them.
@@ -62,27 +65,25 @@
     }
 }
 
+} // namespace
+
 LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor)
       : mTimelineProcessor(&processor) {}
 
-void LatencyTracker::trackNotifyMotion(const NotifyMotionArgs& args) {
-    std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
-    trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
-                  InputEventType::MOTION);
-}
+void LatencyTracker::trackListener(const NotifyArgs& args) {
+    if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) {
+        std::set<InputDeviceUsageSource> sources =
+                getUsageSourcesForKeyArgs(*keyArgs, mInputDevices);
+        trackListener(keyArgs->id, keyArgs->eventTime, keyArgs->readTime, keyArgs->deviceId,
+                      sources, keyArgs->action, InputEventType::KEY);
 
-void LatencyTracker::trackNotifyKey(const NotifyKeyArgs& args) {
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE;
-    for (auto& inputDevice : mInputDevices) {
-        if (args.deviceId == inputDevice.getId()) {
-            keyboardType = inputDevice.getKeyboardType();
-            break;
-        }
+    } else if (const NotifyMotionArgs* motionArgs = std::get_if<NotifyMotionArgs>(&args)) {
+        std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(*motionArgs);
+        trackListener(motionArgs->id, motionArgs->eventTime, motionArgs->readTime,
+                      motionArgs->deviceId, sources, motionArgs->action, InputEventType::MOTION);
+    } else {
+        LOG(FATAL) << "Unexpected NotifyArgs type: " << args.index();
     }
-    std::set<InputDeviceUsageSource> sources =
-            std::set{getUsageSourceForKeyArgs(keyboardType, args)};
-    trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
-                  InputEventType::KEY);
 }
 
 void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime,
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index eb58222..79ea14c 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -44,28 +44,20 @@
      */
     LatencyTracker(InputEventTimelineProcessor& processor);
     /**
-     * Start keeping track of an event identified by inputEventId. This must be called first.
+     * Start keeping track of an event identified by the args. This must be called first.
      * If duplicate events are encountered (events that have the same eventId), none of them will be
-     * tracked. This is because there is not enough information to correctly track them. The api's
-     * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the
-     * eventTime. Even if eventTime was provided, there would still be a possibility of having
-     * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
-     * must drop all duplicate data.
+     * tracked. This is because there is not enough information to correctly track them. It is
+     * always possible that two different events are generated with the same inputEventId and the
+     * same eventTime, so there aren't ways to distinguish those. Therefore, we must drop all
+     * duplicate data.
+     * For that reason, the APIs 'trackFinishedEvent' and 'trackGraphicsLatency' only receive the
+     * inputEventId as input.
      */
-    void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
-                       const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
-                       InputEventType inputEventType);
+    void trackListener(const NotifyArgs& args);
     void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
                             nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
     void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
-    /**
-     * trackNotifyMotion and trackNotifyKeys are intermediates between InputDispatcher and
-     * trackListener. They compute the InputDeviceUsageSource set and call trackListener with
-     * the relevant parameters for latency computation.
-     */
-    void trackNotifyMotion(const NotifyMotionArgs& args);
-    void trackNotifyKey(const NotifyKeyArgs& args);
 
     std::string dump(const char* prefix) const;
     void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
@@ -90,6 +82,10 @@
 
     InputEventTimelineProcessor* mTimelineProcessor;
     std::vector<InputDeviceInfo> mInputDevices;
+
+    void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
+                       const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
+                       InputEventType inputEventType);
     void reportAndPruneMatureRecords(nsecs_t newEventTime);
 };
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 756a29b..6093c91 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -426,6 +426,16 @@
 
     /* Notifies that mouse cursor faded due to typing. */
     virtual void notifyMouseCursorFadedOnTyping() = 0;
+
+    /* Set whether the given input device can wake up the kernel from sleep
+     * when it generates input events. By default, usually only internal (built-in)
+     * input devices can wake the kernel from sleep. For an external input device
+     * that supports remote wakeup to be able to wake the kernel, this must be called
+     * after each time the device is connected/added.
+     *
+     * Returns true if setting power wakeup was successful.
+     */
+    virtual bool setKernelWakeEnabled(int32_t deviceId, bool enabled) = 0;
 };
 
 // --- TouchAffineTransformation ---
diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
index 5b94d57..13eaaf3 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -24,13 +24,15 @@
 #include <input/Keyboard.h>
 #include <utils/Timers.h> // for nsecs_t, systemTime
 
+#include <cstdint>
 #include <vector>
 
 namespace android {
 
 class MotionArgsBuilder {
 public:
-    MotionArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
+    MotionArgsBuilder(int32_t action, int32_t source, int32_t eventId = InputEvent::nextId())
+          : mEventId(eventId) {
         mAction = action;
         if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
             addFlag(AMOTION_EVENT_FLAG_CANCELED);
@@ -55,6 +57,11 @@
         return *this;
     }
 
+    MotionArgsBuilder& readTime(nsecs_t readTime) {
+        mReadTime = readTime;
+        return *this;
+    }
+
     MotionArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
         mDisplayId = displayId;
         return *this;
@@ -121,7 +128,7 @@
 
         return {mEventId,
                 mEventTime,
-                /*readTime=*/mEventTime,
+                mReadTime.value_or(mEventTime),
                 mDeviceId,
                 mSource,
                 mDisplayId,
@@ -151,6 +158,7 @@
     uint32_t mSource;
     nsecs_t mDownTime;
     nsecs_t mEventTime;
+    std::optional<nsecs_t> mReadTime;
     ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
     uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
     int32_t mActionButton{0};
@@ -165,7 +173,8 @@
 
 class KeyArgsBuilder {
 public:
-    KeyArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
+    KeyArgsBuilder(int32_t action, int32_t source, int32_t eventId = InputEvent::nextId())
+          : mEventId(eventId) {
         mAction = action;
         mSource = source;
         mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -187,6 +196,11 @@
         return *this;
     }
 
+    KeyArgsBuilder& readTime(nsecs_t readTime) {
+        mReadTime = readTime;
+        return *this;
+    }
+
     KeyArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
         mDisplayId = displayId;
         return *this;
@@ -214,18 +228,10 @@
     }
 
     NotifyKeyArgs build() const {
-        return {mEventId,
-                mEventTime,
-                /*readTime=*/mEventTime,
-                mDeviceId,
-                mSource,
-                mDisplayId,
-                mPolicyFlags,
-                mAction,
-                mFlags,
-                mKeyCode,
-                mScanCode,
-                mMetaState,
+        return {mEventId,     mEventTime, mReadTime.value_or(mEventTime),
+                mDeviceId,    mSource,    mDisplayId,
+                mPolicyFlags, mAction,    mFlags,
+                mKeyCode,     mScanCode,  mMetaState,
                 mDownTime};
     }
 
@@ -236,6 +242,7 @@
     uint32_t mSource;
     nsecs_t mDownTime;
     nsecs_t mEventTime;
+    std::optional<nsecs_t> mReadTime;
     ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
     uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
     int32_t mFlags{0};
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f8cd973..8dec9e5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -2848,6 +2848,35 @@
     mNeedToReopenDevices = true;
 }
 
+bool EventHub::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    std::scoped_lock _l(mLock);
+    std::string enabledStr = enabled ? "enabled" : "disabled";
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr) {
+        ALOGE("Device Id %d does not exist for setting power wakeup", deviceId);
+        return false;
+    }
+    if (device->associatedDevice == nullptr) {
+        return false;
+    }
+    std::filesystem::path currentPath = device->associatedDevice->sysfsRootPath;
+    while (!currentPath.empty() && currentPath != "/") {
+        std::string nodePath = currentPath / "power/wakeup";
+        if (std::filesystem::exists(nodePath)) {
+            if (base::WriteStringToFile(enabledStr, nodePath)) {
+                return true;
+
+            }
+            // No need to continue searching in parent directories as power/wakeup nodes
+            // higher up may control other subdevices.
+            ALOGW("Failed to set power/wakeup node at %s", nodePath.c_str());
+            return false;
+        }
+        currentPath = currentPath.parent_path();
+    }
+    return false;
+}
+
 void EventHub::dump(std::string& dump) const {
     dump += "Event Hub State:\n";
 
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 02eeb0a..5e42d57 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -745,6 +745,14 @@
     }
 }
 
+bool InputDevice::setKernelWakeEnabled(bool enabled) {
+    bool success = false;
+    for_each_subdevice([&enabled, &success](InputDeviceContext& context) {
+        success |= context.setKernelWakeEnabled(enabled);
+    });
+    return success;
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ab27042..a2b7e82 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -901,6 +901,16 @@
     mPreventingTouchpadTaps = true;
 }
 
+bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->setKernelWakeEnabled(enabled);
+    }
+    return false;
+}
+
 void InputReader::dump(std::string& dump) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index dffd8e3..4336945 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -396,6 +396,13 @@
     /* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery,
      * etc. is detected. */
     virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
+
+    /* Set whether the given input device can wake up the kernel from sleep
+     * when it generates input events. By default, usually only internal (built-in)
+     * input devices can wake the kernel from sleep. For an external input device
+     * that supports remote wakeup to be able to wake the kernel, this must be called
+     * after each time the device is connected/added. */
+    virtual bool setKernelWakeEnabled(int32_t deviceId, bool enabled) = 0;
 };
 
 template <std::size_t BITS>
@@ -603,6 +610,8 @@
 
     void sysfsNodeChanged(const std::string& sysfsNodePath) override final;
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override final;
+
     ~EventHub() override;
 
 private:
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 8958d9e..abe7a5f 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -139,6 +139,8 @@
 
     std::optional<HardwareProperties> getTouchpadHardwareProperties();
 
+    bool setKernelWakeEnabled(bool enabled);
+
     // construct and add a mapper to the input device
     template <class T, typename... Args>
     T& addMapper(int32_t eventHubId, Args... args) {
@@ -469,6 +471,9 @@
     inline void setKeyboardType(KeyboardType keyboardType) {
         return mDevice.setKeyboardType(keyboardType);
     }
+    inline bool setKernelWakeEnabled(bool enabled) {
+        return mEventHub->setKernelWakeEnabled(mId, enabled);
+    }
 
 private:
     InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1003871..7614a05 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -120,6 +120,8 @@
 
     void notifyMouseCursorFadedOnTyping() override;
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 567a3e2..a433a72 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -132,7 +132,9 @@
 void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
-    info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
+    if (const auto kcm = getDeviceContext().getKeyCharacterMap(); kcm != nullptr) {
+        info.setKeyCharacterMap(std::make_unique<KeyCharacterMap>(*kcm));
+    }
 
     std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
     if (keyboardLayoutInfo) {
@@ -241,11 +243,16 @@
     mHidUsageAccumulator.process(rawEvent);
     switch (rawEvent.type) {
         case EV_KEY: {
-            int32_t scanCode = rawEvent.code;
+            // Skip processing repeated keys (value == 2) since auto repeat is handled by Android
+            // internally.
+            if (rawEvent.value == 2) {
+                break;
+            }
 
+            const int32_t scanCode = rawEvent.code;
             if (isSupportedScanCode(scanCode)) {
-                out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0,
-                                  scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
+                out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0, scanCode,
+                                  mHidUsageAccumulator.consumeCurrentHidUsage());
             }
             break;
         }
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 943de6e..e72c440 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -650,4 +650,25 @@
     }
 }
 
+bool FakeEventHub::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        return false;
+    }
+    mKernelWakeup.emplace(deviceId, enabled);
+    return true;
+}
+
+bool FakeEventHub::fakeReadKernelWakeup(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        return false;
+    }
+    auto it = mKernelWakeup.find(deviceId);
+    if (it == mKernelWakeup.end()) {
+        return false;
+    }
+    return it->second;
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 2dfbb23..143b93b 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -94,6 +94,8 @@
     // Simulates a device light intensities, from light id to light intensities map.
     std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
             mLightIntensities;
+    // fake sysfs node path and value.
+    std::unordered_map<int32_t /*deviceId*/, bool /* wakeupNode*/> mKernelWakeup;
 
 public:
     static constexpr int32_t DEFAULT_BATTERY = 1;
@@ -158,6 +160,8 @@
     void setMtSlotValues(int32_t deviceId, int32_t axis, const std::vector<int32_t>& values);
     base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
                                                        size_t slotCount) const override;
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override;
+    bool fakeReadKernelWakeup(int32_t deviceId) const;
 
 private:
     Device* getDevice(int32_t deviceId) const;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 6c8b65c..19b738e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1392,6 +1392,20 @@
     ASSERT_EQ(mReader->getLightColor(deviceId, /*lightId=*/1), LIGHT_BRIGHTNESS);
 }
 
+TEST_F(InputReaderTest, SetPowerWakeUp) {
+    ASSERT_NO_FATAL_FAILURE(addDevice(1, "1st", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, "2nd", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(3, "3rd", InputDeviceClass::KEYBOARD, nullptr));
+
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(1), false);
+
+    ASSERT_TRUE(mFakeEventHub->setKernelWakeEnabled(2, true));
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(2), true);
+
+    ASSERT_TRUE(mFakeEventHub->setKernelWakeEnabled(3, false));
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(3), false);
+}
+
 // --- InputReaderIntegrationTest ---
 
 // These tests create and interact with the InputReader only through its interface.
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index a43e4e4..25e2b45 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -180,6 +180,7 @@
     MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
     MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override));
     MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
+    MOCK_METHOD(bool, setKernelWakeEnabled, (int32_t deviceId, bool enabled), (override));
 };
 
 class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolicyInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 88c25d3..bcc6062 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -20,16 +20,19 @@
 
 #include "InputMapperTest.h"
 #include "InterfaceMocks.h"
+#include "TestEventMatchers.h"
 
 #define TAG "KeyboardInputMapper_test"
 
 namespace android {
 
 using testing::_;
+using testing::AllOf;
 using testing::Args;
 using testing::DoAll;
 using testing::Return;
 using testing::SetArgPointee;
+using testing::VariantWith;
 
 /**
  * Unit tests for KeyboardInputMapper.
@@ -86,4 +89,24 @@
     }
 }
 
+TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) {
+    std::list<NotifyArgs> args;
+    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    EXPECT_THAT(args,
+                ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+                                                             WithKeyCode(AKEYCODE_0),
+                                                             WithScanCode(KEY_0))),
+                            VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+                                                             WithKeyCode(AKEYCODE_0),
+                                                             WithScanCode(KEY_0)))));
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 3f14c23..ca0f1e8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -16,10 +16,14 @@
 
 #include "../dispatcher/LatencyTracker.h"
 #include "../InputDeviceMetricsSource.h"
+#include "NotifyArgsBuilders.h"
+#include "android/input.h"
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
+#include <input/PrintTools.h>
 #include <inttypes.h>
 #include <linux/input.h>
 #include <log/log.h>
@@ -48,11 +52,44 @@
 }
 
 void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
-    InputDeviceInfo deviceInfo = generateTestDeviceInfo(
-            /*vendorId=*/0, /*productId=*/0, DEVICE_ID);
+    InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID);
     tracker.setInputDevices({deviceInfo});
 }
 
+const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+
+/**
+ * This is a convenience method for comparing timelines that also prints the difference between
+ * the two structures. This helps debugging when the timelines don't match.
+ * @param received the timeline that was actually received
+ * @param expected the timeline that we expected to receive
+ * @return true if the two timelines match, false otherwise.
+ */
+bool timelinesAreEqual(const InputEventTimeline& received, const InputEventTimeline& expected) {
+    LOG_IF(ERROR, expected.eventTime != received.eventTime)
+            << "Received timeline with eventTime=" << received.eventTime
+            << " instead of expected eventTime=" << expected.eventTime;
+    LOG_IF(ERROR, expected.readTime != received.readTime)
+            << "Received timeline with readTime=" << received.readTime
+            << " instead of expected readTime=" << expected.readTime;
+    LOG_IF(ERROR, expected.vendorId != received.vendorId)
+            << "Received timeline with vendorId=" << received.vendorId
+            << " instead of expected vendorId=" << expected.vendorId;
+    LOG_IF(ERROR, expected.productId != received.productId)
+            << "Received timeline with productId=" << received.productId
+            << " instead of expected productId=" << expected.productId;
+    LOG_IF(ERROR, expected.sources != received.sources)
+            << "Received timeline with sources=" << dumpSet(received.sources, ftl::enum_string)
+            << " instead of expected sources=" << dumpSet(expected.sources, ftl::enum_string);
+    LOG_IF(ERROR, expected.inputEventActionType != received.inputEventActionType)
+            << "Received timeline with inputEventActionType="
+            << ftl::enum_string(received.inputEventActionType)
+            << " instead of expected inputEventActionType="
+            << ftl::enum_string(expected.inputEventActionType);
+
+    return received == expected;
+}
+
 } // namespace
 
 const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
@@ -64,15 +101,14 @@
             /*eventTime=*/2,
             /*readTime=*/3,
             /*vendorId=*/0,
-            /*productId=*/0,
-            /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+            /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
             /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
     ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
-    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline{};
     graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
     graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 10;
-    expectedCT.setGraphicsTimeline(std::move(graphicsTimeline));
-    t.connectionTimelines.emplace(sp<BBinder>::make(), std::move(expectedCT));
+    expectedCT.setGraphicsTimeline(graphicsTimeline);
+    t.connectionTimelines.emplace(sp<BBinder>::make(), expectedCT);
     return t;
 }
 
@@ -118,16 +154,19 @@
 void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
     const nsecs_t triggerEventTime =
             lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
-    mTracker->trackListener(/*inputEventId=*/1, triggerEventTime,
-                            /*readTime=*/3, DEVICE_ID,
-                            /*sources=*/{InputDeviceUsageSource::UNKNOWN},
-                            AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+    mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+                                              AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/1)
+                                    .eventTime(triggerEventTime)
+                                    .readTime(3)
+                                    .deviceId(DEVICE_ID)
+                                    .pointer(FIRST_TOUCH_POINTER)
+                                    .build());
 }
 
-void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
+void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& expectedTimeline) {
     ASSERT_FALSE(mReceivedTimelines.empty());
-    const InputEventTimeline& t = mReceivedTimelines.front();
-    ASSERT_EQ(timeline, t);
+    const InputEventTimeline& received = mReceivedTimelines.front();
+    ASSERT_TRUE(timelinesAreEqual(received, expectedTimeline));
     mReceivedTimelines.pop_front();
 }
 
@@ -148,6 +187,11 @@
                 break;
             }
         }
+        if (!found) {
+            for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) {
+                LOG(ERROR) << "Received timeline with eventTime=" << receivedTimeline.eventTime;
+            }
+        }
         ASSERT_TRUE(found) << "Could not find expected timeline with eventTime="
                            << expectedTimeline.eventTime;
     }
@@ -170,14 +214,20 @@
  * any additional ConnectionTimeline's.
  */
 TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
-    mTracker->trackListener(/*inputEventId=*/1, /*eventTime=*/2,
-                            /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN},
-                            AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+    mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+                                              AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/1)
+                                    .eventTime(2)
+                                    .readTime(3)
+                                    .deviceId(DEVICE_ID)
+                                    .pointer(FIRST_TOUCH_POINTER)
+                                    .build());
     triggerEventReporting(/*eventTime=*/2);
     assertReceivedTimeline(
             InputEventTimeline{/*eventTime=*/2,
-                               /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
-                               /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+                               /*readTime=*/3,
+                               /*vendorId=*/0,
+                               /*productID=*/0,
+                               {InputDeviceUsageSource::TOUCHSCREEN},
                                /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT});
 }
 
@@ -209,9 +259,13 @@
 
     const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
 
-    mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+                    .eventTime(expected.eventTime)
+                    .readTime(expected.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
     mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -230,12 +284,20 @@
 
     // In the following 2 calls to trackListener, the inputEventId's are the same, but event times
     // are different.
-    mTracker->trackListener(inputEventId, /*eventTime=*/1, readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
-    mTracker->trackListener(inputEventId, /*eventTime=*/2, readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+                    .eventTime(1)
+                    .readTime(readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+                    .eventTime(2)
+                    .readTime(readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
 
     triggerEventReporting(/*eventTime=*/2);
     // Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -249,8 +311,7 @@
             /*eventTime*/ 2,
             /*readTime*/ 3,
             /*vendorId=*/0,
-            /*productId=*/0,
-            /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+            /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
             /*inputEventType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
     timeline1.connectionTimelines.emplace(connection1,
                                           ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
@@ -266,8 +327,7 @@
             /*eventTime=*/20,
             /*readTime=*/30,
             /*vendorId=*/0,
-            /*productId=*/0,
-            /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+            /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
             /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
     timeline2.connectionTimelines.emplace(connection2,
                                           ConnectionTimeline(/*deliveryTime=*/60,
@@ -280,13 +340,21 @@
     connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2));
 
     // Start processing first event
-    mTracker->trackListener(inputEventId1, timeline1.eventTime, timeline1.readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId1)
+                    .eventTime(timeline1.eventTime)
+                    .readTime(timeline1.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
     // Start processing second event
-    mTracker->trackListener(inputEventId2, timeline2.eventTime, timeline2.readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId2)
+                    .eventTime(timeline2.eventTime)
+                    .readTime(timeline2.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
     mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
                                  connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
 
@@ -311,10 +379,13 @@
     const sp<IBinder>& token = timeline.connectionTimelines.begin()->first;
 
     for (size_t i = 1; i <= 100; i++) {
-        mTracker->trackListener(/*inputEventId=*/i, timeline.eventTime, timeline.readTime,
-                                /*deviceId=*/DEVICE_ID,
-                                /*sources=*/{InputDeviceUsageSource::UNKNOWN},
-                                AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+        mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+                                                  AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/i)
+                                        .eventTime(timeline.eventTime)
+                                        .readTime(timeline.readTime)
+                                        .deviceId(DEVICE_ID)
+                                        .pointer(FIRST_TOUCH_POINTER)
+                                        .build());
         expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime,
                                                        timeline.vendorId, timeline.productId,
                                                        timeline.sources,
@@ -344,9 +415,13 @@
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
 
-    mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+                    .eventTime(expected.eventTime)
+                    .readTime(expected.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
     triggerEventReporting(expected.eventTime);
     assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime,
                                               expected.vendorId, expected.productId,
@@ -362,20 +437,25 @@
     constexpr int32_t inputEventId = 1;
     InputEventTimeline timeline(
             /*eventTime*/ 2, /*readTime*/ 3,
-            /*vendorId=*/50, /*productId=*/60,
-            /*sources=*/
-            {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT},
+            /*vendorId=*/50, /*productId=*/60, {InputDeviceUsageSource::STYLUS_DIRECT},
             /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
     InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
             /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
     InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
             /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
+    deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN);
+    deviceInfo2.addSource(AINPUT_SOURCE_STYLUS);
 
     mTracker->setInputDevices({deviceInfo1, deviceInfo2});
-    mTracker->trackListener(inputEventId, timeline.eventTime, timeline.readTime, DEVICE_ID,
-                            {InputDeviceUsageSource::TOUCHSCREEN,
-                             InputDeviceUsageSource::STYLUS_DIRECT},
-                            AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+                              AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId)
+
+                    .eventTime(timeline.eventTime)
+                    .readTime(timeline.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200))
+                    .build());
     triggerEventReporting(timeline.eventTime);
     assertReceivedTimeline(timeline);
 }
@@ -388,58 +468,74 @@
     // Create timelines for different event types (Motion, Key)
     InputEventTimeline motionDownTimeline(
             /*eventTime*/ 2, /*readTime*/ 3,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+            InputEventActionType::MOTION_ACTION_DOWN);
 
     InputEventTimeline motionMoveTimeline(
             /*eventTime*/ 4, /*readTime*/ 5,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+            InputEventActionType::MOTION_ACTION_MOVE);
 
     InputEventTimeline motionUpTimeline(
             /*eventTime*/ 6, /*readTime*/ 7,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+            InputEventActionType::MOTION_ACTION_UP);
 
     InputEventTimeline keyDownTimeline(
             /*eventTime*/ 8, /*readTime*/ 9,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::KEY);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS},
+            InputEventActionType::KEY);
 
     InputEventTimeline keyUpTimeline(
             /*eventTime*/ 10, /*readTime*/ 11,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::KEY);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS},
+            InputEventActionType::KEY);
 
     InputEventTimeline unknownTimeline(
             /*eventTime*/ 12, /*readTime*/ 13,
-            /*vendorId*/ 0, /*productId*/ 0,
-            /*sources*/ {InputDeviceUsageSource::UNKNOWN},
-            /*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT);
+            /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+            InputEventActionType::UNKNOWN_INPUT_EVENT);
 
-    mTracker->trackListener(inputEventId, motionDownTimeline.eventTime, motionDownTimeline.readTime,
-                            DEVICE_ID, motionDownTimeline.sources, AMOTION_EVENT_ACTION_DOWN,
-                            InputEventType::MOTION);
-    mTracker->trackListener(inputEventId + 1, motionMoveTimeline.eventTime,
-                            motionMoveTimeline.readTime, DEVICE_ID, motionMoveTimeline.sources,
-                            AMOTION_EVENT_ACTION_MOVE, InputEventType::MOTION);
-    mTracker->trackListener(inputEventId + 2, motionUpTimeline.eventTime, motionUpTimeline.readTime,
-                            DEVICE_ID, motionUpTimeline.sources, AMOTION_EVENT_ACTION_UP,
-                            InputEventType::MOTION);
-    mTracker->trackListener(inputEventId + 3, keyDownTimeline.eventTime, keyDownTimeline.readTime,
-                            DEVICE_ID, keyDownTimeline.sources, AKEY_EVENT_ACTION_DOWN,
-                            InputEventType::KEY);
-    mTracker->trackListener(inputEventId + 4, keyUpTimeline.eventTime, keyUpTimeline.readTime,
-                            DEVICE_ID, keyUpTimeline.sources, AKEY_EVENT_ACTION_UP,
-                            InputEventType::KEY);
-    mTracker->trackListener(inputEventId + 5, unknownTimeline.eventTime, unknownTimeline.readTime,
-                            DEVICE_ID, unknownTimeline.sources, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                            InputEventType::MOTION);
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+                    .eventTime(motionDownTimeline.eventTime)
+                    .readTime(motionDownTimeline.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
+    mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                              inputEventId + 1)
+                                    .eventTime(motionMoveTimeline.eventTime)
+                                    .readTime(motionMoveTimeline.readTime)
+                                    .deviceId(DEVICE_ID)
+                                    .pointer(FIRST_TOUCH_POINTER)
+                                    .build());
+    mTracker->trackListener(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 2)
+                    .eventTime(motionUpTimeline.eventTime)
+                    .readTime(motionUpTimeline.readTime)
+                    .deviceId(DEVICE_ID)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .build());
+    mTracker->trackListener(
+            KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD, inputEventId + 3)
+                    .eventTime(keyDownTimeline.eventTime)
+                    .readTime(keyDownTimeline.readTime)
+                    .deviceId(DEVICE_ID)
+                    .build());
+    mTracker->trackListener(
+            KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD, inputEventId + 4)
+                    .eventTime(keyUpTimeline.eventTime)
+                    .readTime(keyUpTimeline.readTime)
+                    .deviceId(DEVICE_ID)
+                    .build());
+    mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN,
+                                              AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 5)
+                                    .eventTime(unknownTimeline.eventTime)
+                                    .readTime(unknownTimeline.readTime)
+                                    .deviceId(DEVICE_ID)
+                                    .pointer(FIRST_TOUCH_POINTER)
+                                    .build());
 
     triggerEventReporting(unknownTimeline.eventTime);
 
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 6fa3365..f58d8fd 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -540,6 +540,34 @@
     return WithKeyCodeMatcher(keyCode);
 }
 
+/// Scan code
+class WithScanCodeMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithScanCodeMatcher(int32_t scanCode) : mScanCode(scanCode) {}
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mScanCode == args.scanCode;
+    }
+
+    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+        return mScanCode == event.getKeyCode();
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with scan code " << KeyEvent::getLabel(mScanCode);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong scan code"; }
+
+private:
+    const int32_t mScanCode;
+};
+
+inline WithScanCodeMatcher WithScanCode(int32_t scanCode) {
+    return WithScanCodeMatcher(scanCode);
+}
+
 /// EventId
 class WithEventIdMatcher {
 public:
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 5442a65..64f3c27 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -171,6 +171,10 @@
 
     void notifyMouseCursorFadedOnTyping() override { reader->notifyMouseCursorFadedOnTyping(); }
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
+        return reader->setKernelWakeEnabled(deviceId, enabled);
+    }
+
 private:
     std::unique_ptr<InputReaderInterface> reader;
 };
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 908fa40..157a333 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -19,6 +19,7 @@
 
 #include "../../InputDeviceMetricsSource.h"
 #include "../InputEventTimeline.h"
+#include "NotifyArgsBuilders.h"
 #include "dispatcher/LatencyTracker.h"
 
 namespace android {
@@ -61,40 +62,49 @@
 
     // Make some pre-defined tokens to ensure that some timelines are complete.
     std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
-    for (size_t i = 0; i < predefinedTokens.size(); i++) {
-        predefinedTokens[i] = sp<BBinder>::make();
+    for (sp<IBinder>& token : predefinedTokens) {
+        token = sp<BBinder>::make();
     }
 
     // Randomly invoke LatencyTracker api's until randomness is exhausted.
     while (fdp.remaining_bytes() > 0) {
         fdp.PickValueInArray<std::function<void()>>({
                 [&]() -> void {
-                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
-                    nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
-                    nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
                     const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
+                    const int32_t source = fdp.ConsumeIntegral<int32_t>();
                     std::set<InputDeviceUsageSource> sources = {
                             fdp.ConsumeEnum<InputDeviceUsageSource>()};
                     const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>();
                     const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>();
-                    tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources,
-                                          inputEventActionType, inputEventType);
+                    const NotifyMotionArgs args =
+                            MotionArgsBuilder(inputEventActionType, source, inputEventId)
+                                    .eventTime(eventTime)
+                                    .readTime(readTime)
+                                    .deviceId(deviceId)
+                                    .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
+                                                     .x(100)
+                                                     .y(200))
+                                    .build();
+                    tracker.trackListener(args);
                 },
                 [&]() -> void {
-                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
                     sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
-                    nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
-                    nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
-                    nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
                     tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime,
                                                consumeTime, finishTime);
                 },
                 [&]() -> void {
-                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
                     sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
-                    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
-                    for (size_t i = 0; i < graphicsTimeline.size(); i++) {
-                        graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>();
+                    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline{};
+                    for (nsecs_t& t : graphicsTimeline) {
+                        t = fdp.ConsumeIntegral<nsecs_t>();
                     }
                     tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline);
                 },
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index fa8270a..60c676d 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -269,6 +269,9 @@
     status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
     status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
     void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
+        return mFdp->ConsumeBool();
+    }
 };
 
 class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index 6b99627..f698515 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -7,6 +7,11 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+vintf_fragment {
+    name: "android.frameworks.stats-service.xml",
+    src: "android.frameworks.stats-service.xml",
+}
+
 cc_library_shared {
     name: "libstatshidl",
     srcs: [
@@ -38,7 +43,7 @@
     local_include_dirs: [
         "include/stats",
     ],
-    vintf_fragments: [
+    vintf_fragment_modules: [
         "android.frameworks.stats-service.xml",
     ],
 }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 7babd17..d500ae8 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -82,6 +82,7 @@
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog_surfaceflinger",
         "libsync",
         "libui",
         "libutils",
@@ -313,3 +314,37 @@
         "libSurfaceFlingerProperties",
     ],
 }
+
+cc_library {
+    name: "libstatslog_surfaceflinger",
+    generated_sources: ["statslog_surfaceflinger.cpp"],
+    generated_headers: ["statslog_surfaceflinger.h"],
+    export_generated_headers: ["statslog_surfaceflinger.h"],
+    shared_libs: [
+        "libbinder",
+        "libstatsbootstrap",
+        "libutils",
+        "android.os.statsbootstrap_aidl-cpp",
+    ],
+}
+
+genrule {
+    name: "statslog_surfaceflinger.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_surfaceflinger.h" +
+        " --module surfaceflinger --namespace android,surfaceflinger,stats --bootstrap",
+    out: [
+        "statslog_surfaceflinger.h",
+    ],
+}
+
+genrule {
+    name: "statslog_surfaceflinger.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_surfaceflinger.cpp" +
+        " --module surfaceflinger --namespace android,surfaceflinger,stats" +
+        " --importHeader statslog_surfaceflinger.h --bootstrap",
+    out: [
+        "statslog_surfaceflinger.cpp",
+    ],
+}
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 5c5d0cd..cfcce47 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -198,25 +198,23 @@
 // these buffers and fire a NO_FENCE to release it. This ensures that all
 // promises for buffer releases are fulfilled at the end of composition.
 void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
-    if (FlagManager::getInstance().ce_fence_promise()) {
-        SFTRACE_CALL();
-        ALOGV(__FUNCTION__);
+    SFTRACE_CALL();
+    ALOGV(__FUNCTION__);
 
-        for (auto& layerFE : args.layers) {
-            if (layerFE->getReleaseFencePromiseStatus() ==
-                LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
-                layerFE->setReleaseFence(Fence::NO_FENCE);
-            }
+    for (auto& layerFE : args.layers) {
+        if (layerFE->getReleaseFencePromiseStatus() ==
+            LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+            layerFE->setReleaseFence(Fence::NO_FENCE);
         }
+    }
 
-        // List of layersWithQueuedFrames does not necessarily overlap with
-        // list of layers, so those layersWithQueuedFrames also need any
-        // unfulfilled promises to be resolved for completeness.
-        for (auto& layerFE : args.layersWithQueuedFrames) {
-            if (layerFE->getReleaseFencePromiseStatus() ==
-                LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
-                layerFE->setReleaseFence(Fence::NO_FENCE);
-            }
+    // List of layersWithQueuedFrames does not necessarily overlap with
+    // list of layers, so those layersWithQueuedFrames also need any
+    // unfulfilled promises to be resolved for completeness.
+    for (auto& layerFE : args.layersWithQueuedFrames) {
+        if (layerFE->getReleaseFencePromiseStatus() ==
+            LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+            layerFE->setReleaseFence(Fence::NO_FENCE);
         }
     }
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 2d8f98f..22ab3d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1610,13 +1610,7 @@
             releaseFence =
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            layer->getLayerFE().setReleaseFence(releaseFence);
-        } else {
-            layer->getLayerFE()
-                    .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
-                                      outputState.layerFilter.layerStack);
-        }
+        layer->getLayerFE().setReleaseFence(releaseFence);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1624,12 +1618,7 @@
     // supply them with the present fence.
     for (auto& weakLayer : mReleasedLayers) {
         if (const auto layer = weakLayer.promote()) {
-            if (FlagManager::getInstance().ce_fence_promise()) {
-                layer->setReleaseFence(frame.presentFence);
-            } else {
-                layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
-                                        outputState.layerFilter.layerStack);
-            }
+            layer->setReleaseFence(frame.presentFence);
         }
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 639164d..48ebc32 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -30,8 +30,6 @@
 #include "TimeStats/TimeStats.h"
 #include "gmock/gmock.h"
 
-#include <variant>
-
 using namespace com::android::graphics::surfaceflinger;
 
 namespace android::compositionengine {
@@ -494,9 +492,6 @@
 };
 
 TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus)
             .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
     EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus)
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c34168d..1c18cd2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -34,7 +34,6 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <cmath>
 #include <cstdint>
 #include <variant>
 
@@ -3263,57 +3262,9 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-    // Simulate getting release fences from each layer, and ensure they are passed to the
-    // front-end layer interface for each layer correctly.
-
-    mOutput.mState.isEnabled = true;
-
-    // Create three unique fence instances
-    sp<Fence> layer1Fence = sp<Fence>::make();
-    sp<Fence> layer2Fence = sp<Fence>::make();
-    sp<Fence> layer3Fence = sp<Fence>::make();
-
-    Output::FrameFences frameFences;
-    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
-    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
-    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Compare the pointers values of each fence to make sure the correct ones
-    // are passed. This happens to work with the current implementation, but
-    // would not survive certain calls like Fence::merge() which would return a
-    // new instance.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer1Fence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer2Fence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
-            });
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
 TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
     // Simulate getting release fences from each layer, and ensure they are passed to the
     // front-end layer interface for each layer correctly.
-
     mOutput.mState.isEnabled = true;
 
     // Create three unique fence instances
@@ -3350,37 +3301,7 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
-    mOutput.mState.isEnabled = true;
-    mOutput.mState.usesClientComposition = true;
-
-    Output::FrameFences frameFences;
-    frameFences.clientTargetAcquireFence = sp<Fence>::make();
-    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make());
-    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
-    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Fence::merge is called, and since none of the fences are actually valid,
-    // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
-    // This is the best we can do without creating a real kernel fence object.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed).WillOnce(Return());
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
 TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     mOutput.mState.isEnabled = true;
     mOutput.mState.usesClientComposition = true;
 
@@ -3403,62 +3324,7 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
-    mOutput.mState.isEnabled = true;
-    mOutput.mState.usesClientComposition = true;
-
-    // This should happen even if there are no (current) output layers.
-    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
-
-    // Load up the released layers with some mock instances
-    sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make();
-    sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make();
-    sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make();
-    Output::ReleasedLayers layers;
-    layers.push_back(releasedLayer1);
-    layers.push_back(releasedLayer2);
-    layers.push_back(releasedLayer3);
-    mOutput.setReleasedLayers(std::move(layers));
-
-    // Set up a fake present fence
-    sp<Fence> presentFence = sp<Fence>::make();
-    Output::FrameFences frameFences;
-    frameFences.presentFence = presentFence;
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Each released layer should be given the presentFence.
-    EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-
-    // After the call the list of released layers should have been cleared.
-    EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
-}
-
 TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     mOutput.mState.isEnabled = true;
     mOutput.mState.usesClientComposition = true;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index e37c0ba..7d77634 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -379,7 +379,7 @@
         const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
         const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
         const DisplayConfiguration::Dpi hwcDpi =
-                DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f,
+                DisplayConfiguration::Dpi{dpiX == -1 ? dpiX : dpiX / 1000.f,
                                           dpiY == -1 ? dpiY : dpiY / 1000.f};
         const DisplayConfiguration::Dpi estimatedDPI =
                 getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c14769e..c88092b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -185,6 +185,14 @@
 
     mFlinger->onLayerDestroyed(this);
 
+    const auto currentTime = std::chrono::steady_clock::now();
+    if (mBufferInfo.mTimeSinceDataspaceUpdate > std::chrono::steady_clock::time_point::min()) {
+        mFlinger->mLayerEvents.emplace_back(mOwnerUid, getSequence(), mBufferInfo.mDataspace,
+                                            std::chrono::duration_cast<std::chrono::milliseconds>(
+                                                    currentTime -
+                                                    mBufferInfo.mTimeSinceDataspaceUpdate));
+    }
+
     if (mDrawingState.sidebandStream != nullptr) {
         mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
     }
@@ -751,54 +759,6 @@
     }
 }
 
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
-                             ui::LayerStack layerStack,
-                             std::function<FenceResult(FenceResult)>&& continuation) {
-    sp<CallbackHandle> ch = findCallbackHandle();
-
-    if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
-        futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
-    }
-
-    if (ch != nullptr) {
-        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-        ch->previousSharedReleaseFences.emplace_back(std::move(futureFenceResult));
-        ch->name = mName;
-    } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
-        // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
-        // asynchronously, then make sure we don't drop the fence.
-        mPreviousReleaseFenceAndContinuations.emplace_back(std::move(futureFenceResult),
-                                                           std::move(continuation));
-        std::vector<FenceAndContinuation> mergedFences;
-        sp<Fence> prevFence = nullptr;
-        // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
-        // grow unbounded.
-        for (const auto& futureAndContinuation : mPreviousReleaseFenceAndContinuations) {
-            auto result = futureAndContinuation.future.wait_for(0s);
-            if (result != std::future_status::ready) {
-                mergedFences.emplace_back(futureAndContinuation);
-                continue;
-            }
-
-            mergeFence(getDebugName(),
-                       futureAndContinuation.chain().get().value_or(Fence::NO_FENCE), prevFence);
-        }
-        if (prevFence != nullptr) {
-            mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
-        }
-
-        mPreviousReleaseFenceAndContinuations.swap(mergedFences);
-    }
-
-    if (mBufferInfo.mBuffer) {
-        mPreviouslyPresentedLayerStacks.push_back(layerStack);
-    }
-
-    if (mDrawingState.frameNumber > 0) {
-        mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
-    }
-}
-
 void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     for (const auto& handle : mDrawingState.callbackHandles) {
         handle->bufferReleaseChannel = mBufferReleaseChannel;
@@ -1111,22 +1071,13 @@
             handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
             handle->previousFrameNumber = mDrawingState.previousFrameNumber;
-            if (FlagManager::getInstance().ce_fence_promise() &&
-                mPreviousReleaseBufferEndpoint == handle->listener) {
+            if (mPreviousReleaseBufferEndpoint == handle->listener) {
                 // Add fence from previous screenshot now so that it can be dispatched to the
                 // client.
                 for (auto& [_, future] : mAdditionalPreviousReleaseFences) {
                     handle->previousReleaseFences.emplace_back(std::move(future));
                 }
                 mAdditionalPreviousReleaseFences.clear();
-            } else if (FlagManager::getInstance().screenshot_fence_preservation() &&
-                       mPreviousReleaseBufferEndpoint == handle->listener) {
-                // Add fences from previous screenshots now so that they can be dispatched to the
-                // client.
-                for (const auto& futureAndContinution : mPreviousReleaseFenceAndContinuations) {
-                    handle->previousSharedReleaseFences.emplace_back(futureAndContinution.chain());
-                }
-                mPreviousReleaseFenceAndContinuations.clear();
             }
             // Store so latched time and release fence can be set
             mDrawingState.callbackHandles.push_back(handle);
@@ -1318,8 +1269,17 @@
             }
         }
     }
-    if (lastDataspace != mBufferInfo.mDataspace) {
+    if (lastDataspace != mBufferInfo.mDataspace ||
+        mBufferInfo.mTimeSinceDataspaceUpdate == std::chrono::steady_clock::time_point::min()) {
         mFlinger->mHdrLayerInfoChanged = true;
+        const auto currentTime = std::chrono::steady_clock::now();
+        if (mBufferInfo.mTimeSinceDataspaceUpdate > std::chrono::steady_clock::time_point::min()) {
+            mFlinger->mLayerEvents
+                    .emplace_back(mOwnerUid, getSequence(), lastDataspace,
+                                  std::chrono::duration_cast<std::chrono::milliseconds>(
+                                          currentTime - mBufferInfo.mTimeSinceDataspaceUpdate));
+        }
+        mBufferInfo.mTimeSinceDataspaceUpdate = currentTime;
     }
     if (mBufferInfo.mDesiredHdrSdrRatio != mDrawingState.desiredHdrSdrRatio) {
         mBufferInfo.mDesiredHdrSdrRatio = mDrawingState.desiredHdrSdrRatio;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ce4b9c4..a2716c6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -242,6 +242,8 @@
         sp<Fence> mFence;
         uint32_t mTransform{0};
         ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        std::chrono::steady_clock::time_point mTimeSinceDataspaceUpdate =
+                std::chrono::steady_clock::time_point::min();
         Rect mCrop;
         PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
         bool mTransformToDisplayInverse{false};
@@ -257,8 +259,6 @@
 
     bool fenceHasSignaled() const;
     void onPreComposition(nsecs_t refreshStartTime);
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
-                          std::function<FenceResult(FenceResult)>&& continuation = nullptr);
 
     // Tracks mLastClientCompositionFence and gets the callback handle for this layer.
     sp<CallbackHandle> findCallbackHandle();
@@ -387,20 +387,6 @@
     // from the layer.
     std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
 
-    struct FenceAndContinuation {
-        ftl::SharedFuture<FenceResult> future;
-        std::function<FenceResult(FenceResult)> continuation;
-
-        ftl::SharedFuture<FenceResult> chain() const {
-            if (continuation) {
-                return ftl::Future(future).then(continuation).share();
-            } else {
-                return future;
-            }
-        }
-    };
-    std::vector<FenceAndContinuation> mPreviousReleaseFenceAndContinuations;
-
     // Release fences for buffers that have not yet received a release
     // callback. A release callback may not be given when capturing
     // screenshots asynchronously. There may be no buffer update for the
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b05f0ee..f64ba9e 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -26,7 +26,6 @@
 
 #include "LayerFE.h"
 #include "SurfaceFlinger.h"
-#include "common/FlagManager.h"
 #include "ui/FenceResult.h"
 #include "ui/LayerStack.h"
 
@@ -84,8 +83,7 @@
     // Ensures that no promise is left unfulfilled before the LayerFE is destroyed.
     // An unfulfilled promise could occur when a screenshot is attempted, but the
     // render area is invalid and there is no memory for the capture result.
-    if (FlagManager::getInstance().ce_fence_promise() &&
-        mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+    if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
         setReleaseFence(Fence::NO_FENCE);
     }
 }
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 06c2f26..011fd9e 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -355,7 +355,7 @@
 
     FenceResult fenceResult;
     if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) {
+        mFlinger.mRenderEngine->isThreaded()) {
         std::vector<sp<LayerFE>> layerFEs;
         auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
                                                                 getLayerSnapshotsFn, layerFEs);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index ab9014e..eca8df2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -722,13 +722,17 @@
             const bool inPrimaryPhysicalRange =
                     policy->primaryRanges.physical.includes(modePtr->getPeakFps());
             const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
-            if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+            if (!mIsVrrDevice.load() &&
+                ((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
                  !inPrimaryRenderRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
                    layer.vote == LayerVoteType::ExplicitExact))) {
                 // Only focused layers with ExplicitDefault frame rate settings are allowed to score
                 // refresh rates outside the primary range.
+                ALOGV("%s ignores %s (primaryRangeIsSingleRate). Current mode = %s",
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -852,7 +856,8 @@
                                    to_string(descending.front().frameRateMode.fps).c_str());
             return {descending, kNoSignals};
         } else {
-            ALOGV("primaryRangeIsSingleRate");
+            ALOGV("%s (primaryRangeIsSingleRate)",
+                  to_string(ranking.front().frameRateMode.fps).c_str());
             SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
                                    to_string(ranking.front().frameRateMode.fps).c_str());
             return {ranking, kNoSignals};
@@ -932,6 +937,8 @@
             using LayerVoteType = RefreshRateSelector::LayerVoteType;
 
             if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
+                ALOGV("%s: %s skips uid=%d due to the vote", __func__,
+                      formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid);
                 skipUid = true;
                 break;
             }
@@ -1014,12 +1021,14 @@
 
         // Layers with ExplicitExactOrMultiple expect touch boost
         if (globalSignals.touch && hasExplicitExactOrMultiple) {
+            ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid);
             continue;
         }
 
         // Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and
         // skip frame rate override.
         if (hasHighHint && !hasExplicitDefault) {
+            ALOGV("%s: Skipping for touch (HighHint): uid=%d", __func__, uid);
             continue;
         }
 
@@ -1043,6 +1052,9 @@
                 constexpr bool isSeamlessSwitch = true;
                 const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
                 score += layer->weight * layerScore;
+                ALOGV("%s: %s gives %s fps score of %.4f", __func__,
+                      formatLayerInfo(*layer, layer->weight).c_str(), to_string(fps).c_str(),
+                      layerScore);
             }
         }
 
@@ -1297,6 +1309,8 @@
     LOG_ALWAYS_FATAL_IF(!activeModeOpt);
     mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(),
                                    ftl::as_non_null(activeModeOpt->get())};
+    mIsVrrDevice = FlagManager::getInstance().vrr_config() &&
+            activeModeOpt->get()->getVrrConfig().has_value();
 
     const auto sortedModes = sortByRefreshRate(mDisplayModes);
     mMinRefreshRateModeIt = sortedModes.front();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cad179c..09dbc59 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -83,6 +83,7 @@
 #include <renderengine/RenderEngine.h>
 #include <renderengine/impl/ExternalTexture.h>
 #include <scheduler/FrameTargeter.h>
+#include <statslog_surfaceflinger.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
@@ -2758,16 +2759,6 @@
             compositionengine::Feature::kSnapshotLayerMetadata);
 
     refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
-
-    if (!FlagManager::getInstance().ce_fence_promise()) {
-        refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (auto& [layer, _] : mLayersWithQueuedFrames) {
-            if (const auto& layerFE = layer->getCompositionEngineLayerFE(
-                        {static_cast<uint32_t>(layer->sequence)}))
-                refreshArgs.layersWithQueuedFrames.push_back(layerFE);
-        }
-    }
-
     refreshArgs.outputColorSetting = mDisplayColorSetting;
     refreshArgs.forceOutputColorMode = mForceColorMode;
 
@@ -2831,51 +2822,35 @@
         layer->onPreComposition(refreshArgs.refreshStartTime);
     }
 
-    if (FlagManager::getInstance().ce_fence_promise()) {
-        for (auto& [layer, layerFE] : layers) {
-            attachReleaseFenceFutureToLayer(layer, layerFE,
-                                            layerFE->mSnapshot->outputFilter.layerStack);
-        }
+    for (auto& [layer, layerFE] : layers) {
+        attachReleaseFenceFutureToLayer(layer, layerFE,
+                                        layerFE->mSnapshot->outputFilter.layerStack);
+    }
 
-        refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (auto& [layer, _] : mLayersWithQueuedFrames) {
-            if (const auto& layerFE = layer->getCompositionEngineLayerFE(
-                        {static_cast<uint32_t>(layer->sequence)})) {
-                refreshArgs.layersWithQueuedFrames.push_back(layerFE);
-                // Some layers are not displayed and do not yet have a future release fence
-                if (layerFE->getReleaseFencePromiseStatus() ==
-                            LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
-                    layerFE->getReleaseFencePromiseStatus() ==
-                            LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
-                    // layerStack is invalid because layer is not on a display
-                    attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
-                                                    ui::INVALID_LAYER_STACK);
-                }
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (auto& [layer, _] : mLayersWithQueuedFrames) {
+        if (const auto& layerFE =
+                    layer->getCompositionEngineLayerFE({static_cast<uint32_t>(layer->sequence)})) {
+            refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+            // Some layers are not displayed and do not yet have a future release fence
+            if (layerFE->getReleaseFencePromiseStatus() ==
+                        LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
+                layerFE->getReleaseFencePromiseStatus() ==
+                        LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
+                // layerStack is invalid because layer is not on a display
+                attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
+                                                ui::INVALID_LAYER_STACK);
             }
         }
+    }
 
-        mCompositionEngine->present(refreshArgs);
-        moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+    mCompositionEngine->present(refreshArgs);
+    moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 
-        for (auto& [layer, layerFE] : layers) {
-            CompositionResult compositionResult{layerFE->stealCompositionResult()};
-            if (compositionResult.lastClientCompositionFence) {
-                layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
-            }
-        }
-
-    } else {
-        mCompositionEngine->present(refreshArgs);
-        moveSnapshotsFromCompositionArgs(refreshArgs, layers);
-
-        for (auto [layer, layerFE] : layers) {
-            CompositionResult compositionResult{layerFE->stealCompositionResult()};
-            for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
-                layer->onLayerDisplayed(std::move(releaseFence), layerStack);
-            }
-            if (compositionResult.lastClientCompositionFence) {
-                layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
-            }
+    for (auto& [layer, layerFE] : layers) {
+        CompositionResult compositionResult{layerFE->stealCompositionResult()};
+        if (compositionResult.lastClientCompositionFence) {
+            layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
         }
     }
 
@@ -3130,13 +3105,8 @@
             auto optDisplay = layerStackToDisplay.get(layerStack);
             if (optDisplay && !optDisplay->get()->isVirtual()) {
                 auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
-                                                   ui::INVALID_LAYER_STACK);
-                } else {
-                    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
-                                            ui::INVALID_LAYER_STACK);
-                }
+                layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
+                                               ui::INVALID_LAYER_STACK);
             }
         }
         layer->releasePendingBuffer(presentTime.ns());
@@ -3174,6 +3144,19 @@
         mAddingHDRLayerInfoListener = false;
     }
 
+    for (const auto& layerEvent : mLayerEvents) {
+        auto result =
+                stats::stats_write(stats::SURFACE_CONTROL_EVENT,
+                                   static_cast<int32_t>(layerEvent.uid),
+                                   static_cast<int64_t>(layerEvent.timeSinceLastEvent.count()),
+                                   static_cast<int32_t>(layerEvent.dataspace));
+        if (result < 0) {
+            ALOGW("Failed to report layer event with error: %d", result);
+        }
+    }
+
+    mLayerEvents.clear();
+
     if (haveNewListeners || mHdrLayerInfoChanged) {
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
@@ -7219,8 +7202,7 @@
         return;
     }
 
-    if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+    if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
         std::vector<sp<LayerFE>> layerFEs;
         auto displayState =
                 getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs);
@@ -7461,10 +7443,8 @@
     auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
                                     kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
         auto layers = getLayerSnapshotsFn();
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            for (auto& [layer, layerFE] : layers) {
-                attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
-            }
+        for (auto& [layer, layerFE] : layers) {
+            attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
         }
         auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
 
@@ -7661,36 +7641,13 @@
     // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
     // to CompositionEngine::present.
     ftl::SharedFuture<FenceResult> presentFuture;
-    if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+    if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
         presentFuture = ftl::yield(present()).share();
     } else {
         presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
                                                     : ftl::yield(present()).share();
     }
 
-    if (!FlagManager::getInstance().ce_fence_promise()) {
-        for (auto& [layer, layerFE] : layers) {
-            layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
-                                    [layerFE = std::move(layerFE)](FenceResult) {
-                                        if (FlagManager::getInstance()
-                                                    .screenshot_fence_preservation()) {
-                                            const auto compositionResult =
-                                                    layerFE->stealCompositionResult();
-                                            const auto& fences = compositionResult.releaseFences;
-                                            // CompositionEngine may choose to cull layers that
-                                            // aren't visible, so pass a non-fence.
-                                            return fences.empty() ? Fence::NO_FENCE
-                                                                  : fences.back().first.get();
-                                        } else {
-                                            return layerFE->stealCompositionResult()
-                                                    .releaseFences.back()
-                                                    .first.get();
-                                        }
-                                    });
-        }
-    }
-
     return presentFuture;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index db0e15e..31218ed 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1221,6 +1221,14 @@
 
     bool mHdrLayerInfoChanged = false;
 
+    struct LayerEvent {
+        uid_t uid;
+        int32_t layerId;
+        ui::Dataspace dataspace;
+        std::chrono::milliseconds timeSinceLastEvent;
+    };
+    std::vector<LayerEvent> mLayerEvents;
+
     // Used to ensure we omit a callback when HDR layer info listener is newly added but the
     // scene hasn't changed
     bool mAddingHDRLayerInfoListener = false;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index c6856ae..de4825b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -28,7 +28,6 @@
 #include "Utils/FenceUtils.h"
 
 #include <binder/IInterface.h>
-#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <utils/RefBase.h>
 
@@ -127,14 +126,8 @@
     if (surfaceControl) {
         sp<Fence> prevFence = nullptr;
 
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            for (auto& future : handle->previousReleaseFences) {
-                mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
-            }
-        } else {
-            for (const auto& future : handle->previousSharedReleaseFences) {
-                mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
-            }
+        for (auto& future : handle->previousReleaseFences) {
+            mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
         }
 
         handle->previousReleaseFence = prevFence;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 14a7487..d81d8d0 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -43,7 +43,6 @@
     std::string name;
     sp<Fence> previousReleaseFence;
     std::vector<ftl::Future<FenceResult>> previousReleaseFences;
-    std::vector<ftl::SharedFuture<FenceResult>> previousSharedReleaseFences;
     std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     nsecs_t latchTime = -1;
     std::optional<uint32_t> transformHint = std::nullopt;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index ea108c7..8a81c56 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -133,7 +133,6 @@
     DUMP_READ_ONLY_FLAG(fp16_client_target);
     DUMP_READ_ONLY_FLAG(game_default_frame_rate);
     DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
-    DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
     DUMP_READ_ONLY_FLAG(vulkan_renderengine);
     DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
     DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
@@ -141,7 +140,6 @@
     DUMP_READ_ONLY_FLAG(restore_blur_step);
     DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
     DUMP_READ_ONLY_FLAG(protected_if_client);
-    DUMP_READ_ONLY_FLAG(ce_fence_promise);
     DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
     DUMP_READ_ONLY_FLAG(graphite_renderengine);
     DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
@@ -241,7 +239,6 @@
 FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
 FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
 FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
-FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
 FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
 FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
 FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
@@ -249,7 +246,6 @@
 FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
 FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
 FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
-FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
 FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
 FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
 FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 419b085..b097bf9 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -71,7 +71,6 @@
     bool fp16_client_target() const;
     bool game_default_frame_rate() const;
     bool enable_layer_command_batching() const;
-    bool screenshot_fence_preservation() const;
     bool vulkan_renderengine() const;
     bool vrr_bugfix_24q4() const;
     bool vrr_bugfix_dropped_frame() const;
@@ -79,7 +78,6 @@
     bool restore_blur_step() const;
     bool dont_skip_on_early_ro() const;
     bool protected_if_client() const;
-    bool ce_fence_promise() const;
     bool idle_screen_refresh_rate_timeout() const;
     bool graphite_renderengine() const;
     bool filter_frames_before_trace_starts() const;
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 67a5247..bf5957a 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -17,7 +17,6 @@
 #define ANDROID_TRANSACTION_TEST_HARNESSES
 
 #include <com_android_graphics_libgui_flags.h>
-#include <common/FlagManager.h>
 #include <ui/DisplayState.h>
 
 #include "LayerTransactionTest.h"
@@ -96,12 +95,8 @@
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
                 t.setDisplayProjection(vDisplay, displayState.orientation,
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    t.setDisplayLayerStack(vDisplay, layerStack);
-                    t.setLayerStack(mirrorSc, layerStack);
-                } else {
-                    t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
-                }
+                t.setDisplayLayerStack(vDisplay, layerStack);
+                t.setLayerStack(mirrorSc, layerStack);
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
 
@@ -121,10 +116,8 @@
                 // CompositionEngine::present may attempt to be called on the same
                 // display multiple times. The layerStack is set to invalid here so
                 // that the display is ignored if that scenario occurs.
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
-                    t.apply(true);
-                }
+                t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
+                t.apply(true);
                 SurfaceComposerClient::destroyVirtualDisplay(vDisplay);
                 return sc;
         }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f1bd87c..40a6fb8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -199,6 +199,7 @@
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog_surfaceflinger",
         "libSurfaceFlingerProp",
         "libsync",
         "libui",
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index adbd868..29e1c21 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -308,6 +308,42 @@
                     << " category=" << ftl::enum_string(testCase.frameRateCategory);
         }
     }
+
+    template <class T>
+    std::vector<LayerRequirement> createLayers(const std::initializer_list<T>& surfaceVotes) {
+        std::vector<LayerRequirement> layers;
+        for (auto surfaceVote : surfaceVotes) {
+            ALOGI("**** %s: Adding layers for %s: (desiredFrameRate=%s, voteType=%s), "
+                  "(frameRateCategory=%s)",
+                  __func__, surfaceVote.name.c_str(),
+                  to_string(surfaceVote.desiredFrameRate).c_str(),
+                  ftl::enum_string(surfaceVote.voteType).c_str(),
+                  ftl::enum_string(surfaceVote.frameRateCategory).c_str());
+
+            if (surfaceVote.desiredFrameRate.isValid()) {
+                std::stringstream ss;
+                ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitDefault ("
+                   << to_string(surfaceVote.desiredFrameRate) << ")";
+                LayerRequirement layer = {.name = ss.str(),
+                                          .vote = surfaceVote.voteType,
+                                          .desiredRefreshRate = surfaceVote.desiredFrameRate,
+                                          .weight = surfaceVote.weight};
+                layers.push_back(layer);
+            }
+
+            if (surfaceVote.frameRateCategory != FrameRateCategory::Default) {
+                std::stringstream ss;
+                ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitCategory ("
+                   << ftl::enum_string(surfaceVote.frameRateCategory) << ")";
+                LayerRequirement layer = {.name = ss.str(),
+                                          .vote = LayerVoteType::ExplicitCategory,
+                                          .frameRateCategory = surfaceVote.frameRateCategory,
+                                          .weight = surfaceVote.weight};
+                layers.push_back(layer);
+            }
+        }
+        return layers;
+    }
 };
 
 RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1776,6 +1812,98 @@
             selector);
 }
 
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multiSurface_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    // Switch the policy to be more like an ARR device (primary range is a single rate).
+    constexpr FpsRange k120_120Hz = {120_Hz, 120_Hz};
+    constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+    constexpr FpsRanges kPrimaryRanges = {/*physical*/ k120_120Hz,
+                                          /*render*/ k120_120Hz};
+    constexpr FpsRanges kAppRequestRanges = {/*physical*/ k120_120Hz,
+                                             /*render*/ k0_120Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {/*defaultMode*/ kModeId120, kPrimaryRanges, kAppRequestRanges}));
+
+    // Surface can translate to multiple layers in SF  scheduler due to category and frame rate
+    // value.
+    struct SurfaceVote {
+        // Params
+        std::string name = "";
+        Fps desiredFrameRate = 0_Hz;
+        LayerVoteType voteType = LayerVoteType::ExplicitDefault;
+        FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+        float weight = 1.f;
+    };
+
+    auto layers = createLayers(
+            std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
+                                                .desiredFrameRate = 60_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .weight = 0.27f},
+                                               {.name = "1 fixed source + NoPreference",
+                                                .desiredFrameRate = 1_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .frameRateCategory =
+                                                        FrameRateCategory::NoPreference}});
+    auto actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(
+            std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
+                                                .desiredFrameRate = 60_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .weight = 0.27f},
+                                               {.name = "1 fixed source + Normal",
+                                                .desiredFrameRate = 1_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .frameRateCategory = FrameRateCategory::Normal}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "30 fixed source + NoPreference",
+             .desiredFrameRate = 30_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "1 fixed source + NoPreference",
+             .desiredFrameRate = 1_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    // Result affected by RefreshRateSelector.kMinSupportedFrameRate.
+    EXPECT_EQ(20_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "24 fixed source + NoPreference",
+             .desiredFrameRate = 24_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(24_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "23.976 fixed source + NoPreference",
+             .desiredFrameRate = 23.976_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    // Chooses 120 unless certain threshold is set, see tests test23976Chooses120 and
+    // test23976Chooses60IfThresholdIs120.
+    EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+}
+
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
     auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
 
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index b06ee3b..3ddc4f2 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -29,11 +29,11 @@
 using aidl::android::hardware::vibrator::Braking;
 using aidl::android::hardware::vibrator::CompositeEffect;
 using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::CompositePwleV2;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
 using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
-using aidl::android::hardware::vibrator::PwleV2Primitive;
 using aidl::android::hardware::vibrator::VendorEffect;
 
 using std::chrono::milliseconds;
@@ -131,8 +131,7 @@
     return HalResult<void>::unsupported();
 }
 
-HalResult<void> HalWrapper::composePwleV2(const std::vector<PwleV2Primitive>&,
-                                          const std::function<void()>&) {
+HalResult<void> HalWrapper::composePwleV2(const CompositePwleV2&, const std::function<void()>&) {
     ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL");
     return HalResult<void>::unsupported();
 }
@@ -243,11 +242,11 @@
     return HalResult<milliseconds>::unsupported();
 }
 
-HalResult<std::vector<PwleV2OutputMapEntry>>
+HalResult<std::vector<FrequencyAccelerationMapEntry>>
 HalWrapper::getFrequencyToOutputAccelerationMapInternal() {
     ALOGV("Skipped getFrequencyToOutputAccelerationMapInternal because it's not "
           "available in Vibrator HAL");
-    return HalResult<std::vector<PwleV2OutputMapEntry>>::unsupported();
+    return HalResult<std::vector<FrequencyAccelerationMapEntry>>::unsupported();
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -360,7 +359,7 @@
     return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb));
 }
 
-HalResult<void> AidlHalWrapper::composePwleV2(const std::vector<PwleV2Primitive>& composite,
+HalResult<void> AidlHalWrapper::composePwleV2(const CompositePwleV2& composite,
                                               const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
@@ -498,13 +497,13 @@
     return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
 }
 
-HalResult<std::vector<PwleV2OutputMapEntry>>
+HalResult<std::vector<FrequencyAccelerationMapEntry>>
 AidlHalWrapper::getFrequencyToOutputAccelerationMapInternal() {
-    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
-    auto status =
-            getHal()->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+    std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
+    auto status = getHal()->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
     return HalResultFactory::fromStatus<
-            std::vector<PwleV2OutputMapEntry>>(std::move(status), frequencyToOutputAccelerationMap);
+            std::vector<FrequencyAccelerationMapEntry>>(std::move(status),
+                                                        frequencyToOutputAccelerationMap);
 }
 
 std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index b2bfffc..339a6e1 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -243,7 +243,8 @@
     using EffectStrength = aidl::android::hardware::vibrator::EffectStrength;
     using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive;
     using Braking = aidl::android::hardware::vibrator::Braking;
-    using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+    using FrequencyAccelerationMapEntry =
+            aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
 
     const HalResult<Capabilities> capabilities;
     const HalResult<std::vector<Effect>> supportedEffects;
@@ -262,7 +263,7 @@
     const HalResult<int32_t> maxEnvelopeEffectSize;
     const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration;
     const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration;
-    const HalResult<std::vector<PwleV2OutputMapEntry>> frequencyToOutputAccelerationMap;
+    const HalResult<std::vector<FrequencyAccelerationMapEntry>> frequencyToOutputAccelerationMap;
 
     void logFailures() const {
         logFailure<Capabilities>(capabilities, "getCapabilities");
@@ -286,8 +287,9 @@
                                               "getMinEnvelopeEffectControlPointDuration");
         logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
                                               "getMaxEnvelopeEffectControlPointDuration");
-        logFailure<std::vector<PwleV2OutputMapEntry>>(frequencyToOutputAccelerationMap,
-                                                      "getfrequencyToOutputAccelerationMap");
+        logFailure<
+                std::vector<FrequencyAccelerationMapEntry>>(frequencyToOutputAccelerationMap,
+                                                            "getfrequencyToOutputAccelerationMap");
     }
 
     bool shouldRetry() const {
@@ -365,8 +367,8 @@
             HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
     HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration =
             HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
-    HalResult<std::vector<Info::PwleV2OutputMapEntry>> mFrequencyToOutputAccelerationMap =
-            HalResult<std::vector<Info::PwleV2OutputMapEntry>>::transactionFailed(MSG);
+    HalResult<std::vector<Info::FrequencyAccelerationMapEntry>> mFrequencyToOutputAccelerationMap =
+            HalResult<std::vector<Info::FrequencyAccelerationMapEntry>>::transactionFailed(MSG);
 
     friend class HalWrapper;
 };
@@ -381,8 +383,9 @@
     using CompositeEffect = aidl::android::hardware::vibrator::CompositeEffect;
     using Braking = aidl::android::hardware::vibrator::Braking;
     using PrimitivePwle = aidl::android::hardware::vibrator::PrimitivePwle;
-    using PwleV2Primitive = aidl::android::hardware::vibrator::PwleV2Primitive;
-    using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+    using CompositePwleV2 = aidl::android::hardware::vibrator::CompositePwleV2;
+    using FrequencyAccelerationMapEntry =
+            aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
 
     explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
           : mCallbackScheduler(std::move(scheduler)) {}
@@ -420,7 +423,7 @@
     virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives,
                                               const std::function<void()>& completionCallback);
 
-    virtual HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite,
+    virtual HalResult<void> composePwleV2(const CompositePwleV2& composite,
                                           const std::function<void()>& completionCallback);
 
 protected:
@@ -450,7 +453,7 @@
     virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal();
     virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal();
     virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal();
-    virtual HalResult<std::vector<PwleV2OutputMapEntry>>
+    virtual HalResult<std::vector<FrequencyAccelerationMapEntry>>
     getFrequencyToOutputAccelerationMapInternal();
 
 private:
@@ -508,7 +511,7 @@
             const std::vector<PrimitivePwle>& primitives,
             const std::function<void()>& completionCallback) override final;
 
-    HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite,
+    HalResult<void> composePwleV2(const CompositePwleV2& composite,
                                   const std::function<void()>& completionCallback) override final;
 
 protected:
@@ -532,8 +535,9 @@
             override final;
     HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal()
             override final;
-    HalResult<std::vector<PwleV2OutputMapEntry>> getFrequencyToOutputAccelerationMapInternal()
-            override final;
+
+    HalResult<std::vector<FrequencyAccelerationMapEntry>>
+    getFrequencyToOutputAccelerationMapInternal() override final;
 
 private:
     const reconnect_fn mReconnectFn;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index d42aa56..c58e05c 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -34,12 +34,13 @@
 using aidl::android::hardware::vibrator::Braking;
 using aidl::android::hardware::vibrator::CompositeEffect;
 using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::CompositePwleV2;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
 using aidl::android::hardware::vibrator::IVibrator;
 using aidl::android::hardware::vibrator::IVibratorCallback;
 using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
 using aidl::android::hardware::vibrator::PwleV2Primitive;
 using aidl::android::hardware::vibrator::VendorEffect;
 using aidl::android::os::PersistableBundle;
@@ -243,11 +244,11 @@
     std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
     std::vector<Braking> supportedBraking = {Braking::CLAB};
     std::vector<float> amplitudes = {0.f, 1.f, 0.f};
-    std::vector<PwleV2OutputMapEntry>
-            frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
-                                                                  /*maxOutputAcceleration=*/0.2),
-                                             PwleV2OutputMapEntry(/*frequency=*/60.0f,
-                                                                  /*maxOutputAcceleration=*/0.8)};
+    std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap{
+            FrequencyAccelerationMapEntry(/*frequency=*/30.0f,
+                                          /*maxOutputAcceleration=*/0.2),
+            FrequencyAccelerationMapEntry(/*frequency=*/60.0f,
+                                          /*maxOutputAcceleration=*/0.8)};
 
     std::vector<std::chrono::milliseconds> primitiveDurations;
     constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
@@ -329,7 +330,7 @@
             .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
             .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
                             Return(ndk::ScopedAStatus::ok())));
-    EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+    EXPECT_CALL(*mMockHal.get(), getFrequencyToOutputAccelerationMap(_))
             .Times(Exactly(2))
             .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
             .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
@@ -391,11 +392,11 @@
     constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
     constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
     std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
-    std::vector<PwleV2OutputMapEntry>
-            frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
-                                                                  /*maxOutputAcceleration=*/0.2),
-                                             PwleV2OutputMapEntry(/*frequency=*/60.0f,
-                                                                  /*maxOutputAcceleration=*/0.8)};
+    std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap{
+            FrequencyAccelerationMapEntry(/*frequency=*/30.0f,
+                                          /*maxOutputAcceleration=*/0.2),
+            FrequencyAccelerationMapEntry(/*frequency=*/60.0f,
+                                          /*maxOutputAcceleration=*/0.8)};
 
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
             .Times(Exactly(1))
@@ -451,7 +452,7 @@
             .Times(Exactly(1))
             .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
                             Return(ndk::ScopedAStatus::ok())));
-    EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+    EXPECT_CALL(*mMockHal.get(), getFrequencyToOutputAccelerationMap(_))
             .Times(Exactly(1))
             .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
                             Return(ndk::ScopedAStatus::ok())));
@@ -754,7 +755,8 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestComposePwleV2) {
-    auto pwleEffect = {
+    CompositePwleV2 composite;
+    composite.pwlePrimitives = {
             PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
             PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
             PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
@@ -773,17 +775,17 @@
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
-    auto result = mWrapper->composePwleV2(pwleEffect, callback);
+    auto result = mWrapper->composePwleV2(composite, callback);
     ASSERT_TRUE(result.isUnsupported());
     // Callback not triggered on failure
     ASSERT_EQ(0, *callbackCounter.get());
 
-    result = mWrapper->composePwleV2(pwleEffect, callback);
+    result = mWrapper->composePwleV2(composite, callback);
     ASSERT_TRUE(result.isFailed());
     // Callback not triggered for unsupported
     ASSERT_EQ(0, *callbackCounter.get());
 
-    result = mWrapper->composePwleV2(pwleEffect, callback);
+    result = mWrapper->composePwleV2(composite, callback);
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(1, *callbackCounter.get());
 }
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index d6dab8d..04dbe4e 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -36,6 +36,7 @@
 using aidl::android::hardware::vibrator::Braking;
 using aidl::android::hardware::vibrator::CompositeEffect;
 using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::CompositePwleV2;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
 using aidl::android::hardware::vibrator::IVibrator;
@@ -380,7 +381,8 @@
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) {
-    auto pwleEffect = {
+    CompositePwleV2 composite;
+    composite.pwlePrimitives = {
             PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
             PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
             PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
@@ -389,7 +391,7 @@
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
-    ASSERT_TRUE(mWrapper->composePwleV2(pwleEffect, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->composePwleV2(composite, callback).isUnsupported());
 
     // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
diff --git a/services/vibratorservice/test/test_mocks.h b/services/vibratorservice/test/test_mocks.h
index 5e09084..ba273be 100644
--- a/services/vibratorservice/test/test_mocks.h
+++ b/services/vibratorservice/test/test_mocks.h
@@ -36,13 +36,13 @@
 using aidl::android::hardware::vibrator::Braking;
 using aidl::android::hardware::vibrator::CompositeEffect;
 using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::CompositePwleV2;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
 using aidl::android::hardware::vibrator::IVibrator;
 using aidl::android::hardware::vibrator::IVibratorCallback;
 using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
-using aidl::android::hardware::vibrator::PwleV2Primitive;
 using aidl::android::hardware::vibrator::VendorEffect;
 
 // -------------------------------------------------------------------------------------------------
@@ -91,16 +91,15 @@
     MOCK_METHOD(ndk::ScopedAStatus, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getPwleCompositionSizeMax, (int32_t * ret), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getSupportedBraking, (std::vector<Braking> * ret), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getPwleV2FrequencyToOutputAccelerationMap,
-                (std::vector<PwleV2OutputMapEntry> * ret), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getFrequencyToOutputAccelerationMap,
+                (std::vector<FrequencyAccelerationMapEntry> * ret), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMaxMillis, (int32_t* ret),
                 (override));
     MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMinMillis, (int32_t* ret),
                 (override));
     MOCK_METHOD(ndk::ScopedAStatus, getPwleV2CompositionSizeMax, (int32_t* ret), (override));
     MOCK_METHOD(ndk::ScopedAStatus, composePwleV2,
-                (const std::vector<PwleV2Primitive>& e,
-                 const std::shared_ptr<IVibratorCallback>& cb),
+                (const CompositePwleV2& e, const std::shared_ptr<IVibratorCallback>& cb),
                 (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));