Merge "Adds hasArrSupport api support" into main
diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md
index 5d16d51..cd28520 100644
--- a/cmds/evemu-record/README.md
+++ b/cmds/evemu-record/README.md
@@ -38,10 +38,10 @@
 ### Timestamp bases
 
 By default, event timestamps are recorded relative to the time of the first event received during
-the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the
-system boot time instead. While this does not affect the playback of the recording, it can be useful
-for matching recorded events with other logs that use such timestamps, such as `dmesg` or the
-touchpad gesture debug logs emitted by `TouchpadInputMapper`.
+the recording. Passing `--timestamp-base=epoch` causes the timestamps to be recorded as Unix
+timestamps, relative to the Unix epoch (00:00:00 UTC on 1st January 1970). While this does not
+affect the playback of the recording, it can make the events in the recording easier to match up
+with those from other log sources, like logcat.
 
 [FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu
 [format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 1899a66..1696a62 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -250,6 +250,9 @@
 public:
     MotionEventBuilder(int32_t action, int32_t source) {
         mAction = action;
+        if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+            mFlags |= AMOTION_EVENT_FLAG_CANCELED;
+        }
         mSource = source;
         mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
         mDownTime = mEventTime;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 67b37b1..7ea34c2 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -137,6 +137,9 @@
     /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
     int32_t applyKeyRemapping(int32_t fromKeyCode) const;
 
+    /** Returns list of keycodes that remap to provided keycode (@see setKeyRemapping()) */
+    std::vector<int32_t> findKeyCodesMappedToKeyCode(int32_t toKeyCode) const;
+
     /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
      * that tries to find a replacement key code based on current meta state */
     std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index da0c5b2..47519c2 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -100,6 +100,17 @@
     RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
 
     /**
+     * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
+     * does not move between samples.
+     */
+    std::optional<Sample> mLastRealSample;
+
+    /**
+     * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
+     */
+    std::optional<Sample> mPreviousPrediction;
+
+    /**
      * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
      * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
      * added to mLatestSamples.
@@ -144,6 +155,23 @@
      */
     std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
 
+    /**
+     * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
+     */
+    void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
+
+    /**
+     * Overwrites with resampled data the pointer coordinates that did not move between motion event
+     * samples, that is, both x and y values are identical to mLastRealSample.
+     */
+    void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
+    /**
+     * Overwrites the pointer coordinates of a sample with event time older than
+     * that of mPreviousPrediction.
+     */
+    void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
     inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
 };
 } // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 379b609..6903cb5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -489,6 +489,7 @@
         "ProcessState.cpp",
         "Static.cpp",
         ":libbinder_aidl",
+        ":libbinder_accessor_aidl",
         ":libbinder_device_interface_sources",
     ],
     target: {
@@ -801,7 +802,6 @@
         "aidl/android/os/IServiceManager.aidl",
         "aidl/android/os/Service.aidl",
         "aidl/android/os/ServiceDebugInfo.aidl",
-        ":libbinder_accessor_aidl",
     ],
     path: "aidl",
 }
@@ -812,26 +812,7 @@
         "aidl/android/os/IAccessor.aidl",
     ],
     path: "aidl",
-}
-
-// TODO(b/353492849): Make this interface private to libbinder.
-aidl_interface {
-    name: "android.os.accessor",
-    srcs: [":libbinder_accessor_aidl"],
-    unstable: true,
-    backend: {
-        rust: {
-            enabled: true,
-            apex_available: [
-                "com.android.virt",
-            ],
-        },
-    },
-    visibility: [
-        ":__subpackages__",
-        "//system/tools/aidl:__subpackages__",
-        "//packages/modules/Virtualization:__subpackages__",
-    ],
+    visibility: [":__subpackages__"],
 }
 
 aidl_interface {
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 77b80ef..32388db 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -386,7 +386,7 @@
         ALOGE("Binder is null");
         return BAD_VALUE;
     }
-    sp<IAccessor> accessor = interface_cast<IAccessor>(binder);
+    sp<IAccessor> accessor = checked_interface_cast<IAccessor>(binder);
     if (accessor == nullptr) {
         ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str());
         return BAD_TYPE;
@@ -420,6 +420,28 @@
     return binder;
 }
 
+status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+                          sp<IBinder>* delegator) {
+    LOG_ALWAYS_FATAL_IF(delegator == nullptr, "delegateAccessor called with a null out param");
+    if (accessor == nullptr) {
+        ALOGW("Accessor argument to delegateAccessor is null.");
+        *delegator = nullptr;
+        return OK;
+    }
+    status_t status = validateAccessor(name, accessor);
+    if (status != OK) {
+        ALOGE("The provided accessor binder is not an IAccessor for instance %s. Status: "
+              "%s",
+              String8(name).c_str(), statusToString(status).c_str());
+        return status;
+    }
+    // validateAccessor already called checked_interface_cast and made sure this
+    // is a valid accessor object.
+    *delegator = sp<android::os::IAccessorDelegator>::make(interface_cast<IAccessor>(accessor));
+
+    return OK;
+}
+
 #if !defined(__ANDROID_VNDK__)
 // IPermissionController is not accessible to vendors
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 842ea77..18c4134 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1186,6 +1186,10 @@
         //printf("Writing %ld bytes, padded to %ld\n", len, padded);
         uint8_t* const data = mData+mDataPos;
 
+        if (status_t status = validateReadData(mDataPos + padded); status != OK) {
+            return nullptr; // drops status
+        }
+
         // Need to pad at end?
         if (padded != len) {
 #if BYTE_ORDER == BIG_ENDIAN
@@ -1774,6 +1778,10 @@
     const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
     if (enoughData && enoughObjects) {
 restart_write:
+        if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+            return status;
+        }
+
         *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
 
         // remember if it's a file descriptor
@@ -2020,6 +2028,10 @@
 
     if ((mDataPos+sizeof(val)) <= mDataCapacity) {
 restart_write:
+        if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+            return status;
+        }
+
         memcpy(mData + mDataPos, &val, sizeof(val));
         return finishWrite(sizeof(val));
     }
@@ -2656,14 +2668,14 @@
 }
 #endif // BINDER_WITH_KERNEL_IPC
 
-void Parcel::closeFileDescriptors() {
+void Parcel::closeFileDescriptors(size_t newObjectsSize) {
     if (auto* kernelFields = maybeKernelFields()) {
 #ifdef BINDER_WITH_KERNEL_IPC
         size_t i = kernelFields->mObjectsSize;
         if (i > 0) {
             // ALOGI("Closing file descriptors for %zu objects...", i);
         }
-        while (i > 0) {
+        while (i > newObjectsSize) {
             i--;
             const flat_binder_object* flat =
                     reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -2674,6 +2686,7 @@
             }
         }
 #else  // BINDER_WITH_KERNEL_IPC
+        (void)newObjectsSize;
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
 #endif // BINDER_WITH_KERNEL_IPC
     } else if (auto* rpcFields = maybeRpcFields()) {
@@ -2898,7 +2911,7 @@
         //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
         auto* kernelFields = maybeKernelFields();
         // Close FDs before freeing, otherwise they will leak for kernel binder.
-        closeFileDescriptors();
+        closeFileDescriptors(/*newObjectsSize=*/0);
         mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
                kernelFields ? kernelFields->mObjectsSize : 0);
     } else {
@@ -3035,13 +3048,38 @@
             objectsSize = 0;
         } else {
             if (kernelFields) {
+#ifdef BINDER_WITH_KERNEL_IPC
+                validateReadData(mDataSize); // hack to sort the objects
                 while (objectsSize > 0) {
-                    if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+                    if (kernelFields->mObjects[objectsSize - 1] + sizeof(flat_binder_object) <=
+                        desired)
+                        break;
                     objectsSize--;
                 }
+#endif // BINDER_WITH_KERNEL_IPC
             } else {
                 while (objectsSize > 0) {
-                    if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+                    // Object size varies by type.
+                    uint32_t pos = rpcFields->mObjectPositions[objectsSize - 1];
+                    size_t size = sizeof(RpcFields::ObjectType);
+                    uint32_t minObjectEnd;
+                    if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+                        minObjectEnd > mDataSize) {
+                        return BAD_VALUE;
+                    }
+                    const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+                    switch (type) {
+                        case RpcFields::TYPE_BINDER_NULL:
+                            break;
+                        case RpcFields::TYPE_BINDER:
+                            size += sizeof(uint64_t); // address
+                            break;
+                        case RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR:
+                            size += sizeof(int32_t); // fd index
+                            break;
+                    }
+
+                    if (pos + size <= desired) break;
                     objectsSize--;
                 }
             }
@@ -3090,15 +3128,24 @@
         if (mData) {
             memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
         }
+#ifdef BINDER_WITH_KERNEL_IPC
         if (objects && kernelFields && kernelFields->mObjects) {
             memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
+            // All FDs are owned when `mOwner`, even when `cookie == 0`. When
+            // we switch to `!mOwner`, we need to explicitly mark the FDs as
+            // owned.
+            for (size_t i = 0; i < objectsSize; i++) {
+                flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
+                if (flat->hdr.type == BINDER_TYPE_FD) {
+                    flat->cookie = 1;
+                }
+            }
         }
         // ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
         if (kernelFields) {
-            // TODO(b/239222407): This seems wrong. We should only free FDs when
-            // they are in a truncated section of the parcel.
-            closeFileDescriptors();
+            closeFileDescriptors(objectsSize);
         }
+#endif // BINDER_WITH_KERNEL_IPC
         mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
                kernelFields ? kernelFields->mObjectsSize : 0);
         mOwner = nullptr;
@@ -3225,11 +3272,19 @@
     }
     while (rpcFields->mObjectPositions.size() > newObjectsSize) {
         uint32_t pos = rpcFields->mObjectPositions.back();
-        rpcFields->mObjectPositions.pop_back();
+        uint32_t minObjectEnd;
+        if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+            minObjectEnd > mDataSize) {
+            return BAD_VALUE;
+        }
         const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
         if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
-            const auto fdIndex =
-                    *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType));
+            uint32_t objectEnd;
+            if (__builtin_add_overflow(minObjectEnd, sizeof(int32_t), &objectEnd) ||
+                objectEnd > mDataSize) {
+                return BAD_VALUE;
+            }
+            const auto fdIndex = *reinterpret_cast<const int32_t*>(mData + minObjectEnd);
             if (rpcFields->mFds == nullptr || fdIndex < 0 ||
                 static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
                 ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
@@ -3239,6 +3294,7 @@
             // In practice, this always removes the last element.
             rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex);
         }
+        rpcFields->mObjectPositions.pop_back();
     }
     return OK;
 }
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2b23276..81f7cdb 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -291,6 +291,28 @@
  * \return OK if the binder is an IAccessor for `instance`
  */
 LIBBINDER_EXPORTED status_t validateAccessor(const String16& instance, const sp<IBinder>& binder);
+
+/**
+ * Have libbinder wrap this IAccessor binder in an IAccessorDelegator and return
+ * it.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to service the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the Accessor.
+ * \param binder to wrap in a Delegator and register with service manager.
+ * \param outDelegator the wrapped kernel binder for IAccessorDelegator
+ *
+ * \return OK if the binder is an IAccessor for `instance` and the delegator was
+ * successfully created.
+ */
+LIBBINDER_EXPORTED status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+                                             sp<IBinder>* delegator);
 #endif // __TRUSTY__
 
 #ifndef __ANDROID__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index ed4e211..ceab20a 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -648,8 +648,8 @@
     LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
 
 private:
-    // Explicitly close all file descriptors in the parcel.
-    void closeFileDescriptors();
+    // Close all file descriptors in the parcel at object positions >= newObjectsSize.
+    void closeFileDescriptors(size_t newObjectsSize);
 
     // `objects` and `objectsSize` always 0 for RPC Parcels.
     typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
@@ -1239,7 +1239,7 @@
             if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
                 return -EOVERFLOW;
             }
-            auto data = reinterpret_cast<const T*>(readInplace(dataLen));
+            auto data = readInplace(dataLen);
             if (data == nullptr) return BAD_VALUE;
             // std::vector::insert and similar methods will require type-dependent
             // byte alignment when inserting from a const iterator such as `data`,
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 3c32a39..07b8c40 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -302,6 +302,28 @@
     }
 }
 
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* instance, AIBinder* accessor,
+                                                     AIBinder** outDelegator) {
+    LOG_ALWAYS_FATAL_IF(outDelegator == nullptr, "The outDelegator argument is null");
+    if (instance == nullptr || accessor == nullptr) {
+        ALOGW("instance or accessor arguments to ABinderRpc_Accessor_delegateBinder are null");
+        *outDelegator = nullptr;
+        return STATUS_UNEXPECTED_NULL;
+    }
+    sp<IBinder> accessorBinder = accessor->getBinder();
+
+    sp<IBinder> delegator;
+    status_t status = android::delegateAccessor(String16(instance), accessorBinder, &delegator);
+    if (status != OK) {
+        return PruneStatusT(status);
+    }
+    sp<AIBinder> binder = ABpBinder::lookupOrCreateFromBinder(delegator);
+    // This AIBinder needs a strong ref to pass ownership to the caller
+    binder->incStrong(nullptr);
+    *outDelegator = binder.get();
+    return OK;
+}
+
 ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) {
     if (addr == nullptr || len < 0 || static_cast<size_t>(len) < sizeof(sa_family_t)) {
         ALOGE("Invalid arguments in ABinderRpc_Connection_new");
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 8296356..0ad110e 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,7 +30,7 @@
 #include <android/binder_auto_utils.h>
 #include <android/binder_ibinder.h>
 
-#if defined(__ANDROID_VENDOR__)
+#if defined(__ANDROID_VENDOR_API__)
 #include <android/llndk-versioning.h>
 #elif !defined(API_LEVEL_AT_LEAST)
 #if defined(__BIONIC__)
@@ -39,7 +39,7 @@
 #else
 #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
 #endif  // __BIONIC__
-#endif  // __ANDROID_VENDOR__
+#endif  // __ANDROID_VENDOR_API__
 
 #if __has_include(<android/binder_shell.h>)
 #include <android/binder_shell.h>
@@ -297,11 +297,10 @@
     }
 #endif
 
-// TODO(b/368559337): fix versioning on product partition
-#if !defined(__ANDROID_PRODUCT__) && \
-        (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36)
+#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
     if API_LEVEL_AT_LEAST (36, 202504) {
-        if (codeToFunction != nullptr) {
+        if (codeToFunction != nullptr &&
+            (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
             AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
                                                                functionCount);
         }
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index c1d0e9f..83976b3 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,8 +22,8 @@
 #include <set>
 #include <sstream>
 
-// Include llndk-versioning.h only for vendor build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR__)
+// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
+#if defined(__ANDROID_VENDOR_API__)
 #include <android/llndk-versioning.h>
 #elif !defined(API_LEVEL_AT_LEAST)
 #if defined(__BIONIC__)
@@ -32,7 +32,7 @@
 #else
 #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
 #endif  // __BIONIC__
-#endif  // __ANDROID_VENDOR__
+#endif  // __ANDROID_VENDOR_API__
 
 namespace aidl::android::os {
 
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 7132554..9fe5d78 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -265,6 +265,35 @@
         __INTRODUCED_IN(36);
 
 /**
+ * Wrap an ABinderRpc_Accessor proxy binder with a delegator binder.
+ *
+ * The IAccessorDelegator binder delegates all calls to the proxy binder.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to serve the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the service associated with the Accessor
+ * \param binder the AIBinder* from the ABinderRpc_Accessor from the
+ *        ABinderRpc_Accessor_asBinder. The other process across the binder RPC
+ *        connection will have called this and passed the AIBinder* across a
+ *        binder interface to the process calling this function.
+ * \param outDelegator the AIBinder* for the kernel binder that wraps the
+ *        'binder' argument and delegates all calls to it. The caller now owns
+ *        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.
+ */
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* _Nonnull instance,
+                                                     AIBinder* _Nonnull binder,
+                                                     AIBinder* _Nullable* _Nonnull outDelegator)
+        __INTRODUCED_IN(36);
+
+/**
  * Create a new ABinderRpc_ConnectionInfo with sockaddr. This can be supported socket
  * types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets).
  *
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index c885816..4d691f8 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -257,6 +257,7 @@
     ABinderRpc_registerAccessorProvider; # systemapi
     ABinderRpc_unregisterAccessorProvider; # systemapi
     ABinderRpc_Accessor_new; # systemapi
+    ABinderRpc_Accessor_delegateAccessor; #systemapi
     ABinderRpc_Accessor_delete; # systemapi
     ABinderRpc_Accessor_asBinder; # systemapi
     ABinderRpc_Accessor_fromBinder; # systemapi
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 0e8e388..f7f3f35 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -123,7 +123,7 @@
 #[cfg(not(trusty))]
 pub use state::{ProcessState, ThreadState};
 #[cfg(not(any(android_vendor, android_vndk)))]
-pub use system_only::{Accessor, ConnectionInfo};
+pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
 
 /// Binder result containing a [`Status`] on error.
 pub type Result<T> = std::result::Result<T, Status>;
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index a91d84d..08582ab 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+use crate::binder::AsNative;
+use crate::error::{status_result, Result};
 use crate::proxy::SpIBinder;
 use crate::sys;
 
@@ -185,3 +187,27 @@
         }
     }
 }
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> {
+    let instance = CString::new(name).unwrap();
+    let mut delegator = ptr::null_mut();
+    let status =
+    // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+    // string pointers. Caller retains ownership of both pointers.
+    // `AServiceManager_addService` creates a new strong reference and copies
+    // the string, so both pointers need only be valid until the call returns.
+        unsafe { sys::ABinderRpc_Accessor_delegateAccessor(instance.as_ptr(),
+            binder.as_native_mut(), &mut delegator) };
+
+    status_result(status)?;
+
+    // Safety: `delegator` is either null or a valid, owned pointer at this
+    // point, so can be safely passed to `SpIBinder::from_raw`.
+    Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
+}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index bdb7e4a..489fa0a 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -945,6 +945,43 @@
         assert!(deleted.load(Ordering::Relaxed));
     }
 
+    #[test]
+    fn test_accessor_delegator_new_each_time() {
+        let get_connection_info = move |_instance: &str| None;
+        let accessor = Accessor::new("foo.service", get_connection_info);
+        let delegator_binder =
+            binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+        let delegator_binder2 =
+            binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+
+        // The delegate_accessor creates new delegators each time
+        assert!(delegator_binder != delegator_binder2);
+    }
+
+    #[test]
+    fn test_accessor_delegate_the_delegator() {
+        let get_connection_info = move |_instance: &str| None;
+        let accessor = Accessor::new("foo.service", get_connection_info);
+        let delegator_binder =
+            binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+        let delegator_binder2 =
+            binder::delegate_accessor("foo.service", delegator_binder.clone().unwrap());
+
+        assert!(delegator_binder.clone() == delegator_binder);
+        // The delegate_accessor creates new delegators each time. Even when they are delegators
+        // of delegators.
+        assert!(delegator_binder != delegator_binder2);
+    }
+
+    #[test]
+    fn test_accessor_delegator_wrong_name() {
+        let get_connection_info = move |_instance: &str| None;
+        let accessor = Accessor::new("foo.service", get_connection_info);
+        let delegator_binder =
+            binder::delegate_accessor("NOT.foo.service", accessor.as_binder().unwrap());
+        assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
+    }
+
     #[tokio::test]
     async fn reassociate_rust_binder_async() {
         let service_name = "testing_service";
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index bcab6de..970852c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -46,6 +46,7 @@
 
 #include <linux/sched.h>
 #include <sys/epoll.h>
+#include <sys/mman.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -110,6 +111,8 @@
     BINDER_LIB_TEST_LINK_DEATH_TRANSACTION,
     BINDER_LIB_TEST_WRITE_FILE_TRANSACTION,
     BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION,
+    BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION,
+    BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION,
     BINDER_LIB_TEST_EXIT_TRANSACTION,
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
@@ -536,6 +539,30 @@
         };
 };
 
+ssize_t countFds() {
+    return std::distance(std::filesystem::directory_iterator("/proc/self/fd"),
+                         std::filesystem::directory_iterator{});
+}
+
+struct FdLeakDetector {
+    int startCount;
+
+    FdLeakDetector() {
+        // This log statement is load bearing. We have to log something before
+        // counting FDs to make sure the logging system is initialized, otherwise
+        // the sockets it opens will look like a leak.
+        ALOGW("FdLeakDetector counting FDs.");
+        startCount = countFds();
+    }
+    ~FdLeakDetector() {
+        int endCount = countFds();
+        if (startCount != endCount) {
+            ADD_FAILURE() << "fd count changed (" << startCount << " -> " << endCount
+                          << ") fd leak?";
+        }
+    }
+};
+
 TEST_F(BinderLibTest, CannotUseBinderAfterFork) {
     // EXPECT_DEATH works by forking the process
     EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork");
@@ -1175,6 +1202,100 @@
     EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize));
 }
 
+TEST_F(BinderLibTest, RecvOwnedFileDescriptors) {
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+                                 &reply));
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndWriteInt) {
+    GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+                                 &reply));
+    reply.setDataPosition(reply.dataSize());
+    reply.writeInt32(0);
+    reply.setDataPosition(0);
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndTruncate) {
+    GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+                                 &reply));
+    reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
+TEST_F(BinderLibTest, RecvUnownedFileDescriptors) {
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+                                 &reply));
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndWriteInt) {
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+                                 &reply));
+    reply.setDataPosition(reply.dataSize());
+    reply.writeInt32(0);
+    reply.setDataPosition(0);
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndTruncate) {
+    FdLeakDetector fd_leak_detector;
+
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(NO_ERROR,
+              m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+                                 &reply));
+    reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+    unique_fd a, b;
+    EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+    EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
 TEST_F(BinderLibTest, PromoteLocal) {
     sp<IBinder> strong = new BBinder();
     wp<IBinder> weak = strong;
@@ -2224,6 +2345,40 @@
                 if (ret != size) return UNKNOWN_ERROR;
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION: {
+                unique_fd fd1(memfd_create("memfd1", MFD_CLOEXEC));
+                if (!fd1.ok()) {
+                    PLOGE("memfd_create failed");
+                    return UNKNOWN_ERROR;
+                }
+                unique_fd fd2(memfd_create("memfd2", MFD_CLOEXEC));
+                if (!fd2.ok()) {
+                    PLOGE("memfd_create failed");
+                    return UNKNOWN_ERROR;
+                }
+                status_t ret;
+                ret = reply->writeFileDescriptor(fd1.release(), true);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                ret = reply->writeFileDescriptor(fd2.release(), true);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                return NO_ERROR;
+            }
+            case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION: {
+                status_t ret;
+                ret = reply->writeFileDescriptor(STDOUT_FILENO, false);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                ret = reply->writeFileDescriptor(STDERR_FILENO, false);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION:
                 alarm(10);
                 return NO_ERROR;
@@ -2261,7 +2416,7 @@
                 if (ret != NO_ERROR) {
                     return ret;
                 }
-                auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+                auto event = frozenStateChangeCallback->events.popWithTimeout(1000ms);
                 if (!event.has_value()) {
                     return NOT_ENOUGH_DATA;
                 }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 077a33a..506fc71 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -75,6 +75,8 @@
 constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
 #endif
 
+constexpr char kKnownAidlService[] = "activity";
+
 static std::string WaitStatusToString(int wstatus) {
     if (WIFEXITED(wstatus)) {
         return "exit status " + std::to_string(WEXITSTATUS(wstatus));
@@ -1549,22 +1551,47 @@
     EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0));
 }
 
-TEST_P(BinderRpcAccessor, ARpcGetService) {
+TEST_F(BinderARpcNdk, ARpcDelegateAccessorWrongInstance) {
+    AccessorProviderData* data = new AccessorProviderData();
+    ABinderRpc_Accessor* accessor = getAccessor(kARpcInstance, data);
+    ASSERT_NE(accessor, nullptr);
+    AIBinder* localAccessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+    EXPECT_NE(localAccessorBinder, nullptr);
+
+    AIBinder* delegatorBinder = nullptr;
+    binder_status_t status =
+            ABinderRpc_Accessor_delegateAccessor("bar", localAccessorBinder, &delegatorBinder);
+    EXPECT_EQ(status, NAME_NOT_FOUND);
+
+    AIBinder_decStrong(localAccessorBinder);
+    ABinderRpc_Accessor_delete(accessor);
+    delete data;
+}
+
+TEST_F(BinderARpcNdk, ARpcDelegateNonAccessor) {
+    auto service = defaultServiceManager()->checkService(String16(kKnownAidlService));
+    ASSERT_NE(nullptr, service);
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(service));
+
+    AIBinder* delegatorBinder = nullptr;
+    binder_status_t status =
+            ABinderRpc_Accessor_delegateAccessor("bar", binder.get(), &delegatorBinder);
+
+    EXPECT_EQ(status, BAD_TYPE);
+}
+
+inline void getServiceTest(BinderRpcTestProcessSession& proc,
+                           ABinderRpc_AccessorProvider_getAccessorCallback getAccessor) {
     constexpr size_t kNumThreads = 10;
     bool isDeleted = false;
 
-    auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
     AccessorProviderData* data =
             new AccessorProviderData{proc.proc->sessions[0].addr, proc.proc->sessions[0].addrLen,
                                      &isDeleted};
-
     ABinderRpc_AccessorProvider* provider =
             ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
                                                 kARpcNumSupportedServices, data,
                                                 accessorProviderDataOnDelete);
-
     EXPECT_NE(provider, nullptr);
     EXPECT_FALSE(isDeleted);
 
@@ -1580,6 +1607,45 @@
     waitForExtraSessionCleanup(proc);
 }
 
+TEST_P(BinderRpcAccessor, ARpcGetService) {
+    constexpr size_t kNumThreads = 10;
+    auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    getServiceTest(proc, getAccessor);
+}
+
+// Create accessors and wrap each of the accessors in a delegator
+ABinderRpc_Accessor* getDelegatedAccessor(const char* instance, void* cookie) {
+    ABinderRpc_Accessor* accessor = getAccessor(instance, cookie);
+    AIBinder* accessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+    // Once we have a handle to the AIBinder which holds a reference to the
+    // underlying accessor IBinder, we can get rid of the ABinderRpc_Accessor
+    ABinderRpc_Accessor_delete(accessor);
+
+    AIBinder* delegatorBinder = nullptr;
+    binder_status_t status =
+            ABinderRpc_Accessor_delegateAccessor(instance, accessorBinder, &delegatorBinder);
+    // No longer need this AIBinder. The delegator has a reference to the
+    // underlying IBinder on success, and on failure we are done here.
+    AIBinder_decStrong(accessorBinder);
+    if (status != OK || delegatorBinder == nullptr) {
+        ALOGE("Unexpected behavior. Status: %s, delegator ptr: %p", statusToString(status).c_str(),
+              delegatorBinder);
+        return nullptr;
+    }
+
+    return ABinderRpc_Accessor_fromBinder(instance, delegatorBinder);
+}
+
+TEST_P(BinderRpcAccessor, ARpcGetServiceWithDelegator) {
+    constexpr size_t kNumThreads = 10;
+    auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    getServiceTest(proc, getDelegatedAccessor);
+}
+
 #endif // BINDER_WITH_KERNEL_IPC
 
 #ifdef BINDER_RPC_TO_TRUSTY_TEST
@@ -1845,7 +1911,7 @@
     ASSERT_NE(nullptr, sm);
     // Any Java service with non-empty getInterfaceDescriptor() would do.
     // Let's pick activity.
-    auto binder = sm->checkService(String16("activity"));
+    auto binder = sm->checkService(String16(kKnownAidlService));
     ASSERT_NE(nullptr, binder);
     auto descriptor = binder->getInterfaceDescriptor();
     ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index d3f2899..1c7e0e4 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -109,6 +109,14 @@
 } # wb_libcameraservice
 
 flag {
+  name: "wb_unlimited_slots"
+  namespace: "core_graphics"
+  description: "Adds APIs and updates the implementation of bufferqueues to have a user-defined slot count."
+  bug: "341359814"
+  is_fixed_read_only: true
+} # wb_unlimited_slots
+
+flag {
   name: "bq_producer_throttles_only_async_mode"
   namespace: "core_graphics"
   description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode."
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index ce8bb43..9665de7 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -193,13 +193,29 @@
 
 InputConsumerNoResampling::~InputConsumerNoResampling() {
     ensureCalledOnLooperThread(__func__);
-    consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
     while (!mOutboundQueue.empty()) {
         processOutboundEvents();
         // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
         // so keep trying to send the events as long as they are present in the queue.
     }
+
     setFdEvents(0);
+    // If there are any remaining unread batches, send an ack for them and don't deliver
+    // them to callbacks.
+    for (auto& [_, batches] : mBatches) {
+        while (!batches.empty()) {
+            finishInputEvent(batches.front().header.seq, /*handled=*/false);
+            batches.pop();
+        }
+    }
+    // However, it is still up to the app to finish any events that have already been delivered
+    // to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
+    // that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
+    // instead. We can't use "mBatchedSequenceNumbers" for this purpose, because it only contains
+    // batchable (i.e., ACTION_MOVE) events that were sent to the callbacks.
+    const size_t unfinishedEvents = mConsumeTimes.size();
+    LOG_IF(INFO, unfinishedEvents != 0)
+            << getName() << " has " << unfinishedEvents << " unfinished event(s)";
 }
 
 int InputConsumerNoResampling::handleReceiveCallback(int events) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index b0563ab..d775327 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -365,6 +365,17 @@
     return toKeyCode;
 }
 
+std::vector<int32_t> KeyCharacterMap::findKeyCodesMappedToKeyCode(int32_t toKeyCode) const {
+    std::vector<int32_t> fromKeyCodes;
+
+    for (const auto& [from, to] : mKeyRemapping) {
+        if (toKeyCode == to) {
+            fromKeyCodes.push_back(from);
+        }
+    }
+    return fromKeyCodes;
+}
+
 std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
                                                               int32_t fromMetaState) const {
     int32_t toKeyCode = fromKeyCode;
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 1adff7b..e2cc6fb 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <ostream>
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -26,10 +27,7 @@
 #include <input/Resampler.h>
 #include <utils/Timers.h>
 
-using std::chrono::nanoseconds;
-
 namespace android {
-
 namespace {
 
 const bool IS_DEBUGGABLE_BUILD =
@@ -49,6 +47,8 @@
     return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
 }
 
+using std::chrono::nanoseconds;
+
 constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
 
 constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
@@ -75,6 +75,31 @@
     resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
     return resampledCoords;
 }
+
+bool equalXY(const PointerCoords& a, const PointerCoords& b) {
+    return (a.getX() == b.getX()) && (a.getY() == b.getY());
+}
+
+void setMotionEventPointerCoords(MotionEvent& motionEvent, size_t sampleIndex, size_t pointerIndex,
+                                 const PointerCoords& pointerCoords) {
+    // Ideally, we should not cast away const. In this particular case, it's safe to cast away const
+    // and dereference getHistoricalRawPointerCoords returned pointer because motionEvent is a
+    // nonconst reference to a MotionEvent object, so mutating the object should not be undefined
+    // behavior; moreover, the invoked method guarantees to return a valid pointer. Otherwise, it
+    // fatally logs. Alternatively, we could've created a new MotionEvent from scratch, but this
+    // approach is simpler and more efficient.
+    PointerCoords& motionEventCoords = const_cast<PointerCoords&>(
+            *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+    motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoords.getX());
+    motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoords.getY());
+    motionEventCoords.isResampled = pointerCoords.isResampled;
+}
+
+std::ostream& operator<<(std::ostream& os, const PointerCoords& pointerCoords) {
+    os << "(" << pointerCoords.getX() << ", " << pointerCoords.getY() << ")";
+    return os;
+}
+
 } // namespace
 
 void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
@@ -85,12 +110,9 @@
         std::vector<Pointer> pointers;
         const size_t numPointers = motionEvent.getPointerCount();
         for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
-            // getSamplePointerCoords is the vector representation of a getHistorySize by
-            // getPointerCount matrix.
-            const PointerCoords& pointerCoords =
-                    motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
-            pointers.push_back(
-                    Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+            pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
+                                       *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
+                                                                                   sampleIndex))});
         }
         mLatestSamples.pushBack(
                 Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
@@ -245,6 +267,47 @@
     return RESAMPLE_LATENCY;
 }
 
+void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
+    const size_t numSamples = motionEvent.getHistorySize() + 1;
+    for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+        overwriteStillPointers(motionEvent, sampleIndex);
+        overwriteOldPointers(motionEvent, sampleIndex);
+    }
+}
+
+void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+    for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
+        const PointerCoords& pointerCoords =
+                *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
+        if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
+            LOG_IF(INFO, debugResampling())
+                    << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
+                    << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
+                    << mLastRealSample->pointers[pointerIndex].coords;
+            setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+                                        mPreviousPrediction->pointers[pointerIndex].coords);
+        }
+    }
+}
+
+void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+    if (!mPreviousPrediction.has_value()) {
+        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.";
+        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+             ++pointerIndex) {
+            setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+                                        mPreviousPrediction->pointers[pointerIndex].coords);
+        }
+    }
+}
+
 void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
                                           const InputMessage* futureSample) {
     const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
@@ -261,6 +324,16 @@
             : (attemptExtrapolation(resampleTime));
     if (sample.has_value()) {
         addSampleToMotionEvent(*sample, motionEvent);
+        if (mPreviousPrediction.has_value()) {
+            overwriteMotionEventSamples(motionEvent);
+        }
+        // mPreviousPrediction is only updated whenever extrapolation occurs because extrapolation
+        // is about predicting upcoming scenarios.
+        if (futureSample == nullptr) {
+            mPreviousPrediction = sample;
+        }
     }
+    mLastRealSample = *(mLatestSamples.end() - 1);
 }
+
 } // namespace android
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index b139e87..883ca82 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -197,8 +197,6 @@
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent({InputEventEntry{0ms,
                                                {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
@@ -238,8 +236,6 @@
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms, {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent({InputEventEntry{0ms,
                                                {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}},
@@ -280,8 +276,6 @@
              {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::STYLUS}},
              AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent({InputEventEntry{0ms,
                                                {Pointer{.id = 0,
@@ -338,8 +332,6 @@
              {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::MOUSE}},
              AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent({InputEventEntry{0ms,
                                                {Pointer{.id = 0,
@@ -396,8 +388,6 @@
              {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
              AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent(
             {InputEventEntry{0ms,
@@ -438,8 +428,6 @@
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
 
-    mClientTestChannel->assertNoSentMessages();
-
     invokeLooperCallback();
     assertReceivedMotionEvent({InputEventEntry{0ms,
                                                {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
@@ -468,4 +456,114 @@
     mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
 }
 
+/**
+ * Once we send a resampled value to the app, we should continue to send the last predicted value if
+ * a pointer does not move. Only real values are used to determine if a pointer does not move.
+ */
+TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+    // InputEvent with a single action.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent({InputEventEntry{0ms,
+                                               {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+                                               AMOTION_EVENT_ACTION_DOWN}});
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+    assertReceivedMotionEvent(
+            {InputEventEntry{10ms,
+                             {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{20ms,
+                             {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{25ms,
+                             {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}});
+
+    // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again,
+    // the system should still report 35.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+    assertReceivedMotionEvent(
+            {InputEventEntry{40ms,
+                             {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+             InputEventEntry{45ms,
+                             {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+    // InputEvent with a single action.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent({InputEventEntry{0ms,
+                                               {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+                                               AMOTION_EVENT_ACTION_DOWN}});
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+    assertReceivedMotionEvent(
+            {InputEventEntry{10ms,
+                             {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{20ms,
+                             {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{25ms,
+                             {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}});
+
+    // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
+    // because we are further bound by how far we can extrapolate by the "last time delta".
+    // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
+    // from the event at 20ms, which is why the resampled event is at t = 25 ms.
+
+    // We resampled the event to 25 ms. Now, an older 'real' event comes in.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {24ms, {Pointer{.id = 0, .x = 40.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+    assertReceivedMotionEvent(
+            {InputEventEntry{24ms,
+                             {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+             InputEventEntry{26ms,
+                             {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index cbb332e..6a3bbe5 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -43,6 +43,9 @@
 using ::testing::AllOf;
 using ::testing::Matcher;
 
+constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr auto ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
 struct Pointer {
     int32_t id{0};
     ToolType toolType{ToolType::FINGER};
@@ -80,10 +83,19 @@
         --mOnBatchedInputEventPendingInvocationCount;
     }
 
-    void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
-        std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
-        ASSERT_NE(motionEvent, nullptr);
+    std::unique_ptr<MotionEvent> assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+        if (mMotionEvents.empty()) {
+            ADD_FAILURE() << "No motion events received";
+            return nullptr;
+        }
+        std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+        mMotionEvents.pop();
+        if (motionEvent == nullptr) {
+            ADD_FAILURE() << "The consumed motion event should never be null";
+            return nullptr;
+        }
         EXPECT_THAT(*motionEvent, matcher);
+        return motionEvent;
     }
 
     InputMessage nextPointerMessage(std::chrono::nanoseconds eventTime, DeviceId deviceId,
@@ -93,12 +105,15 @@
     sp<Looper> mLooper;
     std::unique_ptr<InputConsumerNoResampling> mConsumer;
 
-    BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
-    BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
-    BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
-    BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
-    BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
-    BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+    std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+    std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+    std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+    std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+    std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+    // Whether or not to automatically call "finish" whenever a motion event is received.
+    bool mShouldFinishMotions{true};
 
 private:
     uint32_t mLastSeq{0};
@@ -107,11 +122,13 @@
     // InputConsumerCallbacks interface
     void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
         mKeyEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
     }
     void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
         mMotionEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        if (mShouldFinishMotions) {
+            mConsumer->finishInputEvent(seq, /*handled=*/true);
+        }
     }
     void onBatchedInputEventPending(int32_t pendingBatchSource) override {
         if (!mConsumer->probablyHasInput()) {
@@ -121,19 +138,19 @@
     };
     void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
         mFocusEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
     };
     void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
         mCaptureEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
     };
     void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
         mDragEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
     }
     void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
         mTouchModeEvents.push(std::move(event));
-        mConsumer->finishInputEvent(seq, true);
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
     };
 };
 
@@ -153,15 +170,15 @@
 TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
                                                .eventTime(nanoseconds{0ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .action(ACTION_DOWN)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
                                                .eventTime(nanoseconds{5ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
                                                .eventTime(nanoseconds{10ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
 
     mClientTestChannel->assertNoSentMessages();
@@ -172,10 +189,10 @@
 
     mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
 
-    std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
-    ASSERT_NE(downMotionEvent, nullptr);
+    assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
 
-    std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+    std::unique_ptr<MotionEvent> moveMotionEvent =
+            assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
     ASSERT_NE(moveMotionEvent, nullptr);
     EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
 
@@ -187,19 +204,19 @@
 TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
                                                .eventTime(nanoseconds{0ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .action(ACTION_DOWN)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
                                                .eventTime(nanoseconds{5ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
                                                .eventTime(nanoseconds{10ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
                                                .eventTime(nanoseconds{15ms}.count())
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
 
     mClientTestChannel->assertNoSentMessages();
@@ -210,56 +227,98 @@
 
     mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
 
-    std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
-    ASSERT_NE(downMotionEvent, nullptr);
+    assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
 
-    std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+    std::unique_ptr<MotionEvent> moveMotionEvent =
+            assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
     ASSERT_NE(moveMotionEvent, nullptr);
     const size_t numSamples = moveMotionEvent->getHistorySize() + 1;
     EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
               moveMotionEvent->getEventTime());
 
-    // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
-    // consumer is set to null before destroying consumer. This leads to a member function call on a
-    // null object.
-    // TODO(b/332613662): Remove this workaround.
-    mConsumer->consumeBatchedInputEvents(std::nullopt);
+    mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    // The event with seq=3 remains unconsumed, and therefore finish will not be called for it until
+    // after the consumer is destroyed.
+    mConsumer.reset();
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/false);
+    mClientTestChannel->assertNoSentMessages();
+}
 
-    mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
-    mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
-    mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
-    mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
+/**
+ * During normal operation, the user of InputConsumer (callbacks) is expected to call "finish"
+ * for each input event received in InputConsumerCallbacks.
+ * If the InputConsumer is destroyed, the events that were already sent to the callbacks will not
+ * be finished automatically.
+ */
+TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) {
+    mClientTestChannel->enqueueMessage(
+            InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+    mClientTestChannel->enqueueMessage(
+            InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+    mShouldFinishMotions = false;
+    invokeLooperCallback();
+    assertOnBatchedInputEventPendingWasCalled();
+    assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+    mClientTestChannel->assertNoSentMessages();
+    // The "finishInputEvent" was not called by the InputConsumerCallbacks.
+    // Now, destroy the consumer and check that the "finish" was not called automatically for the
+    // DOWN event, but was called for the undelivered MOVE event.
+    mConsumer.reset();
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+    mClientTestChannel->assertNoSentMessages();
+}
+
+/**
+ * Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
+ * the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
+ * destroyed.
+ * This test is similar to the one above, but here we are calling "finish"
+ * automatically for any event received in the callbacks.
+ */
+TEST_F(InputConsumerTest, UnconsumedEventDoesNotCauseACrash) {
+    mClientTestChannel->enqueueMessage(
+            InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+    invokeLooperCallback();
+    assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+    mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+    mClientTestChannel->enqueueMessage(
+            InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+    invokeLooperCallback();
+    mConsumer.reset();
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
 }
 
 TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
                                                .deviceId(0)
-                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .action(ACTION_DOWN)
                                                .build());
 
     invokeLooperCallback();
-    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN)));
 
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
                                                .deviceId(0)
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
                                                .deviceId(0)
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
                                                .deviceId(0)
-                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .action(ACTION_MOVE)
                                                .build());
 
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
                                                .deviceId(1)
-                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .action(ACTION_DOWN)
                                                .build());
 
     invokeLooperCallback();
-    assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+    assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN)));
 
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
                                                .deviceId(0)
@@ -267,7 +326,7 @@
                                                .build());
 
     invokeLooperCallback();
-    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE)));
 
     mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
     mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
@@ -303,60 +362,52 @@
  * The first field is device ID, and the second field is event time.
  */
 TEST_F(InputConsumerTest, MultiDeviceResampling) {
-    mClientTestChannel->enqueueMessage(nextPointerMessage(0ms, /*deviceId=*/0,
-                                                          AMOTION_EVENT_ACTION_DOWN,
-                                                          Pointer{.x = 0, .y = 0}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(0ms, /*deviceId=*/0, ACTION_DOWN, Pointer{.x = 0, .y = 0}));
 
     mClientTestChannel->assertNoSentMessages();
 
     invokeLooperCallback();
-    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                    WithSampleCount(1)));
+    assertReceivedMotionEvent(
+            AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
 
-    mClientTestChannel->enqueueMessage(nextPointerMessage(5ms, /*deviceId=*/0,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 1.0f, .y = 2.0f}));
-    mClientTestChannel->enqueueMessage(nextPointerMessage(10ms, /*deviceId=*/0,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 2.0f, .y = 4.0f}));
-    mClientTestChannel->enqueueMessage(nextPointerMessage(15ms, /*deviceId=*/1,
-                                                          AMOTION_EVENT_ACTION_DOWN,
-                                                          Pointer{.x = 10.0f, .y = 10.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(5ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 1.0f, .y = 2.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(10ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 2.0f, .y = 4.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(15ms, /*deviceId=*/1, ACTION_DOWN, Pointer{.x = 10.0f, .y = 10.0f}));
 
     invokeLooperCallback();
     mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
 
-    assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                    WithSampleCount(1)));
     assertReceivedMotionEvent(
-            AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+            AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+    assertReceivedMotionEvent(
+            AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
                   WithSample(/*sampleIndex=*/2,
                              Sample{11ms,
                                     {PointerArgs{.x = 2.2f, .y = 4.4f, .isResampled = true}}})));
 
-    mClientTestChannel->enqueueMessage(nextPointerMessage(20ms, /*deviceId=*/1,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 11.0f, .y = 12.0f}));
-    mClientTestChannel->enqueueMessage(nextPointerMessage(25ms, /*deviceId=*/1,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 12.0f, .y = 14.0f}));
-    mClientTestChannel->enqueueMessage(nextPointerMessage(30ms, /*deviceId=*/0,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 5.0f, .y = 6.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(20ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 11.0f, .y = 12.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(25ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 12.0f, .y = 14.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(30ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 5.0f, .y = 6.0f}));
 
     invokeLooperCallback();
     assertOnBatchedInputEventPendingWasCalled();
     mConsumer->consumeBatchedInputEvents(32'000'000 /*ns*/);
 
     assertReceivedMotionEvent(
-            AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+            AllOf(WithDeviceId(1), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
                   WithSample(/*sampleIndex=*/2,
                              Sample{27ms,
                                     {PointerArgs{.x = 12.4f, .y = 14.8f, .isResampled = true}}})));
 
-    mClientTestChannel->enqueueMessage(nextPointerMessage(35ms, /*deviceId=*/0,
-                                                          AMOTION_EVENT_ACTION_MOVE,
-                                                          Pointer{.x = 8.0f, .y = 9.0f}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(35ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 8.0f, .y = 9.0f}));
     mClientTestChannel->enqueueMessage(nextPointerMessage(40ms, /*deviceId=*/1,
                                                           AMOTION_EVENT_ACTION_UP,
                                                           Pointer{.x = 12.0f, .y = 14.0f}));
@@ -371,7 +422,7 @@
             AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1)));
 
     assertReceivedMotionEvent(
-            AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+            AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
                   WithSample(/*sampleIndex=*/2,
                              Sample{37'500'000ns,
                                     {PointerArgs{.x = 9.5f, .y = 10.5f, .isResampled = true}}})));
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
deleted file mode 100644
index eda5ea4..0000000
--- a/libs/ultrahdr/Android.bp
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: [
-        "frameworks_native_license",
-        "adobe_hdr_gain_map_license",
-    ],
-}
-
-cc_library {
-    name: "libultrahdr-deprecated",
-    enabled: false,
-    host_supported: true,
-    vendor_available: true,
-    export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
-
-    srcs: [
-        "icc.cpp",
-        "jpegr.cpp",
-        "gainmapmath.cpp",
-        "jpegrutils.cpp",
-        "multipictureformat.cpp",
-    ],
-
-    shared_libs: [
-        "libimage_io",
-        "libjpeg",
-        "libjpegencoder",
-        "libjpegdecoder",
-        "liblog",
-        "libutils",
-    ],
-}
-
-cc_library {
-    name: "libjpegencoder-deprecated",
-    enabled: false,
-    host_supported: true,
-    vendor_available: true,
-
-    shared_libs: [
-        "libjpeg",
-        "liblog",
-        "libutils",
-    ],
-
-    export_include_dirs: ["include"],
-
-    srcs: [
-        "jpegencoderhelper.cpp",
-    ],
-}
-
-cc_library {
-    name: "libjpegdecoder-deprecated",
-    enabled: false,
-    host_supported: true,
-    vendor_available: true,
-
-    shared_libs: [
-        "libjpeg",
-        "liblog",
-        "libutils",
-    ],
-
-    export_include_dirs: ["include"],
-
-    srcs: [
-        "jpegdecoderhelper.cpp",
-    ],
-}
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
deleted file mode 100644
index 8d9132f..0000000
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_defaults {
-    name: "ultrahdr_fuzzer_defaults-deprecated",
-    enabled: false,
-    host_supported: true,
-    shared_libs: [
-        "libimage_io",
-        "libjpeg",
-    ],
-    static_libs: [
-        "libjpegdecoder",
-        "libjpegencoder",
-        "libultrahdr",
-        "libutils",
-        "liblog",
-    ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        description: "The fuzzers target the APIs of jpeg hdr",
-        service_privilege: "constrained",
-        users: "multi_user",
-        fuzzed_code_usage: "future_version",
-        vector: "local_no_privileges_required",
-    },
-}
-
-cc_fuzz {
-    name: "ultrahdr_enc_fuzzer-deprecated",
-    enabled: false,
-    defaults: ["ultrahdr_fuzzer_defaults"],
-    srcs: [
-        "ultrahdr_enc_fuzzer.cpp",
-    ],
-}
-
-cc_fuzz {
-    name: "ultrahdr_dec_fuzzer-deprecated",
-    enabled: false,
-    defaults: ["ultrahdr_fuzzer_defaults"],
-    srcs: [
-        "ultrahdr_dec_fuzzer.cpp",
-    ],
-}
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
deleted file mode 100644
index 00cc797..0000000
--- a/libs/ultrahdr/tests/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_test {
-    name: "ultrahdr_unit_test-deprecated",
-    enabled: false,
-    test_suites: ["device-tests"],
-    srcs: [
-        "gainmapmath_test.cpp",
-        "icchelper_test.cpp",
-        "jpegr_test.cpp",
-        "jpegencoderhelper_test.cpp",
-        "jpegdecoderhelper_test.cpp",
-    ],
-    shared_libs: [
-        "libimage_io",
-        "libjpeg",
-        "liblog",
-    ],
-    static_libs: [
-        "libgmock",
-        "libgtest",
-        "libjpegdecoder",
-        "libjpegencoder",
-        "libultrahdr",
-        "libutils",
-    ],
-    data: [
-        "./data/*.*",
-    ],
-}
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 9809148..1ed6c29 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <gui/WindowInfo.h>
+#include <input/Input.h>
 #include <utils/StrongPointer.h>
 #include <string>
 
@@ -25,8 +26,9 @@
 namespace inputdispatcher {
 
 struct DragState {
-    DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t pointerId)
-          : dragWindow(windowHandle), pointerId(pointerId) {}
+    DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId,
+              int32_t pointerId)
+          : dragWindow(windowHandle), deviceId(deviceId), pointerId(pointerId) {}
     void dump(std::string& dump, const char* prefix = "");
 
     // The window being dragged.
@@ -37,6 +39,8 @@
     bool isStartDrag = false;
     // Indicate if the stylus button is down at the start of the drag.
     bool isStylusButtonDownAtStart = false;
+    // Indicate which device started this drag and drop.
+    const DeviceId deviceId;
     // Indicate which pointer id is tracked by the drag and drop.
     const int32_t pointerId;
 };
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4b43c27..602904f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1031,8 +1031,7 @@
         nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
 
         if (mPerDeviceInputLatencyMetricsFlag) {
-            const nsecs_t nextStatisticsPush = processLatencyStatisticsLocked();
-            nextWakeupTime = std::min(nextWakeupTime, nextStatisticsPush);
+            processLatencyStatisticsLocked();
         }
 
         // We are about to enter an infinitely long sleep, because we have no commands or
@@ -1117,9 +1116,8 @@
 
 /**
  * Check if enough time has passed since the last latency statistics push.
- * Return the time at which we should wake up next.
  */
-nsecs_t InputDispatcher::processLatencyStatisticsLocked() {
+void InputDispatcher::processLatencyStatisticsLocked() {
     const nsecs_t currentTime = now();
     // Log the atom recording latency statistics if more than 6 hours passed from the last
     // push
@@ -1127,7 +1125,6 @@
         mInputEventTimelineProcessor->pushLatencyStatistics();
         mLastStatisticPushTime = currentTime;
     }
-    return mLastStatisticPushTime + LATENCY_STATISTICS_PUSH_INTERVAL;
 }
 
 std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
@@ -2898,7 +2895,8 @@
 }
 
 void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
-    if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId) {
+    if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
+        mDragState->deviceId != entry.deviceId) {
         return;
     }
 
@@ -4796,6 +4794,39 @@
     }
 }
 
+bool InputDispatcher::shouldRejectInjectedMotionLocked(const MotionEvent& motionEvent,
+                                                       DeviceId deviceId,
+                                                       ui::LogicalDisplayId displayId,
+                                                       std::optional<gui::Uid> targetUid,
+                                                       int32_t flags) {
+    // Don't verify targeted injection, since it will only affect the caller's
+    // window, and the windows are typically destroyed at the end of the test.
+    if (targetUid.has_value()) {
+        return false;
+    }
+
+    // Verify all other injected streams, whether the injection is coming from apps or from
+    // input filter. Print an error if the stream becomes inconsistent with this event.
+    // An inconsistent injected event sent could cause a crash in the later stages of
+    // dispatching pipeline.
+    auto [it, _] = mInputFilterVerifiersByDisplay.try_emplace(displayId,
+                                                              std::string("Injection on ") +
+                                                                      displayId.toString());
+    InputVerifier& verifier = it->second;
+
+    Result<void> result =
+            verifier.processMovement(deviceId, motionEvent.getSource(), motionEvent.getAction(),
+                                     motionEvent.getPointerCount(),
+                                     motionEvent.getPointerProperties(),
+                                     motionEvent.getSamplePointerCoords(), flags);
+    if (!result.ok()) {
+        logDispatchStateLocked();
+        LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error();
+        return true;
+    }
+    return false;
+}
+
 InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
                                                             std::optional<gui::Uid> targetUid,
                                                             InputEventInjectionSync syncMode,
@@ -4906,32 +4937,10 @@
 
             mLock.lock();
 
-            {
-                // Verify all injected streams, whether the injection is coming from apps or from
-                // input filter. Print an error if the stream becomes inconsistent with this event.
-                // An inconsistent injected event sent could cause a crash in the later stages of
-                // dispatching pipeline.
-                auto [it, _] =
-                        mInputFilterVerifiersByDisplay.try_emplace(displayId,
-                                                                   std::string("Injection on ") +
-                                                                           displayId.toString());
-                InputVerifier& verifier = it->second;
-
-                Result<void> result =
-                        verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
-                                                 motionEvent.getAction(),
-                                                 motionEvent.getPointerCount(),
-                                                 motionEvent.getPointerProperties(),
-                                                 motionEvent.getSamplePointerCoords(), flags);
-                if (!result.ok()) {
-                    logDispatchStateLocked();
-                    LOG(ERROR) << "Inconsistent event: " << motionEvent
-                               << ", reason: " << result.error();
-                    if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
-                        mLock.unlock();
-                        return InputEventInjectionResult::FAILED;
-                    }
-                }
+            if (shouldRejectInjectedMotionLocked(motionEvent, resolvedDeviceId, displayId,
+                                                 targetUid, flags)) {
+                mLock.unlock();
+                return InputEventInjectionResult::FAILED;
             }
 
             const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
@@ -5823,7 +5832,7 @@
             }
             // Track the pointer id for drag window and generate the drag state.
             const size_t id = pointers.begin()->id;
-            mDragState = std::make_unique<DragState>(toWindowHandle, id);
+            mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
         }
 
         // Synthesize cancel for old window and down for new window.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 24e36ae..fade853 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -299,6 +299,10 @@
 
     // Event injection and synchronization.
     std::condition_variable mInjectionResultAvailable;
+    bool shouldRejectInjectedMotionLocked(const MotionEvent& motion, DeviceId deviceId,
+                                          ui::LogicalDisplayId displayId,
+                                          std::optional<gui::Uid> targetUid, int32_t flags)
+            REQUIRES(mLock);
     void setInjectionResult(const EventEntry& entry,
                             android::os::InputEventInjectionResult injectionResult);
     void transformMotionEntryForInjectionLocked(MotionEntry&,
@@ -329,7 +333,7 @@
     std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
 
     nsecs_t processAnrsLocked() REQUIRES(mLock);
-    nsecs_t processLatencyStatisticsLocked() REQUIRES(mLock);
+    void processLatencyStatisticsLocked() REQUIRES(mLock);
     std::chrono::nanoseconds getDispatchingTimeoutLocked(
             const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 2f6c6d7..756a29b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -96,6 +96,10 @@
         // The key remapping has changed.
         KEY_REMAPPING = 1u << 14,
 
+        // The mouse settings changed, this includes mouse reverse vertical scrolling and swap
+        // primary button.
+        MOUSE_SETTINGS = 1u << 15,
+
         // All devices must be reopened.
         MUST_REOPEN = 1u << 31,
     };
@@ -252,6 +256,15 @@
     // Keycodes to be remapped.
     std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping;
 
+    // True if the external mouse should have its vertical scrolling reversed, so that rotating the
+    // wheel downwards scrolls the content upwards.
+    bool mouseReverseVerticalScrollingEnabled;
+
+    // True if the connected mouse should have its primary button (default: left click) swapped,
+    // so that the right click will be the primary action button and the left click will be the
+    // secondary action.
+    bool mouseSwapPrimaryButtonEnabled;
+
     InputReaderConfiguration()
           : virtualKeyQuietTime(0),
             defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
@@ -282,7 +295,9 @@
             shouldNotifyTouchpadHardwareState(false),
             touchpadRightClickZoneEnabled(false),
             stylusButtonMotionEventsEnabled(true),
-            stylusPointerIconEnabled(false) {}
+            stylusPointerIconEnabled(false),
+            mouseReverseVerticalScrollingEnabled(false),
+            mouseSwapPrimaryButtonEnabled(false) {}
 
     std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0865eed..f8cd973 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -659,6 +659,21 @@
 }
 
 bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+    if (hasKeycodeInternalLocked(keycode)) {
+        return true;
+    }
+    if (!keyMap.haveKeyCharacterMap()) {
+        return false;
+    }
+    for (auto& fromKey : getKeyCharacterMap()->findKeyCodesMappedToKeyCode(keycode)) {
+        if (hasKeycodeInternalLocked(fromKey)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool EventHub::Device::hasKeycodeInternalLocked(int keycode) const {
     if (!keyMap.haveKeyLayout()) {
         return false;
     }
@@ -676,7 +691,6 @@
     if (usageCodes.size() > 0 && mscBitmask.test(MSC_SCAN)) {
         return true;
     }
-
     return false;
 }
 
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6185f1a..02eeb0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -691,16 +691,6 @@
     return result;
 }
 
-void InputDevice::updateMetaState(int32_t keyCode) {
-    first_in_mappers<bool>([keyCode](InputMapper& mapper) {
-        if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) &&
-            mapper.updateMetaState(keyCode)) {
-            return std::make_optional(true);
-        }
-        return std::optional<bool>();
-    });
-}
-
 void InputDevice::bumpGeneration() {
     mGeneration = mContext->bumpGeneration();
 }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 8b664d5..ab27042 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -584,18 +584,9 @@
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
     std::scoped_lock _l(mLock);
-    InputDevice* device = findInputDeviceLocked(deviceId);
-    if (!device) {
-        ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
-        return;
+    if (mKeyboardClassifier->getKeyboardType(deviceId) == KeyboardType::ALPHABETIC) {
+        updateLedMetaStateLocked(mLedMetaState ^ AMETA_CAPS_LOCK_ON);
     }
-
-    if (device->isIgnored()) {
-        ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId);
-        return;
-    }
-
-    device->updateMetaState(AKEYCODE_CAPS_LOCK);
 }
 
 bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index edc3037..dffd8e3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -680,6 +680,7 @@
         void configureFd();
         void populateAbsoluteAxisStates();
         bool hasKeycodeLocked(int keycode) const;
+        bool hasKeycodeInternalLocked(int keycode) const;
         void loadConfigurationLocked();
         bool loadVirtualKeyMapLocked();
         status_t loadKeyMapLocked();
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 62cc4da..8958d9e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -122,8 +122,6 @@
     std::optional<int32_t> getLightPlayerId(int32_t lightId);
 
     int32_t getMetaState();
-    void updateMetaState(int32_t keyCode);
-
     void setKeyboardType(KeyboardType keyboardType);
 
     void bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 20cdb59..630bd9b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -164,6 +164,10 @@
         changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
         configureOnChangePointerSpeed(readerConfig);
     }
+
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::MOUSE_SETTINGS)) {
+        configureOnChangeMouseSettings(readerConfig);
+    }
     return out;
 }
 
@@ -275,7 +279,12 @@
     PointerCoords pointerCoords;
     pointerCoords.clear();
 
-    float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
+    // A negative value represents inverted scrolling direction.
+    // Applies only if the source is a mouse.
+    const bool isMouse =
+            (mSource == AINPUT_SOURCE_MOUSE) || (mSource == AINPUT_SOURCE_MOUSE_RELATIVE);
+    const int scrollingDirection = (mMouseReverseVerticalScrolling && isMouse) ? -1 : 1;
+    float vscroll = scrollingDirection * mCursorScrollAccumulator.getRelativeVWheel();
     float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
     bool scrolled = vscroll != 0 || hscroll != 0;
 
@@ -537,4 +546,9 @@
     bumpGeneration();
 }
 
+void CursorInputMapper::configureOnChangeMouseSettings(const InputReaderConfiguration& config) {
+    mMouseReverseVerticalScrolling = config.mouseReverseVerticalScrollingEnabled;
+    mCursorButtonAccumulator.setSwapLeftRightButtons(config.mouseSwapPrimaryButtonEnabled);
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 3fc370c..403e96d 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -121,6 +121,7 @@
     nsecs_t mLastEventTime;
 
     const bool mEnableNewMousePointerBallistics;
+    bool mMouseReverseVerticalScrolling = false;
 
     explicit CursorInputMapper(InputDeviceContext& deviceContext,
                                const InputReaderConfiguration& readerConfig);
@@ -129,6 +130,7 @@
     void configureOnPointerCapture(const InputReaderConfiguration& config);
     void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
     void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
+    void configureOnChangeMouseSettings(const InputReaderConfiguration& config);
 
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 627df7f..9e9ed2d 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -109,10 +109,6 @@
     return 0;
 }
 
-bool InputMapper::updateMetaState(int32_t keyCode) {
-    return false;
-}
-
 std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
     return {};
 }
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 75cc4bb..d4a86ac 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -111,11 +111,6 @@
     virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
 
     virtual int32_t getMetaState();
-    /**
-     * Process the meta key and update the global meta state when changed.
-     * Return true if the meta key could be handled by the InputMapper.
-     */
-    virtual bool updateMetaState(int32_t keyCode);
 
     [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 38dcd65..567a3e2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -390,15 +390,6 @@
     return mMetaState;
 }
 
-bool KeyboardInputMapper::updateMetaState(int32_t keyCode) {
-    if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) {
-        return false;
-    }
-
-    updateMetaStateIfNeeded(keyCode, false);
-    return true;
-}
-
 bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
     int32_t oldMetaState = mMetaState;
     int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
@@ -434,17 +425,21 @@
     mMetaState &= ~(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON);
     mMetaState |= getContext()->getLedMetaState();
 
-    constexpr int32_t META_NUM = 3;
-    const std::vector<int32_t> keyCodes{AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
-                                        AKEYCODE_SCROLL_LOCK};
-    const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
-                                                     AMETA_SCROLL_LOCK_ON};
-    std::array<uint8_t, META_NUM> flags = {0, 0, 0};
-    bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodes, flags.data());
+    std::vector<int32_t> keyCodesToCheck{AKEYCODE_NUM_LOCK, AKEYCODE_SCROLL_LOCK};
+    std::vector<int32_t> metaCodes{AMETA_NUM_LOCK_ON, AMETA_SCROLL_LOCK_ON};
+    // Check for physical CapsLock key only for non-alphabetic keyboards. For Alphabetic
+    // keyboards, we will allow Caps Lock even if there is no physical CapsLock key.
+    if (getDeviceContext().getKeyboardType() != KeyboardType::ALPHABETIC) {
+        keyCodesToCheck.push_back(AKEYCODE_CAPS_LOCK);
+        metaCodes.push_back(AMETA_CAPS_LOCK_ON);
+    }
+    size_t size = keyCodesToCheck.size();
+    std::vector<uint8_t> flags(size, 0);
+    bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodesToCheck, flags.data());
     // If the device doesn't have the physical meta key it shouldn't generate the corresponding
     // meta state.
     if (hasKeyLayout) {
-        for (int i = 0; i < META_NUM; i++) {
+        for (size_t i = 0; i < size; i++) {
             if (!flags[i]) {
                 mMetaState &= ~metaCodes[i];
             }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 2df0b85..10bd424 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -45,7 +45,6 @@
     int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
 
     int32_t getMetaState() override;
-    bool updateMetaState(int32_t keyCode) override;
     std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
     void updateLedState(bool reset) override;
 
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 9e722d4..456562c 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,6 +47,10 @@
     mBtnTask = 0;
 }
 
+void CursorButtonAccumulator::setSwapLeftRightButtons(bool shouldSwap) {
+    mSwapLeftRightButtons = shouldSwap;
+}
+
 void CursorButtonAccumulator::process(const RawEvent& rawEvent) {
     if (rawEvent.type == EV_KEY) {
         switch (rawEvent.code) {
@@ -81,10 +85,12 @@
 uint32_t CursorButtonAccumulator::getButtonState() const {
     uint32_t result = 0;
     if (mBtnLeft) {
-        result |= AMOTION_EVENT_BUTTON_PRIMARY;
+        result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_SECONDARY
+                                        : AMOTION_EVENT_BUTTON_PRIMARY;
     }
     if (mBtnRight) {
-        result |= AMOTION_EVENT_BUTTON_SECONDARY;
+        result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_PRIMARY
+                                        : AMOTION_EVENT_BUTTON_SECONDARY;
     }
     if (mBtnMiddle) {
         result |= AMOTION_EVENT_BUTTON_TERTIARY;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 256b2bb..6990030 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -41,6 +41,8 @@
     inline bool isExtraPressed() const { return mBtnExtra; }
     inline bool isTaskPressed() const { return mBtnTask; }
 
+    void setSwapLeftRightButtons(bool shouldSwap);
+
 private:
     bool mBtnLeft;
     bool mBtnRight;
@@ -51,6 +53,8 @@
     bool mBtnExtra;
     bool mBtnTask;
 
+    bool mSwapLeftRightButtons = false;
+
     void clearButtons();
 };
 
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index b27d02d..1762a45 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -205,9 +205,14 @@
     args.clear();
     args += process(EV_KEY, BTN_LEFT, 1);
     args += process(EV_SYN, SYN_REPORT, 0);
+
     ASSERT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
-                            VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
 
     // Move some more.
     args.clear();
@@ -221,9 +226,76 @@
     args += process(EV_KEY, BTN_LEFT, 0);
     args += process(EV_SYN, SYN_REPORT, 0);
     ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_RELEASE),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+}
+
+/**
+ * Test that enabling mouse swap primary button will have the left click result in a
+ * `SECONDARY_BUTTON` event and a right click will result in a `PRIMARY_BUTTON` event.
+ */
+TEST_F(CursorInputMapperUnitTest, SwappedPrimaryButtonPress) {
+    mReaderConfiguration.mouseSwapPrimaryButtonEnabled = true;
+    createMapper();
+    std::list<NotifyArgs> args;
+
+    // Now click the left mouse button , expect a `SECONDARY_BUTTON` button state.
+    args.clear();
+    args += process(EV_KEY, BTN_LEFT, 1);
+    args += process(EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))));
+
+    // Release the left button.
+    args.clear();
+    args += process(EV_KEY, BTN_LEFT, 0);
+    args += process(EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_RELEASE),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+    // Now click the right mouse button , expect a `PRIMARY_BUTTON` button state.
+    args.clear();
+    args += process(EV_KEY, BTN_RIGHT, 1);
+    args += process(EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
+
+    // Release the right button.
+    args.clear();
+    args += process(EV_KEY, BTN_RIGHT, 0);
+    args += process(EV_SYN, SYN_REPORT, 0);
+    ASSERT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
                             VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
                             VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(BUTTON_RELEASE),
+                                          WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
 }
 
 /**
@@ -882,6 +954,51 @@
                                           WithScroll(0.5f, 0.5f)))));
 }
 
+TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) {
+    mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+    createMapper();
+
+    std::list<NotifyArgs> args;
+    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    // Reversed vertical scrolling only affects the y-axis, expect it to be -1.0f to indicate the
+    // inverted scroll direction.
+    EXPECT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                          WithScroll(1.0f, -1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) {
+    mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+    vd_flags::high_resolution_scroll(true);
+    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+            .WillRepeatedly(Return(true));
+    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+            .WillRepeatedly(Return(true));
+    createMapper();
+
+    std::list<NotifyArgs> args;
+    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    EXPECT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                          WithScroll(0.5f, -0.5f)))));
+}
+
 /**
  * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
  * pointer acceleration or speed processing should not be applied.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c5702e9..3413caa 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5105,9 +5105,7 @@
 /**
  * Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
  */
-TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
-                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
-                                                       a11y_crash_on_inconsistent_event_stream))) {
+TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
                                                              ui::LogicalDisplayId::DEFAULT);
@@ -5123,10 +5121,11 @@
                     .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+    // Another HOVER_ENTER would be inconsistent, and should therefore fail to
+    // get injected.
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
               injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
-    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
-    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
 }
 
 /**
@@ -12889,6 +12888,22 @@
     // Remove drag window
     mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
 
+    // Complete the first event stream, even though the injection will fail because there aren't any
+    // valid targets to dispatch this event to. This is still needed to make the input stream
+    // consistent
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionEvent(*mDispatcher,
+                                MotionEventBuilder(ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
+                                        .displayId(ui::LogicalDisplayId::DEFAULT)
+                                        .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
+                                                         .x(150)
+                                                         .y(50))
+                                        .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+                                                         .x(50)
+                                                         .y(50))
+                                        .build(),
+                                INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT));
+
     // Inject a simple gesture, ensure dispatcher not crashed
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
@@ -12927,6 +12942,87 @@
             << "Drag and drop should not work with a hovering pointer";
 }
 
+/**
+ * Two devices, we use the second pointer of Device A to start the drag, during the drag process, if
+ * we perform a click using Device B, the dispatcher should work well.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouchAndMultiDevice) {
+    const DeviceId deviceA = 1;
+    const DeviceId deviceB = 2;
+    // First down on second window with deviceA.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceA)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+                                      .build());
+    mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+                                            WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    // Second down on first window with deviceA
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceA)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(50).y(50))
+                                      .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+                                      WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+    mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+                                            WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    // Perform drag and drop from first window.
+    ASSERT_TRUE(startDrag(/*sendDown=*/false));
+
+    // Click first window with device B, we should ensure dispatcher work well.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(deviceB)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB),
+                                      WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(deviceB)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .build());
+    mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceB),
+                                      WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    // Move with device A.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceA)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+                                      .build());
+
+    mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+                                          WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+                                          WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+    mWindow->consumeDragEvent(false, 51, 51);
+    mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+                                            WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    // Releasing the drag pointer should cause drop.
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceA)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+                                      .build());
+    mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+                                          WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+                                          WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+    mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+    mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+                                            WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+    // Release all pointers.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceA)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+                                      .build());
+    mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+                                            WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+    mWindow->assertNoEvents();
+}
+
 class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
 
 TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 17c37d5..6c8b65c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3671,40 +3671,6 @@
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
-TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
-    mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
-    mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
-    mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
-    mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
-
-    KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
-    // Meta state should be AMETA_NONE after reset
-    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-    // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
-    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
-    NotifyKeyArgs args;
-    // Press button "A"
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    ASSERT_EQ(AMETA_NONE, args.metaState);
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
-    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-
-    // Button up.
-    process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    ASSERT_EQ(AMETA_NONE, args.metaState);
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-    ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
-    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-}
-
 TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
     // keyboard 1.
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index f41b39a..a43e4e4 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -246,8 +246,6 @@
     MOCK_METHOD(std::optional<int32_t>, getLightPlayerId, (int32_t lightId), ());
 
     MOCK_METHOD(int32_t, getMetaState, (), ());
-    MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
-
     MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
 
     MOCK_METHOD(void, bumpGeneration, (), ());
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 9e02502..11b038b 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -99,7 +99,6 @@
                                                  nullptr);
                 },
                 [&]() -> void { mapper.getMetaState(); },
-                [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
                 [&]() -> void { mapper.getAssociatedDisplayId(); },
         })();
     }
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 060508c..eabbb39 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2049,9 +2049,10 @@
     }
 
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
-    if (mCurrentOperatingMode != NORMAL && mCurrentOperatingMode != REPLAY_DATA_INJECTION &&
-           !isAllowListedPackage(connection->getPackageName())) {
-        return INVALID_OPERATION;
+    if (mCurrentOperatingMode != NORMAL &&
+        !isInjectionMode(mCurrentOperatingMode) &&
+        !isAllowListedPackage(connection->getPackageName())) {
+      return INVALID_OPERATION;
     }
 
     SensorRecord* rec = mActiveSensors.valueFor(handle);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index c2a9880..7babd17 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -173,7 +173,6 @@
         "DisplayHardware/VirtualDisplaySurface.cpp",
         "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
-        "EventLog/EventLog.cpp",
         "FrontEnd/LayerCreationArgs.cpp",
         "FrontEnd/LayerHandle.cpp",
         "FrontEnd/LayerSnapshot.cpp",
@@ -279,7 +278,7 @@
         "libSurfaceFlingerProp",
     ],
 
-    logtags: ["EventLog/EventLogTags.logtags"],
+    logtags: ["surfaceflinger.logtags"],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
deleted file mode 100644
index 3b60952..0000000
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <log/log.h>
-
-#include "EventLog.h"
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
-
-
-EventLog::EventLog() {
-}
-
-void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
-                                   size_t numDurations) {
-    EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
-    buffer.startList(1 + numDurations);
-    buffer.writeString(name);
-    for (size_t i = 0; i < numDurations; i++) {
-        buffer.writeInt32(durations[i]);
-    }
-    buffer.endList();
-    buffer.log();
-}
-
-void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
-                                 size_t numDurations) {
-    EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
-}
-
-// ---------------------------------------------------------------------------
-
-EventLog::TagBuffer::TagBuffer(int32_t tag)
-    : mPos(0), mTag(tag), mOverflow(false) {
-}
-
-void EventLog::TagBuffer::log() {
-    if (mOverflow) {
-        ALOGW("couldn't log to binary event log: overflow.");
-    } else if (android_bWriteLog(mTag, mStorage, mPos) < 0) {
-        ALOGE("couldn't log to EventLog: %s", strerror(errno));
-    }
-    // purge the buffer
-    mPos = 0;
-    mOverflow = false;
-}
-
-void EventLog::TagBuffer::startList(int8_t count) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(count);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_LIST;
-    mStorage[mPos + 1] = count;
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::endList() {
-    if (mOverflow) return;
-    const size_t needed = 1;
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = '\n';
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt32(int32_t value) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(value);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_INT;
-    memcpy(&mStorage[mPos + 1], &value, sizeof(value));
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt64(int64_t value) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(value);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_LONG;
-    memcpy(&mStorage[mPos + 1], &value, sizeof(value));
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeString(const std::string_view& value) {
-    if (mOverflow) return;
-    const size_t stringLen = value.length();
-    const size_t needed = 1 + sizeof(int32_t) + stringLen;
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_STRING;
-    memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
-    memcpy(&mStorage[mPos + 5], value.data(), stringLen);
-    mPos += needed;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
deleted file mode 100644
index ee3587e..0000000
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-
-#include <cstdint>
-#include <string_view>
-
-namespace android {
-
-class EventLog : public Singleton<EventLog> {
-
-public:
-    static void logFrameDurations(const std::string_view& name, const int32_t* durations,
-                                  size_t numDurations);
-
-protected:
-    EventLog();
-
-private:
-    /*
-     * EventLogBuffer is a helper class to construct an in-memory event log
-     * tag. In this version the buffer is not dynamic, so write operation can
-     * fail if there is not enough space in the temporary buffer.
-     * Once constructed, the buffer can be logger by calling the log()
-     * method.
-     */
-
-    class TagBuffer {
-        enum { STORAGE_MAX_SIZE = 128 };
-        int32_t mPos;
-        int32_t mTag;
-        bool mOverflow;
-        char mStorage[STORAGE_MAX_SIZE];
-    public:
-        explicit TagBuffer(int32_t tag);
-
-        void startList(int8_t count);
-        void endList();
-
-        void writeInt32(int32_t);
-        void writeInt64(int64_t);
-        void writeString(const std::string_view&);
-
-        void log();
-    };
-
-    friend class Singleton<EventLog>;
-    EventLog(const EventLog&);
-    EventLog& operator =(const EventLog&);
-
-    enum { LOGTAG_SF_FRAME_DUR = 60100 };
-    void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
-                             size_t numDurations);
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index ca8cdc3..93d0313 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -26,16 +26,10 @@
 #include <ui/FrameStats.h>
 
 #include "FrameTracker.h"
-#include "EventLog/EventLog.h"
 
 namespace android {
 
-FrameTracker::FrameTracker() :
-        mOffset(0),
-        mNumFences(0),
-        mDisplayPeriod(0) {
-    resetFrameCountersLocked();
-}
+FrameTracker::FrameTracker() : mOffset(0), mNumFences(0), mDisplayPeriod(0) {}
 
 void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
     Mutex::Autolock lock(mMutex);
@@ -73,9 +67,6 @@
 void FrameTracker::advanceFrame() {
     Mutex::Autolock lock(mMutex);
 
-    // Update the statistic to include the frame we just finished.
-    updateStatsLocked(mOffset);
-
     // Advance to the next frame.
     mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
     mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -138,19 +129,12 @@
     }
 }
 
-void FrameTracker::logAndResetStats(const std::string_view& name) {
-    Mutex::Autolock lock(mMutex);
-    logStatsLocked(name);
-    resetFrameCountersLocked();
-}
-
 void FrameTracker::processFencesLocked() const {
     FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
     int& numFences = const_cast<int&>(mNumFences);
 
     for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
-        size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
-        bool updated = false;
+        size_t idx = (mOffset + NUM_FRAME_RECORDS - i) % NUM_FRAME_RECORDS;
 
         const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
         if (rfence != nullptr) {
@@ -158,7 +142,6 @@
             if (records[idx].frameReadyTime < INT64_MAX) {
                 records[idx].frameReadyFence = nullptr;
                 numFences--;
-                updated = true;
             }
         }
 
@@ -169,59 +152,8 @@
             if (records[idx].actualPresentTime < INT64_MAX) {
                 records[idx].actualPresentFence = nullptr;
                 numFences--;
-                updated = true;
             }
         }
-
-        if (updated) {
-            updateStatsLocked(idx);
-        }
-    }
-}
-
-void FrameTracker::updateStatsLocked(size_t newFrameIdx) const {
-    int* numFrames = const_cast<int*>(mNumFrames);
-
-    if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) {
-        size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) %
-                NUM_FRAME_RECORDS;
-
-        if (isFrameValidLocked(prevFrameIdx)) {
-            nsecs_t newPresentTime =
-                    mFrameRecords[newFrameIdx].actualPresentTime;
-            nsecs_t prevPresentTime =
-                    mFrameRecords[prevFrameIdx].actualPresentTime;
-
-            nsecs_t duration = newPresentTime - prevPresentTime;
-            int numPeriods = int((duration + mDisplayPeriod/2) /
-                    mDisplayPeriod);
-
-            for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) {
-                int nextBucket = 1 << (i+1);
-                if (numPeriods < nextBucket) {
-                    numFrames[i]++;
-                    return;
-                }
-            }
-
-            // The last duration bucket is a catch-all.
-            numFrames[NUM_FRAME_BUCKETS-1]++;
-        }
-    }
-}
-
-void FrameTracker::resetFrameCountersLocked() {
-    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
-        mNumFrames[i] = 0;
-    }
-}
-
-void FrameTracker::logStatsLocked(const std::string_view& name) const {
-    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
-        if (mNumFrames[i] > 0) {
-            EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
-            return;
-        }
     }
 }
 
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index bc412ae..fd6fadc 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -41,8 +41,6 @@
     // frame time history.
     enum { NUM_FRAME_RECORDS = 128 };
 
-    enum { NUM_FRAME_BUCKETS = 7 };
-
     FrameTracker();
 
     // setDesiredPresentTime sets the time at which the current frame
@@ -142,13 +140,6 @@
     // doesn't grow with NUM_FRAME_RECORDS.
     int mNumFences;
 
-    // mNumFrames keeps a count of the number of frames with a duration in a
-    // particular range of vsync periods.  Element n of the array stores the
-    // number of frames with duration in the half-inclusive range
-    // [2^n, 2^(n+1)).  The last element of the array contains the count for
-    // all frames with duration greater than 2^(NUM_FRAME_BUCKETS-1).
-    int32_t mNumFrames[NUM_FRAME_BUCKETS];
-
     // mDisplayPeriod is the display refresh period of the display for which
     // this FrameTracker is gathering information.
     nsecs_t mDisplayPeriod;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 0788d1a..07a5724 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -61,6 +61,7 @@
     ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
     uint32_t parentId = UNASSIGNED_LAYER_ID;
     uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+    std::atomic<int32_t>* pendingBuffers = 0;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 1eff9d6..713a5c5 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -56,7 +56,8 @@
         ownerUid(args.ownerUid),
         ownerPid(args.ownerPid),
         parentId(args.parentId),
-        layerIdToMirror(args.layerIdToMirror) {
+        layerIdToMirror(args.layerIdToMirror),
+        pendingBuffers(args.pendingBuffers) {
     layerId = static_cast<int32_t>(args.sequence);
     changes |= RequestedLayerState::Changes::Created;
     metadata.merge(args.metadata);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 3220e86..7ddd7ba 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -131,6 +131,7 @@
     uint64_t barrierFrameNumber = 0;
     uint32_t barrierProducerId = 0;
     std::string debugName;
+    std::atomic<int32_t>* pendingBuffers = 0;
 
     // book keeping states
     bool handleAlive = true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9fdcbd0..c14769e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -183,7 +183,6 @@
     mFlinger->mTimeStats->onDestroy(layerId);
     mFlinger->mFrameTracer->onDestroy(layerId);
 
-    mFrameTracker.logAndResetStats(mName);
     mFlinger->onLayerDestroyed(this);
 
     if (mDrawingState.sidebandStream != nullptr) {
@@ -605,10 +604,6 @@
     mFrameTracker.clearStats();
 }
 
-void Layer::logFrameStats() {
-    mFrameTracker.logAndResetStats(mName);
-}
-
 void Layer::getFrameStats(FrameStats* outStats) const {
     mFrameTracker.getStats(outStats);
 }
@@ -1347,7 +1342,7 @@
 }
 
 void Layer::decrementPendingBufferCount() {
-    int32_t pendingBuffers = --mPendingBufferTransactions;
+    int32_t pendingBuffers = --mPendingBuffers;
     tracePendingBufferCount(pendingBuffers);
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 57093ae..ce4b9c4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -369,7 +369,7 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBuffers; }
     std::string getPendingBufferCounterName() { return mBlastTransactionName; }
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                    const sp<GraphicBuffer>& buffer, uint64_t framenumber,
@@ -562,7 +562,7 @@
     //     - If the integer increases, a buffer arrived at the server.
     //     - If the integer decreases in latchBuffer, that buffer was latched
     //     - If the integer decreases in setBuffer, a buffer was dropped
-    std::atomic<int32_t> mPendingBufferTransactions{0};
+    std::atomic<int32_t> mPendingBuffers{0};
 
     // Contains requested position and matrix updates. This will be applied if the client does
     // not specify a destination frame.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5e13154..2815cb7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -426,6 +426,8 @@
     eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
 bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
     const bool isPacesetter =
             FTL_FAKE_GUARD(kMainThreadContext,
@@ -446,6 +448,7 @@
 
     return isPacesetter;
 }
+#pragma clang diagnostic pop
 
 void Scheduler::emitModeChangeIfNeeded() {
     if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a2a16a1..cad179c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3285,8 +3285,6 @@
         // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
         SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
     }
-
-    logFrameStats(presentTime);
 }
 
 void SurfaceFlinger::commitTransactions() {
@@ -5198,6 +5196,7 @@
                 std::string counterName = layer->getPendingBufferCounterName();
                 mBufferCountTracker.add(LayerHandle::getLayerId(outResult.handle), counterName,
                                         pendingBufferCounter);
+                args.pendingBuffers = pendingBufferCounter;
             }
         } break;
         default:
diff --git a/services/surfaceflinger/EventLog/EventLogTags.logtags b/services/surfaceflinger/surfaceflinger.logtags
similarity index 93%
rename from services/surfaceflinger/EventLog/EventLogTags.logtags
rename to services/surfaceflinger/surfaceflinger.logtags
index 6c851dd..e68d9f5 100644
--- a/services/surfaceflinger/EventLog/EventLogTags.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -35,7 +35,6 @@
 
 # 60100 - 60199 reserved for surfaceflinger
 
-60100 sf_frame_dur (window|3),(dur0|1),(dur1|1),(dur2|1),(dur3|1),(dur4|1),(dur5|1),(dur6|1)
 60110 sf_stop_bootanim (time|2|3)
 
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 102e2b6..e40be51 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -11,6 +11,14 @@
 } # adpf_gpu_sf
 
 flag {
+  name: "arr_setframerate_api"
+  namespace: "core_graphics"
+  description: "New setFrameRate API for Android 16"
+  bug: "356987016"
+  is_fixed_read_only: true
+} # arr_setframerate_api
+
+flag {
   name: "ce_fence_promise"
   namespace: "window_surfaces"
   description: "Moves logic for buffer release fences into LayerFE"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 9efe73d..adbd868 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -100,7 +100,9 @@
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
     using RefreshRateSelector::GetRankedFrameRatesCache;
-    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+    auto& mutableGetRankedRefreshRatesCache() NO_THREAD_SAFETY_ANALYSIS {
+        return mGetRankedFrameRatesCache;
+    }
 
     auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
                              GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
@@ -138,7 +140,9 @@
         return setPolicy(policy);
     }
 
-    const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+    const auto& getPrimaryFrameRates() const NO_THREAD_SAFETY_ANALYSIS {
+        return mPrimaryFrameRates;
+    }
 };
 
 class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 4ac1618..b06ee3b 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -107,6 +107,10 @@
         mInfoCache.mMaxEnvelopeEffectControlPointDuration =
                 getMaxEnvelopeEffectControlPointDurationInternal();
     }
+    if (mInfoCache.mFrequencyToOutputAccelerationMap.isFailed()) {
+        mInfoCache.mFrequencyToOutputAccelerationMap =
+                getFrequencyToOutputAccelerationMapInternal();
+    }
     return mInfoCache.get();
 }
 
@@ -239,6 +243,13 @@
     return HalResult<milliseconds>::unsupported();
 }
 
+HalResult<std::vector<PwleV2OutputMapEntry>>
+HalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+    ALOGV("Skipped getFrequencyToOutputAccelerationMapInternal because it's not "
+          "available in Vibrator HAL");
+    return HalResult<std::vector<PwleV2OutputMapEntry>>::unsupported();
+}
+
 // -------------------------------------------------------------------------------------------------
 
 HalResult<void> AidlHalWrapper::ping() {
@@ -487,6 +498,15 @@
     return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
 }
 
+HalResult<std::vector<PwleV2OutputMapEntry>>
+AidlHalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+    auto status =
+            getHal()->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+    return HalResultFactory::fromStatus<
+            std::vector<PwleV2OutputMapEntry>>(std::move(status), frequencyToOutputAccelerationMap);
+}
+
 std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 4938b15..b2bfffc 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -243,6 +243,7 @@
     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;
 
     const HalResult<Capabilities> capabilities;
     const HalResult<std::vector<Effect>> supportedEffects;
@@ -261,6 +262,7 @@
     const HalResult<int32_t> maxEnvelopeEffectSize;
     const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration;
     const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration;
+    const HalResult<std::vector<PwleV2OutputMapEntry>> frequencyToOutputAccelerationMap;
 
     void logFailures() const {
         logFailure<Capabilities>(capabilities, "getCapabilities");
@@ -284,6 +286,8 @@
                                               "getMinEnvelopeEffectControlPointDuration");
         logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
                                               "getMaxEnvelopeEffectControlPointDuration");
+        logFailure<std::vector<PwleV2OutputMapEntry>>(frequencyToOutputAccelerationMap,
+                                                      "getfrequencyToOutputAccelerationMap");
     }
 
     bool shouldRetry() const {
@@ -296,7 +300,8 @@
                 qFactor.shouldRetry() || maxAmplitudes.shouldRetry() ||
                 maxEnvelopeEffectSize.shouldRetry() ||
                 minEnvelopeEffectControlPointDuration.shouldRetry() ||
-                maxEnvelopeEffectControlPointDuration.shouldRetry();
+                maxEnvelopeEffectControlPointDuration.shouldRetry() ||
+                frequencyToOutputAccelerationMap.shouldRetry();
     }
 
 private:
@@ -327,7 +332,8 @@
                 mMaxAmplitudes,
                 mMaxEnvelopeEffectSize,
                 mMinEnvelopeEffectControlPointDuration,
-                mMaxEnvelopeEffectControlPointDuration};
+                mMaxEnvelopeEffectControlPointDuration,
+                mFrequencyToOutputAccelerationMap};
     }
 
 private:
@@ -359,6 +365,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);
 
     friend class HalWrapper;
 };
@@ -442,6 +450,8 @@
     virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal();
     virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal();
     virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal();
+    virtual HalResult<std::vector<PwleV2OutputMapEntry>>
+    getFrequencyToOutputAccelerationMapInternal();
 
 private:
     std::mutex mInfoMutex;
@@ -518,12 +528,12 @@
     HalResult<float> getQFactorInternal() override final;
     HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
     HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final;
-
     HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal()
             override final;
-
     HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal()
             override final;
+    HalResult<std::vector<PwleV2OutputMapEntry>> getFrequencyToOutputAccelerationMapInternal()
+            override final;
 
 private:
     const reconnect_fn mReconnectFn;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 17f384d..d42aa56 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -39,6 +39,7 @@
 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;
@@ -242,6 +243,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<std::chrono::milliseconds> primitiveDurations;
     constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
@@ -323,6 +329,11 @@
             .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(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+            .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+                            Return(ndk::ScopedAStatus::ok())));
 
     vibrator::Info failed = mWrapper->getInfo();
     ASSERT_TRUE(failed.capabilities.isFailed());
@@ -342,6 +353,7 @@
     ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed());
     ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed());
     ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed());
+    ASSERT_TRUE(failed.frequencyToOutputAccelerationMap.isFailed());
 
     vibrator::Info successful = mWrapper->getInfo();
     ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
@@ -364,6 +376,8 @@
               successful.minEnvelopeEffectControlPointDuration.value());
     ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
               successful.maxEnvelopeEffectControlPointDuration.value());
+    ASSERT_EQ(frequencyToOutputAccelerationMap,
+              successful.frequencyToOutputAccelerationMap.value());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
@@ -377,6 +391,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)};
 
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
             .Times(Exactly(1))
@@ -432,6 +451,10 @@
             .Times(Exactly(1))
             .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
                             Return(ndk::ScopedAStatus::ok())));
+    EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+            .Times(Exactly(1))
+            .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+                            Return(ndk::ScopedAStatus::ok())));
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
@@ -460,6 +483,7 @@
               info.minEnvelopeEffectControlPointDuration.value());
     ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
               info.maxEnvelopeEffectControlPointDuration.value());
+    ASSERT_EQ(frequencyToOutputAccelerationMap, info.frequencyToOutputAccelerationMap.value());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index a09ddec..d6dab8d 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -223,6 +223,7 @@
     ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
     ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
     ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+    ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
@@ -259,6 +260,7 @@
     ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
     ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
     ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+    ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {